linux_kselftest-fixes-6.8-rc3

This kselftest fixes update for Linux 6.8-rc3 consists of three
 fixes to livepatch, rseq, and seccomp tests.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmW5bRQACgkQCwJExA0N
 QxyMNxAAj/VwvGuZMHtFv7PNS2GAe0ifCKQvuOGJXx53K+SZQZ9VftJ9wIc9TqwT
 GtQCyXznpxBc1jELjE/7mx9xybCBIwwzIqPsDPmVmWopUVJeboh6W4nSeg0EALcZ
 md6w6ps0n7x2kuXvVR/Ie4LFG8WMMXyttLkS/Typd4TNERjr8bDmx1bABuA21uxg
 qG7FfME9AldaDUdj3AVsEKlqWXDnbMrou2mIzOSNLXk3UFcjx9dDsbeYC3WYvi/9
 /3wmNElPssco9JgWjn+14CV690qXDXR7FhsK/osHpojWXMweW1i3msAyrwYR3gCL
 HtrLJwh94LRiS6EvddqaoL+A5/N5irbS6xzXlsxW9axsmbFlHKnzu56C4uVfp+hE
 eZp8U69CygTUXLhJOs3wM+Ahkss2HHZDL6caA6Mb25Ui7iAZi1oTNToyADYVauMo
 jFKYW/FNTZBGMUaJCY3nIU5QGBVpG6bDzJZDyRQVrhrQMs2YEkILzSnK8ru+28kj
 KNMxbRnvrOCu6NxG7mPPIT1nCJwSx0+z2oCGnGuN9G4p6tidMQXIVyVPcgsHktjG
 Z2KkcUTVdk6Qkdgmdmfvtak1jswoQvDu6QK+Ub+In6L8QJVj2Mc7UvpGUwinwhEM
 aGxp/ref/Go+WL0W82p1vThEes2sdYb/PrJAbuGjONBAoXgLpvI=
 =0a0Q
 -----END PGP SIGNATURE-----

Merge tag 'linux_kselftest-fixes-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull kselftest fixes from Shuah Khan:
 "Three fixes to livepatch, rseq, and seccomp tests"

* tag 'linux_kselftest-fixes-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
  kselftest/seccomp: Report each expectation we assert as a KTAP test
  kselftest/seccomp: Use kselftest output functions for benchmark
  selftests/livepatch: fix and refactor new dmesg message code
  selftests/rseq: Do not skip !allowed_cpus for mm_cid
This commit is contained in:
Linus Torvalds 2024-01-30 15:10:17 -08:00
commit d1d873a9bf
4 changed files with 109 additions and 68 deletions

View File

