Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0402 cet perf 2 #207

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion cet/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ IS_KER_SRC = $(shell [ -d $(KER_SRC) ] && echo true)

ifeq ($(GCC_GE_8),true)
BIN := shstk_alloc test_shadow_stack quick_test wrss shstk_huge_page \
shstk_unlock_test shstk_cp cet_app glibc_shstk_test
shstk_unlock_test shstk_cp cet_app glibc_shstk_test shstk_cpu \
shstk_cpu_legacy

$(info GCC major version: ${GCC_VER_MAJOR})
else
Expand Down Expand Up @@ -57,6 +58,12 @@ cet_app:
glibc_shstk_test: glibc_shstk_test.c
gcc $(CETFLAGS) $^ -o $@

shstk_cpu: shstk_cpu.c
gcc $(NOCETFLAGS) $^ -o $@

shstk_cpu_legacy: shstk_cpu_legacy.c
gcc $(NOCETFLAGS) $^ -o $@

cet_ioctl:
$(MAKE) -C $(KER_SRC) M=$(DRIVER_PATH) modules

Expand Down
186 changes: 185 additions & 1 deletion cet/cet_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ TEST_MOD="cet_ioctl"
TEST_MOD_KO="${TEST_MOD}.ko"
KO_FILE="./cet_driver/${TEST_MOD_KO}"

export OFFLINE_CPUS=""
export teardown_handler="cet_teardown"

usage() {
Expand All @@ -28,6 +29,10 @@ __EOF

# Reserve for taerdown, present no change for cpu test
cet_teardown() {
[[ -z "$OFFLINE_CPUS" ]] || {
set_cpus_on_off "$OFFLINE_CPUS" || test_print_err "Set offline $OFFLINE_CPUS"
}

check_mod=$(lsmod | grep "$TEST_MOD")
[[ -z "$check_mod" ]] || {
test_print_trc "rmmod $TEST_MOD"
Expand Down Expand Up @@ -58,6 +63,95 @@ load_cet_driver() {
fi
}

# Check test used time cycles in log, and print 2 test logs gap rate
# $1: 1st test log file
# $2: 2nd test log file
# $3: test log folder path
# Return: 0 for true, otherwise false or die
cet_perf_compare() {
local file1=$1
local file2=$2
local path=$3
local key_word="RESULTS"
local cycle1=""
local cycle2=""
local gap=""
local gap_rate=""
local result=""
local gap_upper="0.6"
local gap_lower="-2.0"

cycle1=$(grep "$key_word" "${path}/${file1}" | cut -d ':' -f 4)
cycle2=$(grep "$key_word" "${path}/${file2}" | cut -d ':' -f 4)
test_print_trc "$file1 used cycles $cycle1"
test_print_trc "$file2 used cycles $cycle2"
gap=$(echo "$cycle1 - $cycle2" | bc)
gap_rate=$(echo "scale=4;$gap/$cycle1" | bc)
test_print_trc "$file1 and $file2 gap rate:$gap_rate"
result=$(echo "$gap_rate > $gap_lower && $gap_rate < $gap_upper" | bc)
[[ $result -eq 1 ]] || {
test_print_wrg "gap: $gap_rate is not in the range:$gap_lower ~ $gap_upper"
return 1
}
}

bin_parm_test() {
local bin_name=$1
local bin_parm=$2
local log_path=/tmp/$3

[[ -n "$bin_name" ]] || die "File $bin_name does not exist"
[[ -d "$log_path" ]] || mkdir -p "$log_path"
bin=$(which "$bin_name")
[[ -e "$bin" ]] || {
die "bin:$bin does not exist"
}

bin_parm_name=$(echo "$bin_parm" | tr ' ' '_')
if [[ "$bin_parm" == "null" ]]; then
log="${log_path}/${bin_name}_${bin_parm_name}.log"
$bin > "$log"
else
log="${log_path}/${bin_name}_${bin_parm_name}.log"
$bin "${bin_parm}" > "$log" || {
test_print_err "Failed to run $bin $bin_parm ret:$?"
return 1
}
fi

[[ -e "$log" ]] || die "No $log file"

return 0
}

dmesg_check() {
local key=$1
local key_parm=$2
local dmesg_file=""

dmesg_file=$(extract_case_dmesg -f)
verify_key=$(grep -i "$key" "$dmesg_file")
case $key_parm in
"$CONTAIN")
if [[ -z "$verify_key" ]]; then
die "No $key found in dmesg when test $BIN_NAME, fail."
else
test_print_trc "$key found in dmesg, pass."
fi
;;
"$NULL")
if [[ -z "$verify_key" ]]; then
test_print_trc "No $key in dmesg when test $BIN_NAME, pass."
else
die "$key found in dmesg when test $BIN_NAME, fail."
fi
;;
*)
block_test "Invalid key_parm:$key_parm"
;;
esac
}

