android13/external/ltp/testcases/kernel/syscalls/close_range/close_range01.c

210 lines
4.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Taken from the kernel self tests, which in turn were based on
* a Syzkaller reproducer.
*
* Self test author and close_range author:
* Christian Brauner <christian.brauner@ubuntu.com>
*
* LTP Author: Richard Palethorpe <rpalethorpe@suse.com>
* Copyright (c) 2021 SUSE LLC, other copyrights may apply.
*/
/*\
* [Description]
*
* We check that close_range()
*
* - closes FDs
* - UNSHARES some FDs before closing them
* - it sets CLOEXEC (in both cloned process and parent)
* - combination of CLOEXEC and UNSHARE.
*
* The final test is the actual bug reproducer. Note that we call
* clone directly to share the file table.
*/
#include <stdlib.h>
#include "tst_test.h"
#include "tst_clone.h"
#include "lapi/clone.h"
#include "lapi/close_range.h"
static int fd[3];
static inline void do_close_range(unsigned int fd, unsigned int max_fd,
unsigned int flags)
{
int ret = close_range(fd, max_fd, flags);
if (!ret)
return;
if (errno == EINVAL) {
if (flags & CLOSE_RANGE_UNSHARE)
tst_brk(TCONF | TERRNO, "No CLOSE_RANGE_UNSHARE");
if (flags & CLOSE_RANGE_CLOEXEC)
tst_brk(TCONF | TERRNO, "No CLOSE_RANGE_CLOEXEC");
}
tst_brk(TBROK | TERRNO, "close_range(%d, %d, %d)", fd, max_fd, flags);
}
static void setup(void)
{
struct rlimit nfd;
SAFE_GETRLIMIT(RLIMIT_NOFILE, &nfd);
if (nfd.rlim_max < 1000) {
tst_brk(TCONF, "NOFILE limit max too low: %lu < 1000",
nfd.rlim_max);
}
nfd.rlim_cur = nfd.rlim_max;
SAFE_SETRLIMIT(RLIMIT_NOFILE, &nfd);
}
static void check_cloexec(int i, int expected)
{
int present = SAFE_FCNTL(fd[i], F_GETFD) & FD_CLOEXEC;
if (expected && !present)
tst_res(TFAIL, "fd[%d] flags do not contain FD_CLOEXEC", i);
if (!expected && present)
tst_res(TFAIL, "fd[%d] flags contain FD_CLOEXEC", i);
}
static void check_closed(int min)
{
int i;
for (i = min; i < 3; i++) {
if (fcntl(fd[i], F_GETFD) > -1)
tst_res(TFAIL, "fd[%d] is still open", i);
}
}
static void child(unsigned int n)
{
switch (n) {
case 0:
SAFE_DUP2(fd[1], fd[2]);
do_close_range(3, ~0U, 0);
check_closed(0);
break;
case 1:
SAFE_DUP2(fd[1], fd[2]);
do_close_range(3, ~0U, CLOSE_RANGE_UNSHARE);
check_closed(0);
break;
case 2:
do_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
check_cloexec(0, 1);
check_cloexec(1, 1);
SAFE_DUP2(fd[1], fd[2]);
check_cloexec(2, 0);
break;
case 3:
do_close_range(3, ~0U,
CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
check_cloexec(0, 1);
check_cloexec(1, 1);
SAFE_DUP2(fd[1], fd[2]);
check_cloexec(2, 0);
break;
}
exit(0);
}
static void run(unsigned int n)
{
const struct tst_clone_args args = {
.flags = CLONE_FILES,
.exit_signal = SIGCHLD,
};
switch (n) {
case 0:
tst_res(TINFO, "Plain close range");
do_close_range(3, ~0U, 0);
break;
case 1:
tst_res(TINFO, "Set UNSHARE and close range");
do_close_range(3, ~0U, CLOSE_RANGE_UNSHARE);
break;
case 2:
tst_res(TINFO, "Set CLOEXEC on range");
do_close_range(3, ~0U, CLOSE_RANGE_CLOEXEC);
break;
case 3:
tst_res(TINFO, "Set UNSHARE and CLOEXEC on range");
do_close_range(3, ~0U,
CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE);
break;
}
fd[0] = SAFE_OPEN("mnt/tmpfile", O_RDWR | O_CREAT, 0644);
fd[1] = SAFE_DUP2(fd[0], 1000);
fd[2] = 42;
if (!SAFE_CLONE(&args))
child(n);
tst_reap_children();
switch (n) {
case 0:
check_closed(0);
break;
case 1:
check_cloexec(0, 0);
check_cloexec(1, 0);
check_cloexec(2, 0);
break;
case 2:
check_cloexec(0, 1);
check_cloexec(1, 1);
check_cloexec(2, 0);
break;
case 3:
check_cloexec(0, 0);
check_cloexec(1, 0);
check_closed(2);
break;
}
do_close_range(3, ~0U, 0);
check_closed(0);
if (tst_taint_check())
tst_res(TFAIL, "Kernel tainted");
else
tst_res(TPASS, "No kernel taints");
}
static struct tst_test test = {
.tcnt = 4,
.forks_child = 1,
.mount_device = 1,
.mntpoint = "mnt",
.all_filesystems = 1,
.needs_root = 1,
.test = run,
.caps = (struct tst_cap []) {
TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
{}
},
.taint_check = TST_TAINT_W | TST_TAINT_D,
.setup = setup,
.tags = (const struct tst_tag[]) {
{"linux-git", "fec8a6a69103"},
{},
},
};