@ -42,17 +42,6 @@ function die() {
exit 1
}
# save existing dmesg so we can detect new content
function save_dmesg() {
SAVED_DMESG=$(mktemp --tmpdir -t klp-dmesg-XXXXXX)
dmesg > "$SAVED_DMESG"
}
# cleanup temporary dmesg file from save_dmesg()
function cleanup_dmesg_file() {
rm -f "$SAVED_DMESG"
}
function push_config() {
DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \
awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}')
@ -99,7 +88,6 @@ function set_ftrace_enabled() {
function cleanup() {
pop_config
cleanup_dmesg_file
}
# setup_config - save the current config and set a script exit trap that
@ -280,7 +268,15 @@ function set_pre_patch_ret {
function start_test {
local test="$1"
save_dmesg
# Dump something unique into the dmesg log, then stash the entry
# in LAST_DMESG. The check_result() function will use it to
# find new kernel messages since the test started.
local last_dmesg_msg="livepatch kselftest timestamp: $(date --rfc-3339=ns)"
log "$last_dmesg_msg"
loop_until 'dmesg | grep -q "$last_dmesg_msg"' ||
die "buffer busy? can't find canary dmesg message: $last_dmesg_msg"
LAST_DMESG=$(dmesg | grep "$last_dmesg_msg")
echo -n "TEST: $test ... "
log "===== TEST: $test ====="
}
@ -291,23 +287,24 @@ function check_result {
local expect="$*"
local result
# Note: when comparing dmesg output, the kernel log timestamps
# help differentiate repeated testing runs. Remove them with a
# post-comparison sed filter.
result=$(dmesg | comm --nocheck-order -13 "$SAVED_DMESG" - | \
# Test results include any new dmesg entry since LAST_DMESG, then:
# - include lines matching keywords
# - exclude lines matching keywords
# - filter out dmesg timestamp prefixes
result=$(dmesg | awk -v last_dmesg="$LAST_DMESG" 'p; $0 == last_dmesg { p=1 }' | \
grep -e 'livepatch:' -e 'test_klp' | \
grep -v '\(tainting\|taints\) kernel' | \
sed 's/^\[[ 0-9.]*\] //')
if [[ "$expect" == "$result" ]] ; then
echo "ok"
elif [[ "$result" == "" ]] ; then
echo -e "not ok\n\nbuffer overrun? can't find canary dmesg entry: $LAST_DMESG\n"
die "livepatch kselftest(s) failed"
else
echo -e "not ok\n\n$(diff -upr --label expected --label result <(echo "$expect") <(echo "$result"))\n"
die "livepatch kselftest(s) failed"
fi
cleanup_dmesg_file
}
# check_sysfs_rights(modname, rel_path, expected_rights) - check sysfs

View File

@ -24,6 +24,11 @@ bool rseq_validate_cpu_id(void)
{
return rseq_mm_cid_available();
}
static
bool rseq_use_cpu_index(void)
{
return false; /* Use mm_cid */
}
#else
# define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID
static
@ -36,6 +41,11 @@ bool rseq_validate_cpu_id(void)
{
return rseq_current_cpu_raw() >= 0;
}
static
bool rseq_use_cpu_index(void)
{
return true; /* Use cpu_id as index. */
}
#endif
struct percpu_lock_entry {
@ -274,7 +284,7 @@ void test_percpu_list(void)
/* Generate list entries for every usable cpu. */
sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
for (i = 0; i < CPU_SETSIZE; i++) {
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
for (j = 1; j <= 100; j++) {
struct percpu_list_node *node;
@ -299,7 +309,7 @@ void test_percpu_list(void)
for (i = 0; i < CPU_SETSIZE; i++) {
struct percpu_list_node *node;
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
while ((node = __percpu_list_pop(&list, i))) {

View File

@ -288,6 +288,11 @@ bool rseq_validate_cpu_id(void)
{
return rseq_mm_cid_available();
}
static
bool rseq_use_cpu_index(void)
{
return false; /* Use mm_cid */
}
# ifdef TEST_MEMBARRIER
/*
* Membarrier does not currently support targeting a mm_cid, so
@ -312,6 +317,11 @@ bool rseq_validate_cpu_id(void)
{
return rseq_current_cpu_raw() >= 0;
}
static
bool rseq_use_cpu_index(void)
{
return true; /* Use cpu_id as index. */
}
# ifdef TEST_MEMBARRIER
static
int rseq_membarrier_expedited(int cpu)
@ -715,7 +725,7 @@ void test_percpu_list(void)
/* Generate list entries for every usable cpu. */
sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
for (i = 0; i < CPU_SETSIZE; i++) {
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
for (j = 1; j <= 100; j++) {
struct percpu_list_node *node;
@ -752,7 +762,7 @@ void test_percpu_list(void)
for (i = 0; i < CPU_SETSIZE; i++) {
struct percpu_list_node *node;
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
while ((node = __percpu_list_pop(&list, i))) {
@ -902,7 +912,7 @@ void test_percpu_buffer(void)
/* Generate list entries for every usable cpu. */
sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
for (i = 0; i < CPU_SETSIZE; i++) {
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
/* Worse-case is every item in same CPU. */
buffer.c[i].array =
@ -952,7 +962,7 @@ void test_percpu_buffer(void)
for (i = 0; i < CPU_SETSIZE; i++) {
struct percpu_buffer_node *node;
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
while ((node = __percpu_buffer_pop(&buffer, i))) {
@ -1113,7 +1123,7 @@ void test_percpu_memcpy_buffer(void)
/* Generate list entries for every usable cpu. */
sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus);
for (i = 0; i < CPU_SETSIZE; i++) {
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
/* Worse-case is every item in same CPU. */
buffer.c[i].array =
@ -1160,7 +1170,7 @@ void test_percpu_memcpy_buffer(void)
for (i = 0; i < CPU_SETSIZE; i++) {
struct percpu_memcpy_buffer_node item;
if (!CPU_ISSET(i, &allowed_cpus))
if (rseq_use_cpu_index() && !CPU_ISSET(i, &allowed_cpus))
continue;
while (__percpu_memcpy_buffer_pop(&buffer, &item, i)) {

View File

@ -38,10 +38,10 @@ unsigned long long timing(clockid_t clk_id, unsigned long long samples)
i *= 1000000000ULL;
i += finish.tv_nsec - start.tv_nsec;
printf("%lu.%09lu - %lu.%09lu = %llu (%.1fs)\n",
finish.tv_sec, finish.tv_nsec,
start.tv_sec, start.tv_nsec,
i, (double)i / 1000000000.0);
ksft_print_msg("%lu.%09lu - %lu.%09lu = %llu (%.1fs)\n",
finish.tv_sec, finish.tv_nsec,
start.tv_sec, start.tv_nsec,
i, (double)i / 1000000000.0);
return i;
}
@ -53,7 +53,7 @@ unsigned long long calibrate(void)
pid_t pid, ret;
int seconds = 15;
printf("Calibrating sample size for %d seconds worth of syscalls ...\n", seconds);
ksft_print_msg("Calibrating sample size for %d seconds worth of syscalls ...\n", seconds);
samples = 0;
pid = getpid();
@ -98,24 +98,36 @@ bool le(int i_one, int i_two)
}
long compare(const char *name_one, const char *name_eval, const char *name_two,
unsigned long long one, bool (*eval)(int, int), unsigned long long two)
unsigned long long one, bool (*eval)(int, int), unsigned long long two,
bool skip)
{
bool good;
printf("\t%s %s %s (%lld %s %lld): ", name_one, name_eval, name_two,
(long long)one, name_eval, (long long)two);
if (skip) {
ksft_test_result_skip("%s %s %s\n", name_one, name_eval,
name_two);
return 0;
}
ksft_print_msg("\t%s %s %s (%lld %s %lld): ", name_one, name_eval, name_two,
(long long)one, name_eval, (long long)two);
if (one > INT_MAX) {
printf("Miscalculation! Measurement went negative: %lld\n", (long long)one);
return 1;
ksft_print_msg("Miscalculation! Measurement went negative: %lld\n", (long long)one);
good = false;
goto out;
}
if (two > INT_MAX) {
printf("Miscalculation! Measurement went negative: %lld\n", (long long)two);
return 1;
ksft_print_msg("Miscalculation! Measurement went negative: %lld\n", (long long)two);
good = false;
goto out;
}
good = eval(one, two);
printf("%s\n", good ? "✔️" : "");
out:
ksft_test_result(good, "%s %s %s\n", name_one, name_eval, name_two);
return good ? 0 : 1;
}
@ -142,15 +154,22 @@ int main(int argc, char *argv[])
unsigned long long samples, calc;
unsigned long long native, filter1, filter2, bitmap1, bitmap2;
unsigned long long entry, per_filter1, per_filter2;
bool skip = false;
setbuf(stdout, NULL);
printf("Running on:\n");
ksft_print_header();
ksft_set_plan(7);
ksft_print_msg("Running on:\n");
ksft_print_msg("");
system("uname -a");
printf("Current BPF sysctl settings:\n");
ksft_print_msg("Current BPF sysctl settings:\n");
/* Avoid using "sysctl" which may not be installed. */
ksft_print_msg("");
system("grep -H . /proc/sys/net/core/bpf_jit_enable");
ksft_print_msg("");
system("grep -H . /proc/sys/net/core/bpf_jit_harden");
if (argc > 1)
@ -158,11 +177,11 @@ int main(int argc, char *argv[])
else
samples = calibrate();
printf("Benchmarking %llu syscalls...\n", samples);
ksft_print_msg("Benchmarking %llu syscalls...\n", samples);
/* Native call */
native = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples;
printf("getpid native: %llu ns\n", native);
ksft_print_msg("getpid native: %llu ns\n", native);
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
assert(ret == 0);
@ -172,35 +191,37 @@ int main(int argc, char *argv[])
assert(ret == 0);
bitmap1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples;
printf("getpid RET_ALLOW 1 filter (bitmap): %llu ns\n", bitmap1);
ksft_print_msg("getpid RET_ALLOW 1 filter (bitmap): %llu ns\n", bitmap1);
/* Second filter resulting in a bitmap */
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog);
assert(ret == 0);
bitmap2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples;
printf("getpid RET_ALLOW 2 filters (bitmap): %llu ns\n", bitmap2);
ksft_print_msg("getpid RET_ALLOW 2 filters (bitmap): %llu ns\n", bitmap2);
/* Third filter, can no longer be converted to bitmap */
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
assert(ret == 0);
filter1 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples;
printf("getpid RET_ALLOW 3 filters (full): %llu ns\n", filter1);
ksft_print_msg("getpid RET_ALLOW 3 filters (full): %llu ns\n", filter1);
/* Fourth filter, can not be converted to bitmap because of filter 3 */
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &bitmap_prog);
assert(ret == 0);
filter2 = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples;
printf("getpid RET_ALLOW 4 filters (full): %llu ns\n", filter2);
ksft_print_msg("getpid RET_ALLOW 4 filters (full): %llu ns\n", filter2);
/* Estimations */
#define ESTIMATE(fmt, var, what) do { \
var = (what); \
printf("Estimated " fmt ": %llu ns\n", var); \
if (var > INT_MAX) \
goto more_samples; \
ksft_print_msg("Estimated " fmt ": %llu ns\n", var); \
if (var > INT_MAX) { \
skip = true; \
ret |= 1; \
} \
} while (0)
ESTIMATE("total seccomp overhead for 1 bitmapped filter", calc,
@ -218,31 +239,34 @@ int main(int argc, char *argv[])
ESTIMATE("seccomp per-filter overhead (filters / 4)", per_filter2,
(filter2 - native - entry) / 4);
printf("Expectations:\n");
ret |= compare("native", "", "1 bitmap", native, le, bitmap1);
bits = compare("native", "", "1 filter", native, le, filter1);
ksft_print_msg("Expectations:\n");
ret |= compare("native", "", "1 bitmap", native, le, bitmap1,
skip);
bits = compare("native", "", "1 filter", native, le, filter1,
skip);
if (bits)
goto more_samples;
skip = true;
ret |= compare("per-filter (last 2 diff)", "", "per-filter (filters / 4)",
per_filter1, approx, per_filter2);
per_filter1, approx, per_filter2, skip);
bits = compare("1 bitmapped", "", "2 bitmapped",
bitmap1 - native, approx, bitmap2 - native);
bitmap1 - native, approx, bitmap2 - native, skip);
if (bits) {
printf("Skipping constant action bitmap expectations: they appear unsupported.\n");
goto out;
ksft_print_msg("Skipping constant action bitmap expectations: they appear unsupported.\n");
skip = true;
}
ret |= compare("entry", "", "1 bitmapped", entry, approx, bitmap1 - native);
ret |= compare("entry", "", "2 bitmapped", entry, approx, bitmap2 - native);
ret |= compare("entry", "", "1 bitmapped", entry, approx,
bitmap1 - native, skip);
ret |= compare("entry", "", "2 bitmapped", entry, approx,
bitmap2 - native, skip);
ret |= compare("native + entry + (per filter * 4)", "", "4 filters total",
entry + (per_filter1 * 4) + native, approx, filter2);
if (ret == 0)
goto out;
entry + (per_filter1 * 4) + native, approx, filter2,
skip);
more_samples:
printf("Saw unexpected benchmark result. Try running again with more samples?\n");
out:
return 0;
if (ret)
ksft_print_msg("Saw unexpected benchmark result. Try running again with more samples?\n");
ksft_finished();
}