cet_shstk_check() {
local bin_name=$1
local bin_parm=$2
Expand Down Expand Up @@ -144,7 +238,8 @@ cet_dmesg_check() {
}

cet_tests() {
bin_file=""
local bin_file=""
local legacy="legacy"

# Absolute path of BIN_NAME
bin_file=$(which "$BIN_NAME")
Expand All @@ -167,6 +262,95 @@ cet_tests() {
cet_ssp)
cet_shstk_check "$bin_file" "$PARM" "$TYPE"
;;
specific_cpu_perf)
local cpus=""
local cpu_num=""
local err_num=0
local cet_compare_path="/tmp/${TYPE}"

cpus=$(cut -d "-" -f 2 "${CPU_SYSFS_FOLDER}/present")
if [[ "$PARM" == "random" ]]; then
cpu_num=$(shuf -i 0-"$cpus" -n 1)
elif [[ "$PARM" -ge 0 && "$PARM" -le "$cpus" ]]; then
cpu_num=$PARM
else
block_test "Invalid CPU NUM in PARM:$PARM"
fi

# Check cpu and kernel enable user space SHSTK really first
bin_parm_test "$BIN_NAME" "0" "$TYPE" || {
((err_num++))
}
check_fail=$(grep "FAIL" "$cet_compare_path/${BIN_NAME}_0.log")
[[ -z "$check_fail" ]] || {
test_print_wrg "Found FAIL in $BIN_NAME 0 output:$check_fail"
block_test "CET user space SHSTK could not be enabled!"
}
[[ "$err_num" -eq 0 ]] || die "Test cpu 0 $BIN_NAME failed!"

# CPU 0 must be 1, so do not check cpu 0
[[ "$cpu_num" -eq 0 ]] || {
cpu_num_on_off=$(cat "${CPU_SYSFS_FOLDER}/cpu${cpu_num}/online")
[[ "$cpu_num_on_off" == "1" ]] || OFFLINE_CPUS="$cpu_num"
set_specific_cpu_on_off "1" "$cpu_num"
}

last_dmesg_timestamp
bin_parm_test "$BIN_NAME" "$i" "$TYPE" || {
((err_num++))
}
bin_parm_test "${BIN_NAME}_${legacy}" "$i" "$TYPE" || {
((err_num++))
}
cet_perf_compare "${BIN_NAME}_${i}.log" \
"${BIN_NAME}_${legacy}_${i}.log" "$cet_compare_path" || {
test_print_err "CPU$i met $BIN_NAME perf regression!"
((err_num++))
}
[[ "$err_num" -eq 0 ]] || die "All cpu cet test with err_cnt:$err_num"
dmesg_check "control protection" "$NULL"
dmesg_check "Call Trace" "$NULL"
dmesg_check "segfault" "$NULL"
;;
all_cpu_perf)
local cet_compare_path="/tmp/${TYPE}"
local err_num=0
local check_fail=""

# Check cpu and kernel enable user space SHSTK really first
bin_parm_test "$BIN_NAME" "0" "$TYPE" || {
((err_num++))
}
check_fail=$(grep "FAIL" "$cet_compare_path/${BIN_NAME}_0.log")
[[ -z "$check_fail" ]] || {
test_print_wrg "Found FAIL in $BIN_NAME 0 output:$check_fail"
block_test "CET user space SHSTK could not be enabled!"
}
[[ "$err_num" -eq 0 ]] || die "Test cpu 0 $BIN_NAME failed!"

