Commit d1d873a9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'linux_kselftest-fixes-6.8-rc3' of...

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
parents 53ed2ac8 b54761f6
Loading
Loading
Loading
Loading
+17 −20
Original line number Diff line number Diff line
@@ -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
+12 −2
Original line number Diff line number Diff line
@@ -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))) {
+16 −6
Original line number Diff line number Diff line
@@ -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)) {
+64 −40
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ 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",
	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);
@@ -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,
	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();
}