1 1.6 riastrad /* $NetBSD: t_clone.c,v 1.6 2024/09/27 18:50:01 riastradh Exp $ */ 2 1.1 pgoyette 3 1.1 pgoyette /*- 4 1.1 pgoyette * Copyright (c) 2008 The NetBSD Foundation, Inc. 5 1.1 pgoyette * All rights reserved. 6 1.1 pgoyette * 7 1.1 pgoyette * This code is derived from software contributed to The NetBSD Foundation 8 1.1 pgoyette * by Jason R. Thorpe. 9 1.1 pgoyette * 10 1.1 pgoyette * Redistribution and use in source and binary forms, with or without 11 1.1 pgoyette * modification, are permitted provided that the following conditions 12 1.1 pgoyette * are met: 13 1.1 pgoyette * 1. Redistributions of source code must retain the above copyright 14 1.1 pgoyette * notice, this list of conditions and the following disclaimer. 15 1.1 pgoyette * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 pgoyette * notice, this list of conditions and the following disclaimer in the 17 1.1 pgoyette * documentation and/or other materials provided with the distribution. 18 1.1 pgoyette * 19 1.1 pgoyette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 pgoyette * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 pgoyette * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 pgoyette * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 pgoyette * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 pgoyette * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 pgoyette * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 pgoyette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 pgoyette * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 pgoyette * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 pgoyette * POSSIBILITY OF SUCH DAMAGE. 30 1.1 pgoyette */ 31 1.1 pgoyette 32 1.1 pgoyette #include <sys/cdefs.h> 33 1.1 pgoyette __COPYRIGHT("@(#) Copyright (c) 2008\ 34 1.1 pgoyette The NetBSD Foundation, inc. All rights reserved."); 35 1.6 riastrad __RCSID("$NetBSD: t_clone.c,v 1.6 2024/09/27 18:50:01 riastradh Exp $"); 36 1.1 pgoyette 37 1.5 christos #include <sys/param.h> 38 1.5 christos #include <sys/types.h> 39 1.1 pgoyette #include <sys/mman.h> 40 1.1 pgoyette #include <sys/resource.h> 41 1.1 pgoyette #include <sys/wait.h> 42 1.1 pgoyette 43 1.1 pgoyette #include <errno.h> 44 1.1 pgoyette #include <sched.h> 45 1.1 pgoyette #include <signal.h> 46 1.1 pgoyette #include <stdio.h> 47 1.1 pgoyette #include <stdlib.h> 48 1.1 pgoyette #include <string.h> 49 1.1 pgoyette #include <unistd.h> 50 1.1 pgoyette 51 1.1 pgoyette #include <atf-c.h> 52 1.1 pgoyette 53 1.1 pgoyette #define STACKSIZE (8 * 1024) 54 1.1 pgoyette #define FROBVAL 41973 55 1.1 pgoyette #define CHILDEXIT 0xa5 56 1.1 pgoyette 57 1.5 christos static void * 58 1.5 christos getstack(void) 59 1.5 christos { 60 1.5 christos void *stack = mmap(NULL, STACKSIZE, PROT_READ|PROT_WRITE, 61 1.5 christos MAP_PRIVATE|MAP_ANON, -1, (off_t) 0); 62 1.5 christos ATF_REQUIRE_ERRNO(errno, stack != MAP_FAILED); 63 1.5 christos #ifndef __MACHINE_STACK_GROWS_UP 64 1.5 christos stack = (char *)stack + STACKSIZE; 65 1.5 christos #endif 66 1.5 christos return stack; 67 1.5 christos } 68 1.5 christos 69 1.6 riastrad static void 70 1.5 christos putstack(void *stack) 71 1.5 christos { 72 1.5 christos #ifndef __MACHINE_STACK_GROWS_UP 73 1.5 christos stack = (char *)stack - STACKSIZE; 74 1.5 christos #endif 75 1.5 christos ATF_REQUIRE_ERRNO(errno, munmap(stack, STACKSIZE) != -1); 76 1.5 christos } 77 1.5 christos 78 1.1 pgoyette static int 79 1.1 pgoyette dummy(void *arg) 80 1.1 pgoyette { 81 1.1 pgoyette 82 1.1 pgoyette return 0; 83 1.1 pgoyette } 84 1.1 pgoyette 85 1.1 pgoyette static int 86 1.1 pgoyette clone_func(void *arg) 87 1.1 pgoyette { 88 1.1 pgoyette long *frobp = arg, diff; 89 1.1 pgoyette 90 1.1 pgoyette printf("child: stack ~= %p, frobme = %p\n", &frobp, frobp); 91 1.1 pgoyette fflush(stdout); 92 1.1 pgoyette 93 1.1 pgoyette if (frobp[0] != getppid()) 94 1.1 pgoyette return 1; 95 1.1 pgoyette 96 1.1 pgoyette if (frobp[0] == getpid()) 97 1.1 pgoyette return 2; 98 1.1 pgoyette 99 1.1 pgoyette diff = labs(frobp[1] - (long) &frobp); 100 1.1 pgoyette 101 1.1 pgoyette if (diff > 1024) 102 1.1 pgoyette return 3; 103 1.1 pgoyette 104 1.1 pgoyette frobp[1] = FROBVAL; 105 1.1 pgoyette 106 1.1 pgoyette return (CHILDEXIT); 107 1.1 pgoyette } 108 1.1 pgoyette 109 1.2 jruoho ATF_TC(clone_basic); 110 1.2 jruoho ATF_TC_HEAD(clone_basic, tc) 111 1.2 jruoho { 112 1.2 jruoho 113 1.2 jruoho atf_tc_set_md_var(tc, "descr", "Checks clone(2)"); 114 1.2 jruoho } 115 1.2 jruoho 116 1.2 jruoho ATF_TC_BODY(clone_basic, tc) 117 1.1 pgoyette { 118 1.1 pgoyette sigset_t mask; 119 1.5 christos void *stack = getstack(); 120 1.1 pgoyette pid_t pid; 121 1.1 pgoyette volatile long frobme[2]; 122 1.1 pgoyette int stat; 123 1.1 pgoyette 124 1.1 pgoyette printf("parent: stack = %p, frobme = %p\n", stack, frobme); 125 1.1 pgoyette fflush(stdout); 126 1.1 pgoyette 127 1.1 pgoyette frobme[0] = (long)getpid(); 128 1.1 pgoyette frobme[1] = (long)stack; 129 1.1 pgoyette 130 1.1 pgoyette sigemptyset(&mask); 131 1.1 pgoyette sigaddset(&mask, SIGUSR1); 132 1.1 pgoyette 133 1.1 pgoyette ATF_REQUIRE_ERRNO(errno, sigprocmask(SIG_BLOCK, &mask, NULL) != -1); 134 1.1 pgoyette 135 1.1 pgoyette switch (pid = __clone(clone_func, stack, 136 1.1 pgoyette CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|SIGUSR1, 137 1.1 pgoyette __UNVOLATILE(frobme))) { 138 1.1 pgoyette case 0: 139 1.1 pgoyette atf_tc_fail("clone() returned 0"); 140 1.1 pgoyette /*NOTREACHED*/ 141 1.1 pgoyette case -1: 142 1.1 pgoyette atf_tc_fail("clone() failed: %s", strerror(errno)); 143 1.1 pgoyette /*NOTREACHED*/ 144 1.1 pgoyette default: 145 1.1 pgoyette while (waitpid(pid, &stat, __WCLONE) != pid) 146 1.1 pgoyette continue; 147 1.1 pgoyette } 148 1.1 pgoyette 149 1.1 pgoyette ATF_REQUIRE_MSG(WIFEXITED(stat) != 0, "child didn't exit"); 150 1.1 pgoyette 151 1.1 pgoyette printf("parent: childexit = 0x%x, frobme = %ld\n", 152 1.1 pgoyette WEXITSTATUS(stat), frobme[1]); 153 1.1 pgoyette 154 1.1 pgoyette switch (WEXITSTATUS(stat)) { 155 1.1 pgoyette case CHILDEXIT: 156 1.1 pgoyette ATF_REQUIRE_EQ(frobme[1], FROBVAL); 157 1.1 pgoyette break; 158 1.1 pgoyette case 1: 159 1.1 pgoyette atf_tc_fail("child: argument does not contain parent's pid"); 160 1.1 pgoyette /*NOTREACHED*/ 161 1.1 pgoyette case 2: 162 1.1 pgoyette atf_tc_fail("child: called in parent's pid"); 163 1.1 pgoyette /*NOTREACHED*/ 164 1.1 pgoyette case 3: 165 1.1 pgoyette atf_tc_fail("child: called with bad stack"); 166 1.1 pgoyette /*NOTREACHED*/ 167 1.1 pgoyette default: 168 1.1 pgoyette atf_tc_fail("child returned unknown code: %d", 169 1.1 pgoyette WEXITSTATUS(stat)); 170 1.1 pgoyette /*NOTREACHED*/ 171 1.1 pgoyette } 172 1.1 pgoyette 173 1.5 christos putstack(stack); 174 1.1 pgoyette } 175 1.1 pgoyette 176 1.2 jruoho ATF_TC(clone_null_stack); 177 1.2 jruoho ATF_TC_HEAD(clone_null_stack, tc) 178 1.1 pgoyette { 179 1.1 pgoyette 180 1.1 pgoyette atf_tc_set_md_var(tc, "descr", 181 1.1 pgoyette "Checks that clone(2) fails when stack pointer is NULL"); 182 1.1 pgoyette } 183 1.1 pgoyette 184 1.2 jruoho ATF_TC_BODY(clone_null_stack, tc) 185 1.1 pgoyette { 186 1.1 pgoyette int rv; 187 1.1 pgoyette 188 1.1 pgoyette rv = __clone(dummy, NULL, 189 1.1 pgoyette CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|SIGCHLD, NULL); 190 1.1 pgoyette 191 1.1 pgoyette ATF_REQUIRE_EQ(rv, -1); 192 1.1 pgoyette ATF_REQUIRE_EQ(errno, EINVAL); 193 1.1 pgoyette } 194 1.1 pgoyette 195 1.2 jruoho ATF_TC(clone_null_func); 196 1.2 jruoho ATF_TC_HEAD(clone_null_func, tc) 197 1.1 pgoyette { 198 1.1 pgoyette 199 1.1 pgoyette atf_tc_set_md_var(tc, "descr", 200 1.1 pgoyette "Checks that clone(2) fails when function pointer is NULL"); 201 1.1 pgoyette } 202 1.1 pgoyette 203 1.2 jruoho ATF_TC_BODY(clone_null_func, tc) 204 1.1 pgoyette { 205 1.5 christos void *stack = getstack(); 206 1.3 joerg int rv; 207 1.1 pgoyette 208 1.1 pgoyette 209 1.1 pgoyette errno = 0; 210 1.1 pgoyette rv = __clone(0, stack, 211 1.1 pgoyette CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|SIGCHLD, NULL); 212 1.1 pgoyette 213 1.1 pgoyette ATF_REQUIRE_EQ(rv, -1); 214 1.1 pgoyette ATF_REQUIRE_EQ(errno, EINVAL); 215 1.1 pgoyette 216 1.5 christos putstack(stack); 217 1.1 pgoyette } 218 1.1 pgoyette 219 1.2 jruoho ATF_TC(clone_out_of_proc); 220 1.2 jruoho ATF_TC_HEAD(clone_out_of_proc, tc) 221 1.1 pgoyette { 222 1.1 pgoyette 223 1.1 pgoyette atf_tc_set_md_var(tc, "descr", 224 1.1 pgoyette "Checks that clone(2) fails when running out of processes"); 225 1.1 pgoyette atf_tc_set_md_var(tc, "require.user", "unprivileged"); 226 1.1 pgoyette } 227 1.1 pgoyette 228 1.2 jruoho ATF_TC_BODY(clone_out_of_proc, tc) 229 1.1 pgoyette { 230 1.5 christos char *stack = getstack(); 231 1.1 pgoyette struct rlimit rl; 232 1.1 pgoyette int rv; 233 1.1 pgoyette 234 1.1 pgoyette ATF_REQUIRE_ERRNO(errno, getrlimit(RLIMIT_NPROC, &rl) != -1); 235 1.1 pgoyette 236 1.1 pgoyette rl.rlim_cur = 0; 237 1.1 pgoyette rl.rlim_max = 0; 238 1.1 pgoyette 239 1.1 pgoyette ATF_REQUIRE_ERRNO(errno, setrlimit(RLIMIT_NPROC, &rl) != -1); 240 1.1 pgoyette 241 1.1 pgoyette errno = 0; 242 1.5 christos rv = __clone(dummy, stack, 243 1.1 pgoyette CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|SIGCHLD, (void *)&rl); 244 1.1 pgoyette 245 1.1 pgoyette ATF_REQUIRE_EQ(rv, -1); 246 1.1 pgoyette ATF_REQUIRE_EQ(errno, EAGAIN); 247 1.5 christos putstack(stack); 248 1.1 pgoyette } 249 1.1 pgoyette 250 1.1 pgoyette ATF_TP_ADD_TCS(tp) 251 1.1 pgoyette { 252 1.1 pgoyette 253 1.2 jruoho ATF_TP_ADD_TC(tp, clone_basic); 254 1.2 jruoho ATF_TP_ADD_TC(tp, clone_null_stack); 255 1.2 jruoho ATF_TP_ADD_TC(tp, clone_null_func); 256 1.2 jruoho ATF_TP_ADD_TC(tp, clone_out_of_proc); 257 1.1 pgoyette 258 1.1 pgoyette return atf_no_error(); 259 1.1 pgoyette } 260