xref: /openbmc/linux/tools/testing/selftests/mm/memfd_secret.c (revision 3386ee827c6279ac922dd7c24be86019363b6ce1)
1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2baa489faSSeongJae Park /*
3baa489faSSeongJae Park  * Copyright IBM Corporation, 2021
4baa489faSSeongJae Park  *
5baa489faSSeongJae Park  * Author: Mike Rapoport <rppt@linux.ibm.com>
6baa489faSSeongJae Park  */
7baa489faSSeongJae Park 
8baa489faSSeongJae Park #define _GNU_SOURCE
9baa489faSSeongJae Park #include <sys/uio.h>
10baa489faSSeongJae Park #include <sys/mman.h>
11baa489faSSeongJae Park #include <sys/wait.h>
12baa489faSSeongJae Park #include <sys/types.h>
13baa489faSSeongJae Park #include <sys/ptrace.h>
14baa489faSSeongJae Park #include <sys/syscall.h>
15baa489faSSeongJae Park #include <sys/resource.h>
16baa489faSSeongJae Park #include <sys/capability.h>
17baa489faSSeongJae Park 
18baa489faSSeongJae Park #include <stdlib.h>
19baa489faSSeongJae Park #include <string.h>
20baa489faSSeongJae Park #include <unistd.h>
21baa489faSSeongJae Park #include <errno.h>
22baa489faSSeongJae Park #include <stdio.h>
23baa489faSSeongJae Park 
24baa489faSSeongJae Park #include "../kselftest.h"
25baa489faSSeongJae Park 
26baa489faSSeongJae Park #define fail(fmt, ...) ksft_test_result_fail(fmt, ##__VA_ARGS__)
27baa489faSSeongJae Park #define pass(fmt, ...) ksft_test_result_pass(fmt, ##__VA_ARGS__)
28baa489faSSeongJae Park #define skip(fmt, ...) ksft_test_result_skip(fmt, ##__VA_ARGS__)
29baa489faSSeongJae Park 
30baa489faSSeongJae Park #ifdef __NR_memfd_secret
31baa489faSSeongJae Park 
32baa489faSSeongJae Park #define PATTERN	0x55
33baa489faSSeongJae Park 
34baa489faSSeongJae Park static const int prot = PROT_READ | PROT_WRITE;
35baa489faSSeongJae Park static const int mode = MAP_SHARED;
36baa489faSSeongJae Park 
37baa489faSSeongJae Park static unsigned long page_size;
38baa489faSSeongJae Park static unsigned long mlock_limit_cur;
39baa489faSSeongJae Park static unsigned long mlock_limit_max;
40baa489faSSeongJae Park 
memfd_secret(unsigned int flags)41baa489faSSeongJae Park static int memfd_secret(unsigned int flags)
42baa489faSSeongJae Park {
43baa489faSSeongJae Park 	return syscall(__NR_memfd_secret, flags);
44baa489faSSeongJae Park }
45baa489faSSeongJae Park 
test_file_apis(int fd)46baa489faSSeongJae Park static void test_file_apis(int fd)
47baa489faSSeongJae Park {
48baa489faSSeongJae Park 	char buf[64];
49baa489faSSeongJae Park 
50baa489faSSeongJae Park 	if ((read(fd, buf, sizeof(buf)) >= 0) ||
51baa489faSSeongJae Park 	    (write(fd, buf, sizeof(buf)) >= 0) ||
52baa489faSSeongJae Park 	    (pread(fd, buf, sizeof(buf), 0) >= 0) ||
53baa489faSSeongJae Park 	    (pwrite(fd, buf, sizeof(buf), 0) >= 0))
54baa489faSSeongJae Park 		fail("unexpected file IO\n");
55baa489faSSeongJae Park 	else
56baa489faSSeongJae Park 		pass("file IO is blocked as expected\n");
57baa489faSSeongJae Park }
58baa489faSSeongJae Park 
test_mlock_limit(int fd)59baa489faSSeongJae Park static void test_mlock_limit(int fd)
60baa489faSSeongJae Park {
61baa489faSSeongJae Park 	size_t len;
62baa489faSSeongJae Park 	char *mem;
63baa489faSSeongJae Park 
64baa489faSSeongJae Park 	len = mlock_limit_cur;
65*09141f08SMuhammad Usama Anjum 	if (len % page_size != 0)
66*09141f08SMuhammad Usama Anjum 		len = (len/page_size) * page_size;
67*09141f08SMuhammad Usama Anjum 
68baa489faSSeongJae Park 	mem = mmap(NULL, len, prot, mode, fd, 0);
69baa489faSSeongJae Park 	if (mem == MAP_FAILED) {
70baa489faSSeongJae Park 		fail("unable to mmap secret memory\n");
71baa489faSSeongJae Park 		return;
72baa489faSSeongJae Park 	}
73baa489faSSeongJae Park 	munmap(mem, len);
74baa489faSSeongJae Park 
75baa489faSSeongJae Park 	len = mlock_limit_max * 2;
76baa489faSSeongJae Park 	mem = mmap(NULL, len, prot, mode, fd, 0);
77baa489faSSeongJae Park 	if (mem != MAP_FAILED) {
78baa489faSSeongJae Park 		fail("unexpected mlock limit violation\n");
79baa489faSSeongJae Park 		munmap(mem, len);
80baa489faSSeongJae Park 		return;
81baa489faSSeongJae Park 	}
82baa489faSSeongJae Park 
83baa489faSSeongJae Park 	pass("mlock limit is respected\n");
84baa489faSSeongJae Park }
85baa489faSSeongJae Park 
try_process_vm_read(int fd,int pipefd[2])86baa489faSSeongJae Park static void try_process_vm_read(int fd, int pipefd[2])
87baa489faSSeongJae Park {
88baa489faSSeongJae Park 	struct iovec liov, riov;
89baa489faSSeongJae Park 	char buf[64];
90baa489faSSeongJae Park 	char *mem;
91baa489faSSeongJae Park 
92baa489faSSeongJae Park 	if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
93baa489faSSeongJae Park 		fail("pipe write: %s\n", strerror(errno));
94baa489faSSeongJae Park 		exit(KSFT_FAIL);
95baa489faSSeongJae Park 	}
96baa489faSSeongJae Park 
97baa489faSSeongJae Park 	liov.iov_len = riov.iov_len = sizeof(buf);
98baa489faSSeongJae Park 	liov.iov_base = buf;
99baa489faSSeongJae Park 	riov.iov_base = mem;
100baa489faSSeongJae Park 
101baa489faSSeongJae Park 	if (process_vm_readv(getppid(), &liov, 1, &riov, 1, 0) < 0) {
102baa489faSSeongJae Park 		if (errno == ENOSYS)
103baa489faSSeongJae Park 			exit(KSFT_SKIP);
104baa489faSSeongJae Park 		exit(KSFT_PASS);
105baa489faSSeongJae Park 	}
106baa489faSSeongJae Park 
107baa489faSSeongJae Park 	exit(KSFT_FAIL);
108baa489faSSeongJae Park }
109baa489faSSeongJae Park 
try_ptrace(int fd,int pipefd[2])110baa489faSSeongJae Park static void try_ptrace(int fd, int pipefd[2])
111baa489faSSeongJae Park {
112baa489faSSeongJae Park 	pid_t ppid = getppid();
113baa489faSSeongJae Park 	int status;
114baa489faSSeongJae Park 	char *mem;
115baa489faSSeongJae Park 	long ret;
116baa489faSSeongJae Park 
117baa489faSSeongJae Park 	if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
118baa489faSSeongJae Park 		perror("pipe write");
119baa489faSSeongJae Park 		exit(KSFT_FAIL);
120baa489faSSeongJae Park 	}
121baa489faSSeongJae Park 
122baa489faSSeongJae Park 	ret = ptrace(PTRACE_ATTACH, ppid, 0, 0);
123baa489faSSeongJae Park 	if (ret) {
124baa489faSSeongJae Park 		perror("ptrace_attach");
125baa489faSSeongJae Park 		exit(KSFT_FAIL);
126baa489faSSeongJae Park 	}
127baa489faSSeongJae Park 
128baa489faSSeongJae Park 	ret = waitpid(ppid, &status, WUNTRACED);
129baa489faSSeongJae Park 	if ((ret != ppid) || !(WIFSTOPPED(status))) {
130baa489faSSeongJae Park 		fprintf(stderr, "weird waitppid result %ld stat %x\n",
131baa489faSSeongJae Park 			ret, status);
132baa489faSSeongJae Park 		exit(KSFT_FAIL);
133baa489faSSeongJae Park 	}
134baa489faSSeongJae Park 
135baa489faSSeongJae Park 	if (ptrace(PTRACE_PEEKDATA, ppid, mem, 0))
136baa489faSSeongJae Park 		exit(KSFT_PASS);
137baa489faSSeongJae Park 
138baa489faSSeongJae Park 	exit(KSFT_FAIL);
139baa489faSSeongJae Park }
140baa489faSSeongJae Park 
check_child_status(pid_t pid,const char * name)141baa489faSSeongJae Park static void check_child_status(pid_t pid, const char *name)
142baa489faSSeongJae Park {
143baa489faSSeongJae Park 	int status;
144baa489faSSeongJae Park 
145baa489faSSeongJae Park 	waitpid(pid, &status, 0);
146baa489faSSeongJae Park 
147baa489faSSeongJae Park 	if (WIFEXITED(status) && WEXITSTATUS(status) == KSFT_SKIP) {
148baa489faSSeongJae Park 		skip("%s is not supported\n", name);
149baa489faSSeongJae Park 		return;
150baa489faSSeongJae Park 	}
151baa489faSSeongJae Park 
152baa489faSSeongJae Park 	if ((WIFEXITED(status) && WEXITSTATUS(status) == KSFT_PASS) ||
153baa489faSSeongJae Park 	    WIFSIGNALED(status)) {
154baa489faSSeongJae Park 		pass("%s is blocked as expected\n", name);
155baa489faSSeongJae Park 		return;
156baa489faSSeongJae Park 	}
157baa489faSSeongJae Park 
158baa489faSSeongJae Park 	fail("%s: unexpected memory access\n", name);
159baa489faSSeongJae Park }
160baa489faSSeongJae Park 
test_remote_access(int fd,const char * name,void (* func)(int fd,int pipefd[2]))161baa489faSSeongJae Park static void test_remote_access(int fd, const char *name,
162baa489faSSeongJae Park 			       void (*func)(int fd, int pipefd[2]))
163baa489faSSeongJae Park {
164baa489faSSeongJae Park 	int pipefd[2];
165baa489faSSeongJae Park 	pid_t pid;
166baa489faSSeongJae Park 	char *mem;
167baa489faSSeongJae Park 
168baa489faSSeongJae Park 	if (pipe(pipefd)) {
169baa489faSSeongJae Park 		fail("pipe failed: %s\n", strerror(errno));
170baa489faSSeongJae Park 		return;
171baa489faSSeongJae Park 	}
172baa489faSSeongJae Park 
173baa489faSSeongJae Park 	pid = fork();
174baa489faSSeongJae Park 	if (pid < 0) {
175baa489faSSeongJae Park 		fail("fork failed: %s\n", strerror(errno));
176baa489faSSeongJae Park 		return;
177baa489faSSeongJae Park 	}
178baa489faSSeongJae Park 
179baa489faSSeongJae Park 	if (pid == 0) {
180baa489faSSeongJae Park 		func(fd, pipefd);
181baa489faSSeongJae Park 		return;
182baa489faSSeongJae Park 	}
183baa489faSSeongJae Park 
184baa489faSSeongJae Park 	mem = mmap(NULL, page_size, prot, mode, fd, 0);
185baa489faSSeongJae Park 	if (mem == MAP_FAILED) {
186baa489faSSeongJae Park 		fail("Unable to mmap secret memory\n");
187baa489faSSeongJae Park 		return;
188baa489faSSeongJae Park 	}
189baa489faSSeongJae Park 
190baa489faSSeongJae Park 	ftruncate(fd, page_size);
191baa489faSSeongJae Park 	memset(mem, PATTERN, page_size);
192baa489faSSeongJae Park 
193baa489faSSeongJae Park 	if (write(pipefd[1], &mem, sizeof(mem)) < 0) {
194baa489faSSeongJae Park 		fail("pipe write: %s\n", strerror(errno));
195baa489faSSeongJae Park 		return;
196baa489faSSeongJae Park 	}
197baa489faSSeongJae Park 
198baa489faSSeongJae Park 	check_child_status(pid, name);
199baa489faSSeongJae Park }
200baa489faSSeongJae Park 
test_process_vm_read(int fd)201baa489faSSeongJae Park static void test_process_vm_read(int fd)
202baa489faSSeongJae Park {
203baa489faSSeongJae Park 	test_remote_access(fd, "process_vm_read", try_process_vm_read);
204baa489faSSeongJae Park }
205baa489faSSeongJae Park 
test_ptrace(int fd)206baa489faSSeongJae Park static void test_ptrace(int fd)
207baa489faSSeongJae Park {
208baa489faSSeongJae Park 	test_remote_access(fd, "ptrace", try_ptrace);
209baa489faSSeongJae Park }
210baa489faSSeongJae Park 
set_cap_limits(rlim_t max)211baa489faSSeongJae Park static int set_cap_limits(rlim_t max)
212baa489faSSeongJae Park {
213baa489faSSeongJae Park 	struct rlimit new;
214baa489faSSeongJae Park 	cap_t cap = cap_init();
215baa489faSSeongJae Park 
216baa489faSSeongJae Park 	new.rlim_cur = max;
217baa489faSSeongJae Park 	new.rlim_max = max;
218baa489faSSeongJae Park 	if (setrlimit(RLIMIT_MEMLOCK, &new)) {
219baa489faSSeongJae Park 		perror("setrlimit() returns error");
220baa489faSSeongJae Park 		return -1;
221baa489faSSeongJae Park 	}
222baa489faSSeongJae Park 
223baa489faSSeongJae Park 	/* drop capabilities including CAP_IPC_LOCK */
224baa489faSSeongJae Park 	if (cap_set_proc(cap)) {
225baa489faSSeongJae Park 		perror("cap_set_proc() returns error");
226baa489faSSeongJae Park 		return -2;
227baa489faSSeongJae Park 	}
228baa489faSSeongJae Park 
229baa489faSSeongJae Park 	return 0;
230baa489faSSeongJae Park }
231baa489faSSeongJae Park 
prepare(void)232baa489faSSeongJae Park static void prepare(void)
233baa489faSSeongJae Park {
234baa489faSSeongJae Park 	struct rlimit rlim;
235baa489faSSeongJae Park 
236baa489faSSeongJae Park 	page_size = sysconf(_SC_PAGE_SIZE);
237baa489faSSeongJae Park 	if (!page_size)
238baa489faSSeongJae Park 		ksft_exit_fail_msg("Failed to get page size %s\n",
239baa489faSSeongJae Park 				   strerror(errno));
240baa489faSSeongJae Park 
241baa489faSSeongJae Park 	if (getrlimit(RLIMIT_MEMLOCK, &rlim))
242baa489faSSeongJae Park 		ksft_exit_fail_msg("Unable to detect mlock limit: %s\n",
243baa489faSSeongJae Park 				   strerror(errno));
244baa489faSSeongJae Park 
245baa489faSSeongJae Park 	mlock_limit_cur = rlim.rlim_cur;
246baa489faSSeongJae Park 	mlock_limit_max = rlim.rlim_max;
247baa489faSSeongJae Park 
248baa489faSSeongJae Park 	printf("page_size: %ld, mlock.soft: %ld, mlock.hard: %ld\n",
249baa489faSSeongJae Park 	       page_size, mlock_limit_cur, mlock_limit_max);
250baa489faSSeongJae Park 
251baa489faSSeongJae Park 	if (page_size > mlock_limit_cur)
252baa489faSSeongJae Park 		mlock_limit_cur = page_size;
253baa489faSSeongJae Park 	if (page_size > mlock_limit_max)
254baa489faSSeongJae Park 		mlock_limit_max = page_size;
255baa489faSSeongJae Park 
256baa489faSSeongJae Park 	if (set_cap_limits(mlock_limit_max))
257baa489faSSeongJae Park 		ksft_exit_fail_msg("Unable to set mlock limit: %s\n",
258baa489faSSeongJae Park 				   strerror(errno));
259baa489faSSeongJae Park }
260baa489faSSeongJae Park 
261baa489faSSeongJae Park #define NUM_TESTS 4
262baa489faSSeongJae Park 
main(int argc,char * argv[])263baa489faSSeongJae Park int main(int argc, char *argv[])
264baa489faSSeongJae Park {
265baa489faSSeongJae Park 	int fd;
266baa489faSSeongJae Park 
267baa489faSSeongJae Park 	prepare();
268baa489faSSeongJae Park 
269baa489faSSeongJae Park 	ksft_print_header();
270baa489faSSeongJae Park 	ksft_set_plan(NUM_TESTS);
271baa489faSSeongJae Park 
272baa489faSSeongJae Park 	fd = memfd_secret(0);
273baa489faSSeongJae Park 	if (fd < 0) {
274baa489faSSeongJae Park 		if (errno == ENOSYS)
275baa489faSSeongJae Park 			ksft_exit_skip("memfd_secret is not supported\n");
276baa489faSSeongJae Park 		else
277baa489faSSeongJae Park 			ksft_exit_fail_msg("memfd_secret failed: %s\n",
278baa489faSSeongJae Park 					   strerror(errno));
279baa489faSSeongJae Park 	}
280baa489faSSeongJae Park 
281baa489faSSeongJae Park 	test_mlock_limit(fd);
282baa489faSSeongJae Park 	test_file_apis(fd);
283baa489faSSeongJae Park 	test_process_vm_read(fd);
284baa489faSSeongJae Park 	test_ptrace(fd);
285baa489faSSeongJae Park 
286baa489faSSeongJae Park 	close(fd);
287baa489faSSeongJae Park 
288baa489faSSeongJae Park 	ksft_finished();
289baa489faSSeongJae Park }
290baa489faSSeongJae Park 
291baa489faSSeongJae Park #else /* __NR_memfd_secret */
292baa489faSSeongJae Park 
main(int argc,char * argv[])293baa489faSSeongJae Park int main(int argc, char *argv[])
294baa489faSSeongJae Park {
295baa489faSSeongJae Park 	printf("skip: skipping memfd_secret test (missing __NR_memfd_secret)\n");
296baa489faSSeongJae Park 	return KSFT_SKIP;
297baa489faSSeongJae Park }
298baa489faSSeongJae Park 
299baa489faSSeongJae Park #endif /* __NR_memfd_secret */
300