Home | History | Annotate | Download | only in clone
      1 /*
      2  * Copyright (c) 2013 Fujitsu Ltd.
      3  * Author: Zeng Linggang <zenglg.jy (at) cn.fujitsu.com>
      4  *
      5  * This program is free software; you can redistribute it and/or modify it
      6  * under the terms of version 2 of the GNU General Public License as
      7  * published by the Free Software Foundation.
      8  *
      9  * This program is distributed in the hope that it would be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     12  *
     13  * You should have received a copy of the GNU General Public License along
     14  * with this program.
     15  */
     16 
     17 #define _GNU_SOURCE
     18 #include <errno.h>
     19 #include <sched.h>
     20 #include <sys/wait.h>
     21 #include "test.h"
     22 #include "clone_platform.h"
     23 #include "safe_macros.h"
     24 #include "linux_syscall_numbers.h"
     25 
     26 char *TCID = "clone08";
     27 
     28 static pid_t ptid, ctid, tgid;
     29 static void *child_stack;
     30 
     31 static void setup(void);
     32 static void cleanup(void);
     33 
     34 static void test_clone_parent(int t);
     35 static int child_clone_parent(void);
     36 static pid_t parent_ppid;
     37 
     38 static void test_clone_tid(int t);
     39 static int child_clone_child_settid(void);
     40 static int child_clone_parent_settid(void);
     41 
     42 #ifdef CLONE_STOPPED
     43 static void test_clone_stopped(int t);
     44 static int child_clone_stopped(void);
     45 static int stopped_flag;
     46 #endif
     47 
     48 static void test_clone_thread(int t);
     49 static int child_clone_thread(void);
     50 static int tst_result;
     51 
     52 /*
     53  * Children cloned with CLONE_VM should avoid using any functions that
     54  * might require dl_runtime_resolve, because they share thread-local
     55  * storage with parent. If both try to resolve symbols at same time you
     56  * can crash, likely at _dl_x86_64_restore_sse().
     57  * See this thread for relevant discussion:
     58  * http://www.mail-archive.com/utrace-devel@redhat.com/msg01944.html
     59  */
     60 static struct test_case {
     61 	char *name;
     62 	int flags;
     63 	void (*testfunc)(int);
     64 	int (*do_child)();
     65 } test_cases[] = {
     66 	{"CLONE_PARENT", CLONE_PARENT | SIGCHLD,
     67 	 test_clone_parent, child_clone_parent},
     68 	{"CLONE_CHILD_SETTID", CLONE_CHILD_SETTID | SIGCHLD,
     69 	 test_clone_tid, child_clone_child_settid},
     70 	{"CLONE_PARENT_SETTID", CLONE_PARENT_SETTID | CLONE_VM | SIGCHLD,
     71 	 test_clone_tid, child_clone_parent_settid},
     72 #ifdef CLONE_STOPPED
     73 	{"CLONE_STOPPED", CLONE_STOPPED | CLONE_VM | SIGCHLD,
     74 	 test_clone_stopped, child_clone_stopped},
     75 #endif
     76 	{"CLONE_THREAD", CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | SIGCHLD,
     77 	 test_clone_thread, child_clone_thread},
     78 };
     79 
     80 int TST_TOTAL = ARRAY_SIZE(test_cases);
     81 
     82 int main(int ac, char **av)
     83 {
     84 	int i, lc;
     85 
     86 	tst_parse_opts(ac, av, NULL, NULL);
     87 
     88 	setup();
     89 	for (lc = 0; TEST_LOOPING(lc); lc++) {
     90 		tst_count = 0;
     91 		for (i = 0; i < TST_TOTAL; i++) {
     92 			tst_resm(TINFO, "running %s", test_cases[i].name);
     93 			test_cases[i].testfunc(i);
     94 		}
     95 	}
     96 	cleanup();
     97 	tst_exit();
     98 }
     99 
    100 static void setup(void)
    101 {
    102 	tst_sig(FORK, DEF_HANDLER, cleanup);
    103 
    104 	TEST_PAUSE;
    105 
    106 	tst_tmpdir();
    107 
    108 	child_stack = SAFE_MALLOC(cleanup, CHILD_STACK_SIZE);
    109 }
    110 
    111 static void cleanup(void)
    112 {
    113 	free(child_stack);
    114 
    115 	tst_rmdir();
    116 }
    117 
    118 static long clone_child(const struct test_case *t, int use_tst)
    119 {
    120 	TEST(ltp_clone7(t->flags, t->do_child, NULL, CHILD_STACK_SIZE,
    121 		child_stack, &ptid, NULL, &ctid));
    122 
    123 	if (TEST_RETURN == -1 && TTERRNO == ENOSYS)
    124 		tst_brkm(TCONF, cleanup, "clone does not support 7 args");
    125 
    126 	if (TEST_RETURN == -1) {
    127 		if (use_tst) {
    128 			tst_brkm(TBROK | TTERRNO, cleanup, "%s clone() failed",
    129 				 t->name);
    130 		} else {
    131 			printf("%s clone() failed, errno: %d",
    132 			       t->name, TEST_ERRNO);
    133 			exit(1);
    134 		}
    135 	}
    136 	return TEST_RETURN;
    137 }
    138 
    139 static int wait4child(pid_t child)
    140 {
    141 	int status;
    142 
    143 	if (waitpid(child, &status, 0) == -1)
    144 		tst_resm(TBROK|TERRNO, "waitpid");
    145 	if (WIFEXITED(status))
    146 		return WEXITSTATUS(status);
    147 	else
    148 		return status;
    149 }
    150 
    151 static void test_clone_parent(int t)
    152 {
    153 	int status;
    154 	pid_t child;
    155 
    156 	fflush(stdout);
    157 	child = FORK_OR_VFORK();
    158 	switch (child) {
    159 	case 0:
    160 		parent_ppid = getppid();
    161 		clone_child(&test_cases[t], 0);
    162 		exit(0);
    163 	case -1:
    164 		tst_brkm(TBROK | TERRNO, NULL, "test_clone_parent fork");
    165 	default:
    166 		status = wait4child(child);
    167 		if (status == 0) {
    168 			/* wait for CLONE_PARENT child */
    169 			status = wait4child(-1);
    170 			if (status == 0) {
    171 				tst_resm(TPASS, "test %s", test_cases[t].name);
    172 			} else {
    173 				tst_resm(TFAIL, "test %s, status: %d",
    174 					 test_cases[t].name, status);
    175 			}
    176 		} else {
    177 			tst_resm(TFAIL, "test %s, status: %d",
    178 				 test_cases[t].name, status);
    179 		}
    180 	};
    181 }
    182 
    183 static int child_clone_parent(void)
    184 {
    185 	if (parent_ppid == getppid())
    186 		exit(0);
    187 	printf("FAIL: getppid != parent_ppid (%d != %d)\n",
    188 	       parent_ppid, getppid());
    189 	exit(1);
    190 }
    191 
    192 static void test_clone_tid(int t)
    193 {
    194 	int status;
    195 	pid_t child;
    196 
    197 	child = clone_child(&test_cases[t], 1);
    198 	status = wait4child(child);
    199 	if (status == 0) {
    200 		tst_resm(TPASS, "test %s", test_cases[t].name);
    201 	} else {
    202 		tst_resm(TFAIL, "test %s, status: %d",
    203 			 test_cases[t].name, status);
    204 	}
    205 }
    206 
    207 static int child_clone_child_settid(void)
    208 {
    209 	if (ctid == ltp_syscall(__NR_getpid))
    210 		ltp_syscall(__NR_exit, 0);
    211 	printf("FAIL: ctid != getpid() (%d != %d)\n",
    212 	       ctid, getpid());
    213 	ltp_syscall(__NR_exit, 1);
    214 	return 0;
    215 }
    216 
    217 static int child_clone_parent_settid(void)
    218 {
    219 	if (ptid == ltp_syscall(__NR_getpid))
    220 		ltp_syscall(__NR_exit, 0);
    221 	printf("FAIL: ptid != getpid() (%d != %d)\n",
    222 	       ptid, getpid());
    223 	ltp_syscall(__NR_exit, 1);
    224 	return 0;
    225 }
    226 
    227 #ifdef CLONE_STOPPED
    228 static void test_clone_stopped(int t)
    229 {
    230 	int i;
    231 	int status;
    232 	int flag;
    233 	pid_t child;
    234 
    235 	if (tst_kvercmp(2, 6, 38) >= 0) {
    236 		tst_resm(TINFO, "CLONE_STOPPED skipped for kernels >= 2.6.38");
    237 		return;
    238 	}
    239 
    240 	stopped_flag = 0;
    241 	child = clone_child(&test_cases[t], 1);
    242 
    243 	/* give the kernel scheduler chance to run the CLONE_STOPPED thread*/
    244 	for (i = 0; i < 100; i++) {
    245 		sched_yield();
    246 		usleep(1000);
    247 	}
    248 
    249 	flag = stopped_flag;
    250 	if (kill(child, SIGCONT) != 0)
    251 		tst_brkm(TBROK | TERRNO, cleanup, "kill SIGCONT failed");
    252 
    253 	status = wait4child(child);
    254 	if (status == 0 && flag == 0) {
    255 		tst_resm(TPASS, "test %s", test_cases[t].name);
    256 	} else {
    257 		tst_resm(TFAIL, "test %s, status: %d, flag: %d",
    258 			 test_cases[t].name, status, flag);
    259 	}
    260 }
    261 
    262 static int child_clone_stopped(void)
    263 {
    264 	stopped_flag = 1;
    265 	ltp_syscall(__NR_exit, 0);
    266 	return 0;
    267 }
    268 #endif
    269 
    270 static void test_clone_thread(int t)
    271 {
    272 	pid_t child;
    273 	int i, status;
    274 
    275 	fflush(stdout);
    276 	child = FORK_OR_VFORK();
    277 	switch (child) {
    278 	case 0:
    279 		tgid = ltp_syscall(__NR_getpid);
    280 		tst_result = -1;
    281 		clone_child(&test_cases[t], 0);
    282 
    283 		for (i = 0; i < 5000; i++) {
    284 			sched_yield();
    285 			usleep(1000);
    286 			if (tst_result != -1)
    287 				break;
    288 		}
    289 		ltp_syscall(__NR_exit, tst_result);
    290 	case -1:
    291 		tst_brkm(TBROK | TERRNO, NULL, "test_clone_thread fork");
    292 	default:
    293 		status = wait4child(child);
    294 		if (status == 0) {
    295 			tst_resm(TPASS, "test %s", test_cases[t].name);
    296 		} else {
    297 			tst_resm(TFAIL, "test %s, status: %d",
    298 				 test_cases[t].name, status);
    299 		}
    300 	};
    301 }
    302 
    303 static int child_clone_thread(void)
    304 {
    305 	if (tgid == ltp_syscall(__NR_getpid))
    306 		tst_result = TPASS;
    307 	else
    308 		tst_result = TFAIL;
    309 	ltp_syscall(__NR_exit, 0);
    310 	return 0;
    311 }
    312