1*15858da5SNam Cao // SPDX-License-Identifier: GPL-2.0
2*15858da5SNam Cao 
3*15858da5SNam Cao #include <fcntl.h>
4*15858da5SNam Cao #include <libgen.h>
5*15858da5SNam Cao #include <linux/limits.h>
6*15858da5SNam Cao #include <pthread.h>
7*15858da5SNam Cao #include <string.h>
8*15858da5SNam Cao #include <sys/resource.h>
9*15858da5SNam Cao #include <unistd.h>
10*15858da5SNam Cao 
11*15858da5SNam Cao #include "../kselftest_harness.h"
12*15858da5SNam Cao 
13*15858da5SNam Cao #define STACKDUMP_FILE "stack_values"
14*15858da5SNam Cao #define STACKDUMP_SCRIPT "stackdump"
15*15858da5SNam Cao #define NUM_THREAD_SPAWN 128
16*15858da5SNam Cao 
do_nothing(void *)17*15858da5SNam Cao static void *do_nothing(void *)
18*15858da5SNam Cao {
19*15858da5SNam Cao 	while (1)
20*15858da5SNam Cao 		pause();
21*15858da5SNam Cao }
22*15858da5SNam Cao 
crashing_child(void)23*15858da5SNam Cao static void crashing_child(void)
24*15858da5SNam Cao {
25*15858da5SNam Cao 	pthread_t thread;
26*15858da5SNam Cao 	int i;
27*15858da5SNam Cao 
28*15858da5SNam Cao 	for (i = 0; i < NUM_THREAD_SPAWN; ++i)
29*15858da5SNam Cao 		pthread_create(&thread, NULL, do_nothing, NULL);
30*15858da5SNam Cao 
31*15858da5SNam Cao 	/* crash on purpose */
32*15858da5SNam Cao 	i = *(int *)NULL;
33*15858da5SNam Cao }
34*15858da5SNam Cao 
FIXTURE(coredump)35*15858da5SNam Cao FIXTURE(coredump)
36*15858da5SNam Cao {
37*15858da5SNam Cao 	char original_core_pattern[256];
38*15858da5SNam Cao };
39*15858da5SNam Cao 
FIXTURE_SETUP(coredump)40*15858da5SNam Cao FIXTURE_SETUP(coredump)
41*15858da5SNam Cao {
42*15858da5SNam Cao 	char buf[PATH_MAX];
43*15858da5SNam Cao 	FILE *file;
44*15858da5SNam Cao 	char *dir;
45*15858da5SNam Cao 	int ret;
46*15858da5SNam Cao 
47*15858da5SNam Cao 	file = fopen("/proc/sys/kernel/core_pattern", "r");
48*15858da5SNam Cao 	ASSERT_NE(NULL, file);
49*15858da5SNam Cao 
50*15858da5SNam Cao 	ret = fread(self->original_core_pattern, 1, sizeof(self->original_core_pattern), file);
51*15858da5SNam Cao 	ASSERT_TRUE(ret || feof(file));
52*15858da5SNam Cao 	ASSERT_LT(ret, sizeof(self->original_core_pattern));
53*15858da5SNam Cao 
54*15858da5SNam Cao 	self->original_core_pattern[ret] = '\0';
55*15858da5SNam Cao 
56*15858da5SNam Cao 	ret = fclose(file);
57*15858da5SNam Cao 	ASSERT_EQ(0, ret);
58*15858da5SNam Cao }
59*15858da5SNam Cao 
FIXTURE_TEARDOWN(coredump)60*15858da5SNam Cao FIXTURE_TEARDOWN(coredump)
61*15858da5SNam Cao {
62*15858da5SNam Cao 	const char *reason;
63*15858da5SNam Cao 	FILE *file;
64*15858da5SNam Cao 	int ret;
65*15858da5SNam Cao 
66*15858da5SNam Cao 	unlink(STACKDUMP_FILE);
67*15858da5SNam Cao 
68*15858da5SNam Cao 	file = fopen("/proc/sys/kernel/core_pattern", "w");
69*15858da5SNam Cao 	if (!file) {
70*15858da5SNam Cao 		reason = "Unable to open core_pattern";
71*15858da5SNam Cao 		goto fail;
72*15858da5SNam Cao 	}
73*15858da5SNam Cao 
74*15858da5SNam Cao 	ret = fprintf(file, "%s", self->original_core_pattern);
75*15858da5SNam Cao 	if (ret < 0) {
76*15858da5SNam Cao 		reason = "Unable to write to core_pattern";
77*15858da5SNam Cao 		goto fail;
78*15858da5SNam Cao 	}
79*15858da5SNam Cao 
80*15858da5SNam Cao 	ret = fclose(file);
81*15858da5SNam Cao 	if (ret) {
82*15858da5SNam Cao 		reason = "Unable to close core_pattern";
83*15858da5SNam Cao 		goto fail;
84*15858da5SNam Cao 	}
85*15858da5SNam Cao 
86*15858da5SNam Cao 	return;
87*15858da5SNam Cao fail:
88*15858da5SNam Cao 	/* This should never happen */
89*15858da5SNam Cao 	fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason);
90*15858da5SNam Cao }
91*15858da5SNam Cao 
TEST_F(coredump,stackdump)92*15858da5SNam Cao TEST_F(coredump, stackdump)
93*15858da5SNam Cao {
94*15858da5SNam Cao 	struct sigaction action = {};
95*15858da5SNam Cao 	unsigned long long stack;
96*15858da5SNam Cao 	char *test_dir, *line;
97*15858da5SNam Cao 	size_t line_length;
98*15858da5SNam Cao 	char buf[PATH_MAX];
99*15858da5SNam Cao 	int ret, i;
100*15858da5SNam Cao 	FILE *file;
101*15858da5SNam Cao 	pid_t pid;
102*15858da5SNam Cao 
103*15858da5SNam Cao 	/*
104*15858da5SNam Cao 	 * Step 1: Setup core_pattern so that the stackdump script is executed when the child
105*15858da5SNam Cao 	 * process crashes
106*15858da5SNam Cao 	 */
107*15858da5SNam Cao 	ret = readlink("/proc/self/exe", buf, sizeof(buf));
108*15858da5SNam Cao 	ASSERT_NE(-1, ret);
109*15858da5SNam Cao 	ASSERT_LT(ret, sizeof(buf));
110*15858da5SNam Cao 	buf[ret] = '\0';
111*15858da5SNam Cao 
112*15858da5SNam Cao 	test_dir = dirname(buf);
113*15858da5SNam Cao 
114*15858da5SNam Cao 	file = fopen("/proc/sys/kernel/core_pattern", "w");
115*15858da5SNam Cao 	ASSERT_NE(NULL, file);
116*15858da5SNam Cao 
117*15858da5SNam Cao 	ret = fprintf(file, "|%1$s/%2$s %%P %1$s/%3$s", test_dir, STACKDUMP_SCRIPT, STACKDUMP_FILE);
118*15858da5SNam Cao 	ASSERT_LT(0, ret);
119*15858da5SNam Cao 
120*15858da5SNam Cao 	ret = fclose(file);
121*15858da5SNam Cao 	ASSERT_EQ(0, ret);
122*15858da5SNam Cao 
123*15858da5SNam Cao 	/* Step 2: Create a process who spawns some threads then crashes */
124*15858da5SNam Cao 	pid = fork();
125*15858da5SNam Cao 	ASSERT_TRUE(pid >= 0);
126*15858da5SNam Cao 	if (pid == 0)
127*15858da5SNam Cao 		crashing_child();
128*15858da5SNam Cao 
129*15858da5SNam Cao 	/*
130*15858da5SNam Cao 	 * Step 3: Wait for the stackdump script to write the stack pointers to the stackdump file
131*15858da5SNam Cao 	 */
132*15858da5SNam Cao 	for (i = 0; i < 10; ++i) {
133*15858da5SNam Cao 		file = fopen(STACKDUMP_FILE, "r");
134*15858da5SNam Cao 		if (file)
135*15858da5SNam Cao 			break;
136*15858da5SNam Cao 		sleep(1);
137*15858da5SNam Cao 	}
138*15858da5SNam Cao 	ASSERT_NE(file, NULL);
139*15858da5SNam Cao 
140*15858da5SNam Cao 	/* Step 4: Make sure all stack pointer values are non-zero */
141*15858da5SNam Cao 	for (i = 0; -1 != getline(&line, &line_length, file); ++i) {
142*15858da5SNam Cao 		stack = strtoull(line, NULL, 10);
143*15858da5SNam Cao 		ASSERT_NE(stack, 0);
144*15858da5SNam Cao 	}
145*15858da5SNam Cao 
146*15858da5SNam Cao 	ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN);
147*15858da5SNam Cao 
148*15858da5SNam Cao 	fclose(file);
149*15858da5SNam Cao }
150*15858da5SNam Cao 
151*15858da5SNam Cao TEST_HARNESS_MAIN
152