xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/clone/clone08.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2017 Oracle and/or its affiliates. All Rights Reserved.
4  * Copyright (c) 2013 Fujitsu Ltd.
5  * Author: Zeng Linggang <[email protected]>
6  */
7 
8 #define _GNU_SOURCE
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <sched.h>
13 #include <sys/wait.h>
14 
15 #include "tst_test.h"
16 #include "clone_platform.h"
17 #include "lapi/syscalls.h"
18 #include "lapi/futex.h"
19 
20 static pid_t ptid, ctid, tgid;
21 static void *child_stack;
22 
23 static void test_clone_parent(int t);
24 static int child_clone_parent(void *);
25 static pid_t parent_ppid;
26 
27 static void test_clone_tid(int t);
28 static int child_clone_child_settid(void *);
29 static int child_clone_parent_settid(void *);
30 
31 
32 static void test_clone_thread(int t);
33 static int child_clone_thread(void *);
34 
35 /*
36  * Children cloned with CLONE_VM should avoid using any functions that
37  * might require dl_runtime_resolve, because they share thread-local
38  * storage with parent. If both try to resolve symbols at same time you
39  * can crash, likely at _dl_x86_64_restore_sse().
40  * See this thread for relevant discussion:
41  * http://www.mail-archive.com/[email protected]/msg01944.html
42  */
43 static struct test_case {
44 	char *name;
45 	int flags;
46 	void (*testfunc)(int);
47 	int (*do_child)(void *);
48 } test_cases[] = {
49 	{"CLONE_PARENT", CLONE_PARENT | SIGCHLD,
50 	 test_clone_parent, child_clone_parent},
51 	{"CLONE_CHILD_SETTID", CLONE_CHILD_SETTID | SIGCHLD,
52 	 test_clone_tid, child_clone_child_settid},
53 	{"CLONE_PARENT_SETTID", CLONE_PARENT_SETTID | CLONE_VM | SIGCHLD,
54 	 test_clone_tid, child_clone_parent_settid},
55 	{"CLONE_THREAD", CLONE_THREAD | CLONE_SIGHAND | CLONE_VM |
56 	 CLONE_CHILD_CLEARTID | SIGCHLD,
57 	 test_clone_thread, child_clone_thread},
58 };
59 
do_test(unsigned int i)60 static void do_test(unsigned int i)
61 {
62 	tst_res(TINFO, "running %s", test_cases[i].name);
63 	test_cases[i].testfunc(i);
64 }
65 
setup(void)66 static void setup(void)
67 {
68 	child_stack = SAFE_MALLOC(CHILD_STACK_SIZE);
69 }
70 
cleanup(void)71 static void cleanup(void)
72 {
73 	free(child_stack);
74 }
75 
clone_child(const struct test_case * t)76 static long clone_child(const struct test_case *t)
77 {
78 	TEST(ltp_clone7(t->flags, t->do_child, NULL, CHILD_STACK_SIZE,
79 		child_stack, &ptid, NULL, &ctid));
80 
81 	if (TST_RET == -1 && TTERRNO == ENOSYS)
82 		tst_brk(TCONF, "clone does not support 7 args");
83 
84 	if (TST_RET == -1)
85 		tst_brk(TBROK | TTERRNO, "%s clone() failed", t->name);
86 
87 	return TST_RET;
88 }
89 
test_clone_parent(int t)90 static void test_clone_parent(int t)
91 {
92 	pid_t child;
93 
94 	child = SAFE_FORK();
95 	if (!child) {
96 		parent_ppid = getppid();
97 		clone_child(&test_cases[t]);
98 		_exit(0);
99 	}
100 	tst_reap_children();
101 }
102 
child_clone_parent(void * arg LTP_ATTRIBUTE_UNUSED)103 static int child_clone_parent(void *arg LTP_ATTRIBUTE_UNUSED)
104 {
105 	if (parent_ppid == getppid()) {
106 		tst_res(TPASS, "clone and forked child has the same parent");
107 	} else {
108 		tst_res(TFAIL, "getppid != parent_ppid (%d != %d)",
109 			parent_ppid, getppid());
110 	}
111 	tst_syscall(__NR_exit, 0);
112 	return 0;
113 }
114 
test_clone_tid(int t)115 static void test_clone_tid(int t)
116 {
117 	clone_child(&test_cases[t]);
118 	tst_reap_children();
119 }
120 
child_clone_child_settid(void * arg LTP_ATTRIBUTE_UNUSED)121 static int child_clone_child_settid(void *arg LTP_ATTRIBUTE_UNUSED)
122 {
123 	if (ctid == tst_syscall(__NR_getpid))
124 		tst_res(TPASS, "clone() correctly set ctid");
125 	else
126 		tst_res(TFAIL, "ctid != getpid() (%d != %d)", ctid, getpid());
127 	tst_syscall(__NR_exit, 0);
128 	return 0;
129 }
130 
child_clone_parent_settid(void * arg LTP_ATTRIBUTE_UNUSED)131 static int child_clone_parent_settid(void *arg LTP_ATTRIBUTE_UNUSED)
132 {
133 	if (ptid == tst_syscall(__NR_getpid))
134 		tst_res(TPASS, "clone() correctly set ptid");
135 	else
136 		tst_res(TFAIL, "ptid != getpid() (%d != %d)", ptid, getpid());
137 	tst_syscall(__NR_exit, 0);
138 	return 0;
139 }
140 
test_clone_thread(int t)141 static void test_clone_thread(int t)
142 {
143 	pid_t child;
144 
145 	child = SAFE_FORK();
146 	if (!child) {
147 		struct timespec timeout = { 5 /* sec */, 0 };
148 
149 		tgid = tst_syscall(__NR_getpid);
150 		ctid = -1;
151 
152 		clone_child(&test_cases[t]);
153 
154 		if (syscall(SYS_futex, &ctid, FUTEX_WAIT, -1, &timeout)) {
155 			/*
156 			 * futex here is racing with clone() above.
157 			 * Which means we can get EWOULDBLOCK if
158 			 * ctid has been already changed by clone()
159 			 * before we make the call. As long as ctid
160 			 * changes we should not report error when
161 			 * futex returns EWOULDBLOCK.
162 			 */
163 			if (errno != EWOULDBLOCK || ctid == -1) {
164 				tst_res(TFAIL | TERRNO,
165 					"futex failed, ctid: %d", ctid);
166 				_exit(0);
167 			}
168 		}
169 		tst_res(TPASS, "futex exit on ctid change, ctid: %d", ctid);
170 		_exit(0);
171 	}
172 
173 	tst_reap_children();
174 }
175 
child_clone_thread(void * arg LTP_ATTRIBUTE_UNUSED)176 static int child_clone_thread(void *arg LTP_ATTRIBUTE_UNUSED)
177 {
178 	if (tgid == tst_syscall(__NR_getpid))
179 		tst_res(TPASS, "clone has the same thread id");
180 	else
181 		tst_res(TFAIL, "clone's thread id not equal parent's id");
182 	tst_syscall(__NR_exit, 0);
183 	return 0;
184 }
185 
186 static struct tst_test test = {
187 	.tcnt = ARRAY_SIZE(test_cases),
188 	.test = do_test,
189 	.setup = setup,
190 	.cleanup = cleanup,
191 	.forks_child = 1
192 };
193