Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: improve vm.memfd_noexec sysctl tests

This adds proper tests for the nesting functionality of vm.memfd_noexec as
well as some minor cleanups to spawn_*_thread().

Link: https://lkml.kernel.org/r/20230814-memfd-vm-noexec-uapi-fixes-v2-5-7ff9e3e10ba6@cyphar.com
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Daniel Verkamp <dverkamp@chromium.org>
Cc: Dominique Martinet <asmadeus@codewreck.org>
Cc: Jeff Xu <jeffxu@google.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Aleksa Sarai and committed by
Andrew Morton
6469b66e 9876cfe8

+268 -99
+268 -99
tools/testing/selftests/memfd/memfd_test.c
··· 18 18 #include <sys/syscall.h> 19 19 #include <sys/wait.h> 20 20 #include <unistd.h> 21 + #include <ctype.h> 21 22 22 23 #include "common.h" 23 24 ··· 44 43 */ 45 44 static size_t mfd_def_size = MFD_DEF_SIZE; 46 45 static const char *memfd_str = MEMFD_STR; 47 - static pid_t spawn_newpid_thread(unsigned int flags, int (*fn)(void *)); 48 46 static int newpid_thread_fn2(void *arg); 49 47 static void join_newpid_thread(pid_t pid); 50 48 ··· 96 96 int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC); 97 97 98 98 if (fd < 0) { 99 - printf("open sysctl failed\n"); 99 + printf("open sysctl failed: %m\n"); 100 100 abort(); 101 101 } 102 102 103 103 if (write(fd, val, strlen(val)) < 0) { 104 - printf("write sysctl failed\n"); 104 + printf("write sysctl %s failed: %m\n", val); 105 105 abort(); 106 106 } 107 107 } ··· 111 111 int fd = open("/proc/sys/vm/memfd_noexec", O_WRONLY | O_CLOEXEC); 112 112 113 113 if (fd < 0) { 114 - printf("open sysctl failed\n"); 114 + printf("open sysctl failed: %m\n"); 115 115 abort(); 116 116 } 117 117 118 118 if (write(fd, val, strlen(val)) >= 0) { 119 119 printf("write sysctl %s succeeded, but failure expected\n", 120 120 val); 121 + abort(); 122 + } 123 + } 124 + 125 + static void sysctl_assert_equal(const char *val) 126 + { 127 + char *p, buf[128] = {}; 128 + int fd = open("/proc/sys/vm/memfd_noexec", O_RDONLY | O_CLOEXEC); 129 + 130 + if (fd < 0) { 131 + printf("open sysctl failed: %m\n"); 132 + abort(); 133 + } 134 + 135 + if (read(fd, buf, sizeof(buf)) < 0) { 136 + printf("read sysctl failed: %m\n"); 137 + abort(); 138 + } 139 + 140 + /* Strip trailing whitespace. */ 141 + p = buf; 142 + while (!isspace(*p)) 143 + p++; 144 + *p = '\0'; 145 + 146 + if (strcmp(buf, val) != 0) { 147 + printf("unexpected sysctl value: expected %s, got %s\n", val, buf); 121 148 abort(); 122 149 } 123 150 } ··· 763 736 return 0; 764 737 } 765 738 766 - static pid_t spawn_idle_thread(unsigned int flags) 739 + static pid_t spawn_thread(unsigned int flags, int (*fn)(void *), void *arg) 767 740 { 768 741 uint8_t *stack; 769 742 pid_t pid; ··· 774 747 abort(); 775 748 } 776 749 777 - pid = clone(idle_thread_fn, 778 - stack + STACK_SIZE, 779 - SIGCHLD | flags, 780 - NULL); 750 + pid = clone(fn, stack + STACK_SIZE, SIGCHLD | flags, arg); 781 751 if (pid < 0) { 782 752 printf("clone() failed: %m\n"); 783 753 abort(); 784 754 } 785 755 786 756 return pid; 757 + } 758 + 759 + static void join_thread(pid_t pid) 760 + { 761 + int wstatus; 762 + 763 + if (waitpid(pid, &wstatus, 0) < 0) { 764 + printf("newpid thread: waitpid() failed: %m\n"); 765 + abort(); 766 + } 767 + 768 + if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) { 769 + printf("newpid thread: exited with non-zero error code %d\n", 770 + WEXITSTATUS(wstatus)); 771 + abort(); 772 + } 773 + 774 + if (WIFSIGNALED(wstatus)) { 775 + printf("newpid thread: killed by signal %d\n", 776 + WTERMSIG(wstatus)); 777 + abort(); 778 + } 779 + } 780 + 781 + static pid_t spawn_idle_thread(unsigned int flags) 782 + { 783 + return spawn_thread(flags, idle_thread_fn, NULL); 787 784 } 788 785 789 786 static void join_idle_thread(pid_t pid) ··· 1162 1111 close(fd); 1163 1112 } 1164 1113 1165 - static void test_sysctl_child(void) 1114 + static void test_sysctl_sysctl0(void) 1166 1115 { 1167 1116 int fd; 1168 - int pid; 1169 1117 1170 - printf("%s sysctl 0\n", memfd_str); 1171 - sysctl_assert_write("0"); 1172 - fd = mfd_assert_new("kern_memfd_sysctl_0", 1118 + sysctl_assert_equal("0"); 1119 + 1120 + fd = mfd_assert_new("kern_memfd_sysctl_0_dfl", 1173 1121 mfd_def_size, 1174 1122 MFD_CLOEXEC | MFD_ALLOW_SEALING); 1123 + mfd_assert_mode(fd, 0777); 1124 + mfd_assert_has_seals(fd, 0); 1125 + mfd_assert_chmod(fd, 0644); 1126 + close(fd); 1127 + } 1175 1128 1129 + static void test_sysctl_set_sysctl0(void) 1130 + { 1131 + sysctl_assert_write("0"); 1132 + test_sysctl_sysctl0(); 1133 + } 1134 + 1135 + static void test_sysctl_sysctl1(void) 1136 + { 1137 + int fd; 1138 + 1139 + sysctl_assert_equal("1"); 1140 + 1141 + fd = mfd_assert_new("kern_memfd_sysctl_1_dfl", 1142 + mfd_def_size, 1143 + MFD_CLOEXEC | MFD_ALLOW_SEALING); 1144 + mfd_assert_mode(fd, 0666); 1145 + mfd_assert_has_seals(fd, F_SEAL_EXEC); 1146 + mfd_fail_chmod(fd, 0777); 1147 + close(fd); 1148 + 1149 + fd = mfd_assert_new("kern_memfd_sysctl_1_exec", 1150 + mfd_def_size, 1151 + MFD_CLOEXEC | MFD_EXEC | MFD_ALLOW_SEALING); 1176 1152 mfd_assert_mode(fd, 0777); 1177 1153 mfd_assert_has_seals(fd, 0); 1178 1154 mfd_assert_chmod(fd, 0644); 1179 1155 close(fd); 1180 1156 1181 - printf("%s sysctl 1\n", memfd_str); 1182 - sysctl_assert_write("1"); 1183 - fd = mfd_assert_new("kern_memfd_sysctl_1", 1157 + fd = mfd_assert_new("kern_memfd_sysctl_1_noexec", 1184 1158 mfd_def_size, 1185 - MFD_CLOEXEC | MFD_ALLOW_SEALING); 1186 - 1187 - printf("%s child ns\n", memfd_str); 1188 - pid = spawn_newpid_thread(CLONE_NEWPID, newpid_thread_fn2); 1189 - join_newpid_thread(pid); 1190 - 1159 + MFD_CLOEXEC | MFD_NOEXEC_SEAL | MFD_ALLOW_SEALING); 1191 1160 mfd_assert_mode(fd, 0666); 1192 1161 mfd_assert_has_seals(fd, F_SEAL_EXEC); 1193 1162 mfd_fail_chmod(fd, 0777); 1194 - sysctl_fail_write("0"); 1195 1163 close(fd); 1164 + } 1196 1165 1197 - printf("%s sysctl 2\n", memfd_str); 1198 - sysctl_assert_write("2"); 1199 - mfd_fail_new("kern_memfd_sysctl_2_exec", 1200 - MFD_EXEC | MFD_CLOEXEC | MFD_ALLOW_SEALING); 1166 + static void test_sysctl_set_sysctl1(void) 1167 + { 1168 + sysctl_assert_write("1"); 1169 + test_sysctl_sysctl1(); 1170 + } 1171 + 1172 + static void test_sysctl_sysctl2(void) 1173 + { 1174 + int fd; 1175 + 1176 + sysctl_assert_equal("2"); 1201 1177 1202 1178 fd = mfd_assert_new("kern_memfd_sysctl_2_dfl", 1203 1179 mfd_def_size, ··· 1234 1156 mfd_fail_chmod(fd, 0777); 1235 1157 close(fd); 1236 1158 1237 - fd = mfd_assert_new("kern_memfd_sysctl_2_noexec_seal", 1159 + mfd_fail_new("kern_memfd_sysctl_2_exec", 1160 + MFD_CLOEXEC | MFD_EXEC | MFD_ALLOW_SEALING); 1161 + 1162 + fd = mfd_assert_new("kern_memfd_sysctl_2_noexec", 1238 1163 mfd_def_size, 1239 - MFD_NOEXEC_SEAL | MFD_CLOEXEC | MFD_ALLOW_SEALING); 1164 + MFD_CLOEXEC | MFD_NOEXEC_SEAL | MFD_ALLOW_SEALING); 1240 1165 mfd_assert_mode(fd, 0666); 1241 1166 mfd_assert_has_seals(fd, F_SEAL_EXEC); 1242 1167 mfd_fail_chmod(fd, 0777); 1243 1168 close(fd); 1244 - 1245 - sysctl_fail_write("0"); 1246 - sysctl_fail_write("1"); 1247 1169 } 1248 1170 1249 - static int newpid_thread_fn(void *arg) 1171 + static void test_sysctl_set_sysctl2(void) 1250 1172 { 1251 - test_sysctl_child(); 1252 - return 0; 1173 + sysctl_assert_write("2"); 1174 + test_sysctl_sysctl2(); 1253 1175 } 1254 1176 1255 - static void test_sysctl_child2(void) 1177 + static int sysctl_simple_child(void *arg) 1256 1178 { 1257 1179 int fd; 1180 + int pid; 1258 1181 1259 - sysctl_fail_write("0"); 1260 - fd = mfd_assert_new("kern_memfd_sysctl_1", 1261 - mfd_def_size, 1262 - MFD_CLOEXEC | MFD_ALLOW_SEALING); 1182 + printf("%s sysctl 0\n", memfd_str); 1183 + test_sysctl_set_sysctl0(); 1263 1184 1264 - mfd_assert_mode(fd, 0666); 1265 - mfd_assert_has_seals(fd, F_SEAL_EXEC); 1266 - mfd_fail_chmod(fd, 0777); 1267 - close(fd); 1268 - } 1185 + printf("%s sysctl 1\n", memfd_str); 1186 + test_sysctl_set_sysctl1(); 1269 1187 1270 - static int newpid_thread_fn2(void *arg) 1271 - { 1272 - test_sysctl_child2(); 1188 + printf("%s sysctl 0\n", memfd_str); 1189 + test_sysctl_set_sysctl0(); 1190 + 1191 + printf("%s sysctl 2\n", memfd_str); 1192 + test_sysctl_set_sysctl2(); 1193 + 1194 + printf("%s sysctl 1\n", memfd_str); 1195 + test_sysctl_set_sysctl1(); 1196 + 1197 + printf("%s sysctl 0\n", memfd_str); 1198 + test_sysctl_set_sysctl0(); 1199 + 1273 1200 return 0; 1274 - } 1275 - static pid_t spawn_newpid_thread(unsigned int flags, int (*fn)(void *)) 1276 - { 1277 - uint8_t *stack; 1278 - pid_t pid; 1279 - 1280 - stack = malloc(STACK_SIZE); 1281 - if (!stack) { 1282 - printf("malloc(STACK_SIZE) failed: %m\n"); 1283 - abort(); 1284 - } 1285 - 1286 - pid = clone(fn, 1287 - stack + STACK_SIZE, 1288 - SIGCHLD | flags, 1289 - NULL); 1290 - if (pid < 0) { 1291 - printf("clone() failed: %m\n"); 1292 - abort(); 1293 - } 1294 - 1295 - return pid; 1296 - } 1297 - 1298 - static void join_newpid_thread(pid_t pid) 1299 - { 1300 - int wstatus; 1301 - 1302 - if (waitpid(pid, &wstatus, 0) < 0) { 1303 - printf("newpid thread: waitpid() failed: %m\n"); 1304 - abort(); 1305 - } 1306 - 1307 - if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) != 0) { 1308 - printf("newpid thread: exited with non-zero error code %d\n", 1309 - WEXITSTATUS(wstatus)); 1310 - abort(); 1311 - } 1312 - 1313 - if (WIFSIGNALED(wstatus)) { 1314 - printf("newpid thread: killed by signal %d\n", 1315 - WTERMSIG(wstatus)); 1316 - abort(); 1317 - } 1318 1201 } 1319 1202 1320 1203 /* 1321 1204 * Test sysctl 1322 - * A very basic sealing test to see whether setting/retrieving seals works. 1205 + * A very basic test to make sure the core sysctl semantics work. 1323 1206 */ 1324 - static void test_sysctl(void) 1207 + static void test_sysctl_simple(void) 1325 1208 { 1326 - int pid = spawn_newpid_thread(CLONE_NEWPID, newpid_thread_fn); 1209 + int pid = spawn_thread(CLONE_NEWPID, sysctl_simple_child, NULL); 1327 1210 1328 - join_newpid_thread(pid); 1211 + join_thread(pid); 1212 + } 1213 + 1214 + static int sysctl_nested(void *arg) 1215 + { 1216 + void (*fn)(void) = arg; 1217 + 1218 + fn(); 1219 + return 0; 1220 + } 1221 + 1222 + static int sysctl_nested_wait(void *arg) 1223 + { 1224 + /* Wait for a SIGCONT. */ 1225 + kill(getpid(), SIGSTOP); 1226 + return sysctl_nested(arg); 1227 + } 1228 + 1229 + static void test_sysctl_sysctl1_failset(void) 1230 + { 1231 + sysctl_fail_write("0"); 1232 + test_sysctl_sysctl1(); 1233 + } 1234 + 1235 + static void test_sysctl_sysctl2_failset(void) 1236 + { 1237 + sysctl_fail_write("1"); 1238 + test_sysctl_sysctl2(); 1239 + 1240 + sysctl_fail_write("0"); 1241 + test_sysctl_sysctl2(); 1242 + } 1243 + 1244 + static int sysctl_nested_child(void *arg) 1245 + { 1246 + int fd; 1247 + int pid; 1248 + 1249 + printf("%s nested sysctl 0\n", memfd_str); 1250 + sysctl_assert_write("0"); 1251 + /* A further nested pidns works the same. */ 1252 + pid = spawn_thread(CLONE_NEWPID, sysctl_simple_child, NULL); 1253 + join_thread(pid); 1254 + 1255 + printf("%s nested sysctl 1\n", memfd_str); 1256 + sysctl_assert_write("1"); 1257 + /* Child inherits our setting. */ 1258 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, test_sysctl_sysctl1); 1259 + join_thread(pid); 1260 + /* Child cannot raise the setting. */ 1261 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, 1262 + test_sysctl_sysctl1_failset); 1263 + join_thread(pid); 1264 + /* Child can lower the setting. */ 1265 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, 1266 + test_sysctl_set_sysctl2); 1267 + join_thread(pid); 1268 + /* Child lowering the setting has no effect on our setting. */ 1269 + test_sysctl_sysctl1(); 1270 + 1271 + printf("%s nested sysctl 2\n", memfd_str); 1272 + sysctl_assert_write("2"); 1273 + /* Child inherits our setting. */ 1274 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, test_sysctl_sysctl2); 1275 + join_thread(pid); 1276 + /* Child cannot raise the setting. */ 1277 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested, 1278 + test_sysctl_sysctl2_failset); 1279 + join_thread(pid); 1280 + 1281 + /* Verify that the rules are actually inherited after fork. */ 1282 + printf("%s nested sysctl 0 -> 1 after fork\n", memfd_str); 1283 + sysctl_assert_write("0"); 1284 + 1285 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1286 + test_sysctl_sysctl1_failset); 1287 + sysctl_assert_write("1"); 1288 + kill(pid, SIGCONT); 1289 + join_thread(pid); 1290 + 1291 + printf("%s nested sysctl 0 -> 2 after fork\n", memfd_str); 1292 + sysctl_assert_write("0"); 1293 + 1294 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1295 + test_sysctl_sysctl2_failset); 1296 + sysctl_assert_write("2"); 1297 + kill(pid, SIGCONT); 1298 + join_thread(pid); 1299 + 1300 + /* 1301 + * Verify that the current effective setting is saved on fork, meaning 1302 + * that the parent lowering the sysctl doesn't affect already-forked 1303 + * children. 1304 + */ 1305 + printf("%s nested sysctl 2 -> 1 after fork\n", memfd_str); 1306 + sysctl_assert_write("2"); 1307 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1308 + test_sysctl_sysctl2); 1309 + sysctl_assert_write("1"); 1310 + kill(pid, SIGCONT); 1311 + join_thread(pid); 1312 + 1313 + printf("%s nested sysctl 2 -> 0 after fork\n", memfd_str); 1314 + sysctl_assert_write("2"); 1315 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1316 + test_sysctl_sysctl2); 1317 + sysctl_assert_write("0"); 1318 + kill(pid, SIGCONT); 1319 + join_thread(pid); 1320 + 1321 + printf("%s nested sysctl 1 -> 0 after fork\n", memfd_str); 1322 + sysctl_assert_write("1"); 1323 + pid = spawn_thread(CLONE_NEWPID, sysctl_nested_wait, 1324 + test_sysctl_sysctl1); 1325 + sysctl_assert_write("0"); 1326 + kill(pid, SIGCONT); 1327 + join_thread(pid); 1328 + 1329 + return 0; 1330 + } 1331 + 1332 + /* 1333 + * Test sysctl with nested pid namespaces 1334 + * Make sure that the sysctl nesting semantics work correctly. 1335 + */ 1336 + static void test_sysctl_nested(void) 1337 + { 1338 + int pid = spawn_thread(CLONE_NEWPID, sysctl_nested_child, NULL); 1339 + 1340 + join_thread(pid); 1329 1341 } 1330 1342 1331 1343 /* ··· 1601 1433 test_seal_grow(); 1602 1434 test_seal_resize(); 1603 1435 1436 + test_sysctl_simple(); 1437 + test_sysctl_nested(); 1438 + 1604 1439 test_share_dup("SHARE-DUP", ""); 1605 1440 test_share_mmap("SHARE-MMAP", ""); 1606 1441 test_share_open("SHARE-OPEN", ""); ··· 1617 1446 test_share_open("SHARE-OPEN", SHARED_FT_STR); 1618 1447 test_share_fork("SHARE-FORK", SHARED_FT_STR); 1619 1448 join_idle_thread(pid); 1620 - 1621 - test_sysctl(); 1622 1449 1623 1450 printf("memfd: DONE\n"); 1624 1451