1 /* $NetBSD: t_execregs.c,v 1.7 2025/04/27 14:30:03 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2025 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __RCSID("$NetBSD: t_execregs.c,v 1.7 2025/04/27 14:30:03 riastradh Exp $"); 31 32 #include <sys/wait.h> 33 34 #include <atf-c.h> 35 #include <err.h> 36 #include <inttypes.h> 37 #include <limits.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #ifdef HAVE_EXECREGS_TEST 43 44 #include "execregs.h" 45 #include "isqemu.h" 46 #include "h_macros.h" 47 48 static void 49 readregs(int rfd, register_t regs[static NEXECREGS]) 50 { 51 uint8_t *p; 52 size_t n; 53 ssize_t nread; 54 55 p = (void *)regs; 56 n = NEXECREGS*sizeof(regs[0]); 57 while (n) { 58 RL(nread = read(rfd, p, n)); 59 ATF_CHECK_MSG((size_t)nread <= n, 60 "overlong read: %zu > %zu", (size_t)nread, n); 61 if (nread == 0) 62 break; 63 p += (size_t)nread; 64 n -= (size_t)nread; 65 } 66 ATF_CHECK_EQ_MSG(n, 0, 67 "truncated read, missing %zu of %zu bytes", 68 n, NEXECREGS*sizeof(regs[0])); 69 } 70 71 static void 72 checkregs(const register_t regs[static NEXECREGS]) 73 { 74 unsigned i; 75 76 #ifdef __hppa__ 77 if (isQEMU()) { 78 atf_tc_expect_fail("PR port-hppa/59114: hppa:" 79 " eager fpu switching for qemu and/or spectre mitigation"); 80 } 81 #endif 82 83 for (i = 0; i < NEXECREGS; i++) { 84 if (regs[i] != 0) { 85 for (i = 0; i < NEXECREGS; i++) { 86 fprintf(stderr, "[%s] %"PRIxREGISTER"\n", 87 regname[i], regs[i]); 88 } 89 fprintf(stderr, "\n"); 90 atf_tc_fail("registers not zeroed"); 91 } 92 } 93 } 94 95 static void 96 testregs(int child, const int pipefd[static 2], 97 register_t regs[static NEXECREGS]) 98 { 99 int status; 100 101 RL(close(pipefd[1])); 102 103 readregs(pipefd[0], regs); 104 105 RL(waitpid(child, &status, 0)); 106 if (WIFSIGNALED(status)) { 107 atf_tc_fail_nonfatal("child terminated on signal %d (%s)", 108 WTERMSIG(status), strsignal(WTERMSIG(status))); 109 } else if (!WIFEXITED(status)) { 110 atf_tc_fail_nonfatal("child terminated mysteriously," 111 " status=0x%x", 112 status); 113 } else { 114 ATF_CHECK_MSG(WEXITSTATUS(status) == 0, 115 "child exited with code %d", WEXITSTATUS(status)); 116 } 117 118 checkregs(regs); 119 } 120 121 #endif 122 123 ATF_TC(execregszero); 124 ATF_TC_HEAD(execregszero, tc) 125 { 126 atf_tc_set_md_var(tc, "descr", 127 "Test execve(2) zeroes registers"); 128 } 129 ATF_TC_BODY(execregszero, tc) 130 { 131 #ifdef HAVE_EXECREGS_TEST 132 char h_execregs[PATH_MAX]; 133 int pipefd[2]; 134 register_t regs[NEXECREGS]; 135 pid_t child; 136 137 RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs", 138 atf_tc_get_config_var(tc, "srcdir"))); 139 140 RL(pipe(pipefd)); 141 memset(regs, 0x5a, sizeof(regs)); 142 143 RL(child = fork()); 144 if (child == 0) { 145 if (dup2(pipefd[1], STDOUT_FILENO) == -1) 146 err(1, "dup2"); 147 if (closefrom(STDERR_FILENO + 1) == -1) 148 err(1, "closefrom"); 149 if (execregschild(h_execregs) == -1) 150 err(1, "execve"); 151 _exit(2); 152 } 153 154 testregs(child, pipefd, regs); 155 #else 156 atf_tc_skip("missing test for PR kern/59084:" 157 " exec/spawn leaks register content"); 158 #endif 159 } 160 161 ATF_TC(spawnregszero); 162 ATF_TC_HEAD(spawnregszero, tc) 163 { 164 atf_tc_set_md_var(tc, "descr", 165 "Test posix_spawn(2) zeroes registers"); 166 } 167 ATF_TC_BODY(spawnregszero, tc) 168 { 169 #ifdef HAVE_EXECREGS_TEST 170 char h_execregs[PATH_MAX]; 171 int pipefd[2]; 172 register_t regs[NEXECREGS]; 173 pid_t child; 174 175 RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs", 176 atf_tc_get_config_var(tc, "srcdir"))); 177 178 RL(pipe(pipefd)); 179 memset(regs, 0x5a, sizeof(regs)); 180 181 RL(child = spawnregschild(h_execregs, pipefd[1])); 182 183 testregs(child, pipefd, regs); 184 #else 185 atf_tc_skip("missing test for PR kern/59084:" 186 " exec/spawn leaks register content"); 187 #endif 188 } 189 190 ATF_TP_ADD_TCS(tp) 191 { 192 193 ATF_TP_ADD_TC(tp, execregszero); 194 ATF_TP_ADD_TC(tp, spawnregszero); 195 196 return atf_no_error(); 197 } 198