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