last_dmesg_timestamp
OFFLINE_CPUS=$(cat "${CPU_SYSFS_FOLDER}/offline")
online_all_cpu
cpus=$(cut -d "-" -f 2 "${CPU_SYSFS_FOLDER}/present")

for((i=0;i<=cpus;i++)); do
bin_parm_test "$BIN_NAME" "$i" "$TYPE" || {
((err_num++))
}
bin_parm_test "${BIN_NAME}_${legacy}" "$i" "$TYPE" || {
((err_num++))
}
cet_perf_compare "${BIN_NAME}_${i}.log" \
"${BIN_NAME}_${legacy}_${i}.log" "$cet_compare_path" || {
test_print_err "CPU$i met $BIN_NAME perf regression!"
((err_num++))
}
done
[[ "$err_num" -eq 0 ]] || die "All cpu cet test with err_cnt:$err_num"
dmesg_check "control protection" "$NULL"
dmesg_check "Call Trace" "$NULL"
dmesg_check "segfault" "$NULL"
;;
*)
usage
block_test "Invalid TYPE:$TYPE"
Expand Down
116 changes: 116 additions & 0 deletions cet/shstk_cpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Verify Shadow Stack performance impact on specific CPU
*
* Author: Pengfei Xu <[email protected]>
*/

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <sys/syscall.h>

/* It's from arch/x86/include/uapi/asm/prctl.h file. */
#define ARCH_SHSTK_ENABLE 0x5001
#define ARCH_SHSTK_DISABLE 0x5002
/* ARCH_SHSTK_ features bits */
#define ARCH_SHSTK_SHSTK (1ULL << 0)
#define ARCH_SHSTK_WRSS (1ULL << 1)

/*
* For use in inline enablement of shadow stack.
*
* The program can't return from the point where shadow stack gets enabled
* because there will be no address on the shadow stack. So it can't use
* syscall() for enablement, since it is a function.
*
* Based on code from nolibc.h. Keep a copy here because this can't pull in all
* of nolibc.h.
*/
#define ARCH_PRCTL(arg1, arg2) \
({ \
long _ret; \
register long _num asm("eax") = __NR_arch_prctl; \
register long _arg1 asm("rdi") = (long)(arg1); \
register long _arg2 asm("rsi") = (long)(arg2); \
\
asm volatile ( \
"syscall\n" \
: "=a"(_ret) \
: "r"(_arg1), "r"(_arg2), \
"0"(_num) \
: "rcx", "r11", "memory", "cc" \
); \
_ret; \
})

void shstk2(void)
{
unsigned long *j;

#ifdef __x86_64__
asm("movq %%rbx,%0" : "=r"(j));
#else

asm("mov %%ebp,%0" : "=r"(j));
#endif
}

void shstk1(void)
{
unsigned long *i;

#ifdef __x86_64__
asm("movq %%rbx,%0" : "=r"(i));
#else
asm("mov %%ebp,%0" : "=r"(i));
#endif
shstk2();
}

int main(int argc, char *argv[])
{
int x = 100000000, a, cpu;
clock_t tstart, tend;
cpu_set_t set;

if (argc >= 2) {
cpu = atoi(argv[1]);
} else {
cpu = 0;
printf("There is no cpu setting, set as cpu0 by default\n");
}
printf("testing on cpu %d\n", cpu);
CPU_ZERO(&set);
CPU_SET(cpu, &set);
if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) {
printf("set affinity failed\n");
return -1;
}

if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK))
printf("[FAIL]\tParent process could not enable SHSTK!\n");
else
printf("[PASS]\tParent process enable SHSTK.\n");

tstart = clock();
for (a = 1; a <= x; a++) {
shstk1();
shstk2();
}
tend = clock();
printf("RESULTS %dloop,start:%ld,end:%ld, used CLOCK:%ld: CLOCK/SEC:%ld\n",
x, tstart, tend, (long)(tend - tstart), CLOCKS_PER_SEC);

/* Disable SHSTK in parent process to avoid segfault issue. */
if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK))
printf("[FAIL]\tParent process disable shadow stack failed.\n");
else
printf("[PASS]\tParent process disable shadow stack successfully.\n");

return 0;
}
Loading
Loading