1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Taken from the kernel self tests, which in turn were based on 4f08c3bdfSopenharmony_ci * a Syzkaller reproducer. 5f08c3bdfSopenharmony_ci * 6f08c3bdfSopenharmony_ci * Self test author and close_range author: 7f08c3bdfSopenharmony_ci * Christian Brauner <christian.brauner@ubuntu.com> 8f08c3bdfSopenharmony_ci * 9f08c3bdfSopenharmony_ci * LTP Author: Richard Palethorpe <rpalethorpe@suse.com> 10f08c3bdfSopenharmony_ci * Copyright (c) 2021 SUSE LLC, other copyrights may apply. 11f08c3bdfSopenharmony_ci */ 12f08c3bdfSopenharmony_ci/*\ 13f08c3bdfSopenharmony_ci * [Description] 14f08c3bdfSopenharmony_ci * 15f08c3bdfSopenharmony_ci * We check that close_range() 16f08c3bdfSopenharmony_ci * 17f08c3bdfSopenharmony_ci * - closes FDs 18f08c3bdfSopenharmony_ci * - UNSHARES some FDs before closing them 19f08c3bdfSopenharmony_ci * - it sets CLOEXEC (in both cloned process and parent) 20f08c3bdfSopenharmony_ci * - combination of CLOEXEC and UNSHARE. 21f08c3bdfSopenharmony_ci * 22f08c3bdfSopenharmony_ci * The final test is the actual bug reproducer. Note that we call 23f08c3bdfSopenharmony_ci * clone directly to share the file table. 24f08c3bdfSopenharmony_ci */ 25f08c3bdfSopenharmony_ci 26f08c3bdfSopenharmony_ci#include <stdlib.h> 27f08c3bdfSopenharmony_ci 28f08c3bdfSopenharmony_ci#include "tst_test.h" 29f08c3bdfSopenharmony_ci#include "tst_clone.h" 30f08c3bdfSopenharmony_ci 31f08c3bdfSopenharmony_ci#include "lapi/sched.h" 32f08c3bdfSopenharmony_ci#include "lapi/close_range.h" 33f08c3bdfSopenharmony_ci 34f08c3bdfSopenharmony_cistatic int fd[3]; 35f08c3bdfSopenharmony_ci 36f08c3bdfSopenharmony_cistatic inline void do_close_range(unsigned int fd, unsigned int max_fd, 37f08c3bdfSopenharmony_ci unsigned int flags) 38f08c3bdfSopenharmony_ci{ 39f08c3bdfSopenharmony_ci int ret = close_range(fd, max_fd, flags); 40f08c3bdfSopenharmony_ci 41f08c3bdfSopenharmony_ci if (!ret) 42f08c3bdfSopenharmony_ci return; 43f08c3bdfSopenharmony_ci 44f08c3bdfSopenharmony_ci if (errno == EINVAL) { 45f08c3bdfSopenharmony_ci if (flags & CLOSE_RANGE_UNSHARE) 46f08c3bdfSopenharmony_ci tst_brk(TCONF | TERRNO, "No CLOSE_RANGE_UNSHARE"); 47f08c3bdfSopenharmony_ci if (flags & CLOSE_RANGE_CLOEXEC) 48f08c3bdfSopenharmony_ci tst_brk(TCONF | TERRNO, "No CLOSE_RANGE_CLOEXEC"); 49f08c3bdfSopenharmony_ci } 50f08c3bdfSopenharmony_ci 51f08c3bdfSopenharmony_ci tst_brk(TBROK | TERRNO, "close_range(%d, %d, %d)", fd, max_fd, flags); 52f08c3bdfSopenharmony_ci} 53f08c3bdfSopenharmony_ci 54f08c3bdfSopenharmony_cistatic void setup(void) 55f08c3bdfSopenharmony_ci{ 56f08c3bdfSopenharmony_ci close_range_supported_by_kernel(); 57f08c3bdfSopenharmony_ci 58f08c3bdfSopenharmony_ci struct rlimit nfd; 59f08c3bdfSopenharmony_ci 60f08c3bdfSopenharmony_ci SAFE_GETRLIMIT(RLIMIT_NOFILE, &nfd); 61f08c3bdfSopenharmony_ci 62f08c3bdfSopenharmony_ci if (nfd.rlim_max < 1000) { 63f08c3bdfSopenharmony_ci tst_brk(TCONF, "NOFILE limit max too low: %lu < 1000", 64f08c3bdfSopenharmony_ci nfd.rlim_max); 65f08c3bdfSopenharmony_ci } 66f08c3bdfSopenharmony_ci 67f08c3bdfSopenharmony_ci nfd.rlim_cur = nfd.rlim_max; 68f08c3bdfSopenharmony_ci SAFE_SETRLIMIT(RLIMIT_NOFILE, &nfd); 69f08c3bdfSopenharmony_ci} 70f08c3bdfSopenharmony_ci 71f08c3bdfSopenharmony_cistatic void check_cloexec(int i, int expected) 72f08c3bdfSopenharmony_ci{ 73f08c3bdfSopenharmony_ci int present = SAFE_FCNTL(fd[i], F_GETFD) & FD_CLOEXEC; 74f08c3bdfSopenharmony_ci 75f08c3bdfSopenharmony_ci if (expected && !present) 76f08c3bdfSopenharmony_ci tst_res(TFAIL, "fd[%d] flags do not contain FD_CLOEXEC", i); 77f08c3bdfSopenharmony_ci 78f08c3bdfSopenharmony_ci if (!expected && present) 79f08c3bdfSopenharmony_ci tst_res(TFAIL, "fd[%d] flags contain FD_CLOEXEC", i); 80f08c3bdfSopenharmony_ci} 81f08c3bdfSopenharmony_ci 82f08c3bdfSopenharmony_cistatic void check_closed(int min) 83f08c3bdfSopenharmony_ci{ 84f08c3bdfSopenharmony_ci int i; 85f08c3bdfSopenharmony_ci 86f08c3bdfSopenharmony_ci for (i = min; i < 3; i++) { 87f08c3bdfSopenharmony_ci if (fcntl(fd[i], F_GETFD) > -1) 88f08c3bdfSopenharmony_ci tst_res(TFAIL, "fd[%d] is still open", i); 89f08c3bdfSopenharmony_ci } 90f08c3bdfSopenharmony_ci} 91f08c3bdfSopenharmony_ci 92f08c3bdfSopenharmony_cistatic void child(unsigned int n) 93f08c3bdfSopenharmony_ci{ 94f08c3bdfSopenharmony_ci switch (n) { 95f08c3bdfSopenharmony_ci case 0: 96f08c3bdfSopenharmony_ci SAFE_DUP2(fd[1], fd[2]); 97f08c3bdfSopenharmony_ci do_close_range(3, ~0U, 0); 98f08c3bdfSopenharmony_ci check_closed(0); 99f08c3bdfSopenharmony_ci break; 100f08c3bdfSopenharmony_ci case 1: 101f08c3bdfSopenharmony_ci SAFE_DUP2(fd[1], fd[2]); 102f08c3bdfSopenharmony_ci do_close_range(3, ~0U, CLOSE_RANGE_UNSHARE); 103f08c3bdfSopenharmony_ci check_closed(0); 104f08c3bdfSopenharmony_ci break; 105f08c3bdfSopenharmony_ci case 2: 106f08c3bdfSopenharmony_ci do_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC); 107f08c3bdfSopenharmony_ci check_cloexec(0, 1); 108f08c3bdfSopenharmony_ci check_cloexec(1, 1); 109f08c3bdfSopenharmony_ci 110f08c3bdfSopenharmony_ci SAFE_DUP2(fd[1], fd[2]); 111f08c3bdfSopenharmony_ci check_cloexec(2, 0); 112f08c3bdfSopenharmony_ci break; 113f08c3bdfSopenharmony_ci case 3: 114f08c3bdfSopenharmony_ci do_close_range(3, ~0U, 115f08c3bdfSopenharmony_ci CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE); 116f08c3bdfSopenharmony_ci check_cloexec(0, 1); 117f08c3bdfSopenharmony_ci check_cloexec(1, 1); 118f08c3bdfSopenharmony_ci 119f08c3bdfSopenharmony_ci SAFE_DUP2(fd[1], fd[2]); 120f08c3bdfSopenharmony_ci check_cloexec(2, 0); 121f08c3bdfSopenharmony_ci break; 122f08c3bdfSopenharmony_ci } 123f08c3bdfSopenharmony_ci 124f08c3bdfSopenharmony_ci exit(0); 125f08c3bdfSopenharmony_ci} 126f08c3bdfSopenharmony_ci 127f08c3bdfSopenharmony_cistatic void run(unsigned int n) 128f08c3bdfSopenharmony_ci{ 129f08c3bdfSopenharmony_ci const struct tst_clone_args args = { 130f08c3bdfSopenharmony_ci .flags = CLONE_FILES, 131f08c3bdfSopenharmony_ci .exit_signal = SIGCHLD, 132f08c3bdfSopenharmony_ci }; 133f08c3bdfSopenharmony_ci 134f08c3bdfSopenharmony_ci switch (n) { 135f08c3bdfSopenharmony_ci case 0: 136f08c3bdfSopenharmony_ci tst_res(TINFO, "Plain close range"); 137f08c3bdfSopenharmony_ci do_close_range(3, ~0U, 0); 138f08c3bdfSopenharmony_ci break; 139f08c3bdfSopenharmony_ci case 1: 140f08c3bdfSopenharmony_ci tst_res(TINFO, "Set UNSHARE and close range"); 141f08c3bdfSopenharmony_ci do_close_range(3, ~0U, CLOSE_RANGE_UNSHARE); 142f08c3bdfSopenharmony_ci break; 143f08c3bdfSopenharmony_ci case 2: 144f08c3bdfSopenharmony_ci tst_res(TINFO, "Set CLOEXEC on range"); 145f08c3bdfSopenharmony_ci do_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC); 146f08c3bdfSopenharmony_ci break; 147f08c3bdfSopenharmony_ci case 3: 148f08c3bdfSopenharmony_ci tst_res(TINFO, "Set UNSHARE and CLOEXEC on range"); 149f08c3bdfSopenharmony_ci do_close_range(3, ~0U, 150f08c3bdfSopenharmony_ci CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE); 151f08c3bdfSopenharmony_ci break; 152f08c3bdfSopenharmony_ci } 153f08c3bdfSopenharmony_ci 154f08c3bdfSopenharmony_ci fd[0] = SAFE_OPEN("mnt/tmpfile", O_RDWR | O_CREAT, 0644); 155f08c3bdfSopenharmony_ci fd[1] = SAFE_DUP2(fd[0], 1000); 156f08c3bdfSopenharmony_ci fd[2] = 42; 157f08c3bdfSopenharmony_ci 158f08c3bdfSopenharmony_ci if (!SAFE_CLONE(&args)) 159f08c3bdfSopenharmony_ci child(n); 160f08c3bdfSopenharmony_ci 161f08c3bdfSopenharmony_ci tst_reap_children(); 162f08c3bdfSopenharmony_ci 163f08c3bdfSopenharmony_ci switch (n) { 164f08c3bdfSopenharmony_ci case 0: 165f08c3bdfSopenharmony_ci check_closed(0); 166f08c3bdfSopenharmony_ci break; 167f08c3bdfSopenharmony_ci case 1: 168f08c3bdfSopenharmony_ci check_cloexec(0, 0); 169f08c3bdfSopenharmony_ci check_cloexec(1, 0); 170f08c3bdfSopenharmony_ci check_cloexec(2, 0); 171f08c3bdfSopenharmony_ci break; 172f08c3bdfSopenharmony_ci case 2: 173f08c3bdfSopenharmony_ci check_cloexec(0, 1); 174f08c3bdfSopenharmony_ci check_cloexec(1, 1); 175f08c3bdfSopenharmony_ci check_cloexec(2, 0); 176f08c3bdfSopenharmony_ci break; 177f08c3bdfSopenharmony_ci case 3: 178f08c3bdfSopenharmony_ci check_cloexec(0, 0); 179f08c3bdfSopenharmony_ci check_cloexec(1, 0); 180f08c3bdfSopenharmony_ci check_closed(2); 181f08c3bdfSopenharmony_ci break; 182f08c3bdfSopenharmony_ci } 183f08c3bdfSopenharmony_ci 184f08c3bdfSopenharmony_ci do_close_range(3, ~0U, 0); 185f08c3bdfSopenharmony_ci check_closed(0); 186f08c3bdfSopenharmony_ci 187f08c3bdfSopenharmony_ci if (tst_taint_check()) 188f08c3bdfSopenharmony_ci tst_res(TFAIL, "Kernel tainted"); 189f08c3bdfSopenharmony_ci else 190f08c3bdfSopenharmony_ci tst_res(TPASS, "No kernel taints"); 191f08c3bdfSopenharmony_ci} 192f08c3bdfSopenharmony_ci 193f08c3bdfSopenharmony_cistatic struct tst_test test = { 194f08c3bdfSopenharmony_ci .tcnt = 4, 195f08c3bdfSopenharmony_ci .forks_child = 1, 196f08c3bdfSopenharmony_ci .mount_device = 1, 197f08c3bdfSopenharmony_ci .mntpoint = "mnt", 198f08c3bdfSopenharmony_ci .all_filesystems = 1, 199f08c3bdfSopenharmony_ci .needs_root = 1, 200f08c3bdfSopenharmony_ci .test = run, 201f08c3bdfSopenharmony_ci .caps = (struct tst_cap []) { 202f08c3bdfSopenharmony_ci TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN), 203f08c3bdfSopenharmony_ci {} 204f08c3bdfSopenharmony_ci }, 205f08c3bdfSopenharmony_ci .taint_check = TST_TAINT_W | TST_TAINT_D, 206f08c3bdfSopenharmony_ci .setup = setup, 207f08c3bdfSopenharmony_ci .tags = (const struct tst_tag[]) { 208f08c3bdfSopenharmony_ci {"linux-git", "fec8a6a69103"}, 209f08c3bdfSopenharmony_ci {}, 210f08c3bdfSopenharmony_ci }, 211f08c3bdfSopenharmony_ci}; 212