Commit 8bda5b78 authored by Amery Hung's avatar Amery Hung Committed by Alexei Starovoitov
Browse files

selftests/bpf: Fix dangling stdout seen by traffic monitor thread



Traffic monitor thread may see dangling stdout as the main thread closes
and reassigns stdout without protection. This happens when the main thread
finishes one subtest and moves to another one in the same netns_new()
scope.

The issue can be reproduced by running test_progs repeatedly with traffic
monitor enabled:

for ((i=1;i<=100;i++)); do
   ./test_progs -a flow_dissector_skb* -m '*'
done

For restoring stdout in crash_handler(), since it does not really care
about closing stdout, simlpy flush stdout and restore it to the original
one.

Then, Fix the issue by consolidating stdio_restore_cleanup() and
stdio_restore(), and protecting the use/close/assignment of stdout with
a lock. The locking in the main thread is always performed regradless of
whether traffic monitor is running or not for simplicity. It won't have
any side-effect.

Signed-off-by: default avatarAmery Hung <ameryhung@gmail.com>
Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
Acked-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://patch.msgid.link/20250305182057.2802606-3-ameryhung@gmail.com


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 34a25aab
Loading
Loading
Loading
Loading
+22 −17
Original line number Diff line number Diff line
@@ -88,7 +88,9 @@ static void stdio_hijack(char **log_buf, size_t *log_cnt)
#endif
}

static void stdio_restore_cleanup(void)
static pthread_mutex_t stdout_lock = PTHREAD_MUTEX_INITIALIZER;

static void stdio_restore(void)
{
#ifdef __GLIBC__
	if (verbose() && env.worker_id == -1) {
@@ -98,6 +100,8 @@ static void stdio_restore_cleanup(void)

	fflush(stdout);

	pthread_mutex_lock(&stdout_lock);

	if (env.subtest_state) {
		fclose(env.subtest_state->stdout_saved);
		env.subtest_state->stdout_saved = NULL;
@@ -106,26 +110,21 @@ static void stdio_restore_cleanup(void)
	} else {
		fclose(env.test_state->stdout_saved);
		env.test_state->stdout_saved = NULL;
		stdout = env.stdout_saved;
		stderr = env.stderr_saved;
	}

	pthread_mutex_unlock(&stdout_lock);
#endif
}

static void stdio_restore(void)
static int traffic_monitor_print_fn(const char *format, va_list args)
{
#ifdef __GLIBC__
	if (verbose() && env.worker_id == -1) {
		/* nothing to do, output to stdout by default */
		return;
	}

	if (stdout == env.stdout_saved)
		return;

	stdio_restore_cleanup();
	pthread_mutex_lock(&stdout_lock);
	vfprintf(stdout, format, args);
	pthread_mutex_unlock(&stdout_lock);

	stdout = env.stdout_saved;
	stderr = env.stderr_saved;
#endif
	return 0;
}

/* Adapted from perf/util/string.c */
@@ -536,7 +535,8 @@ void test__end_subtest(void)
				   test_result(subtest_state->error_cnt,
					       subtest_state->skipped));

	stdio_restore_cleanup();
	stdio_restore();

	env.subtest_state = NULL;
}

@@ -1265,7 +1265,10 @@ void crash_handler(int signum)

	sz = backtrace(bt, ARRAY_SIZE(bt));

	stdio_restore();
	fflush(stdout);
	stdout = env.stdout_saved;
	stderr = env.stderr_saved;

	if (env.test) {
		env.test_state->error_cnt++;
		dump_test_log(env.test, env.test_state, true, false, NULL);
@@ -1957,6 +1960,8 @@ int main(int argc, char **argv)
	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
	libbpf_set_print(libbpf_print_fn);

	traffic_monitor_set_print(traffic_monitor_print_fn);

	srand(time(NULL));

	env.jit_enabled = is_jit_enabled();