xref: /linux/tools/testing/selftests/mm/memfd_secret.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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>
20*a5c6bc59SJohn Hubbard #include <asm-generic/unistd.h>
21baa489faSSeongJae Park #include <errno.h>
22baa489faSSeongJae Park #include <stdio.h>
23c139ca42SDavid Hildenbrand #include <fcntl.h>
24baa489faSSeongJae Park 
25baa489faSSeongJae Park #include "../kselftest.h"
26baa489faSSeongJae Park 
27baa489faSSeongJae Park #define fail(fmt, ...) ksft_test_result_fail(fmt, ##__VA_ARGS__)
28baa489faSSeongJae Park #define pass(fmt, ...) ksft_test_result_pass(fmt, ##__VA_ARGS__)
29baa489faSSeongJae Park #define skip(fmt, ...) ksft_test_result_skip(fmt, ##__VA_ARGS__)
30baa489faSSeongJae Park 
31baa489faSSeongJae Park #define PATTERN	0x55
32baa489faSSeongJae Park 
33baa489faSSeongJae Park static const int prot = PROT_READ | PROT_WRITE;
34baa489faSSeongJae Park static const int mode = MAP_SHARED;
35baa489faSSeongJae Park 
36baa489faSSeongJae Park static unsigned long page_size;
37baa489faSSeongJae Park static unsigned long mlock_limit_cur;
38baa489faSSeongJae Park static unsigned long mlock_limit_max;
39baa489faSSeongJae Park 
memfd_secret(unsigned int flags)40baa489faSSeongJae Park static int memfd_secret(unsigned int flags)
41baa489faSSeongJae Park {
42baa489faSSeongJae Park 	return syscall(__NR_memfd_secret, flags);
43baa489faSSeongJae Park }
44baa489faSSeongJae Park 
test_file_apis(int fd)45baa489faSSeongJae Park static void test_file_apis(int fd)
46baa489faSSeongJae Park {
47baa489faSSeongJae Park 	char buf[64];
48baa489faSSeongJae Park 
49baa489faSSeongJae Park 	if ((read(fd, buf, sizeof(buf)) >= 0) ||
50baa489faSSeongJae Park 	    (write(fd, buf, sizeof(buf)) >= 0) ||
51baa489faSSeongJae Park 	    (pread(fd, buf, sizeof(buf), 0) >= 0) ||
52baa489faSSeongJae Park 	    (pwrite(fd, buf, sizeof(buf), 0) >= 0))
53baa489faSSeongJae Park 		fail("unexpected file IO\n");
54baa489faSSeongJae Park 	else
55baa489faSSeongJae Park 		pass("file IO is blocked as expected\n");
56baa489faSSeongJae Park }
57baa489faSSeongJae Park 
test_mlock_limit(int fd)58baa489faSSeongJae Park static void test_mlock_limit(int fd)
59baa489faSSeongJae Park {
60baa489faSSeongJae Park 	size_t len;
61baa489faSSeongJae Park 	char *mem;
62baa489faSSeongJae Park 
63baa489faSSeongJae Park 	len = mlock_limit_cur;
640aac13adSMuhammad Usama Anjum 	if (len % page_size != 0)
650aac13adSMuhammad Usama Anjum 		len = (len/page_size) * page_size;
660aac13adSMuhammad Usama Anjum 
67baa489faSSeongJae Park 	mem = mmap(NULL, len, prot, mode, fd, 0);
68baa489faSSeongJae Park 	if (mem == MAP_FAILED) {
69baa489faSSeongJae Park 		fail("unable to mmap secret memory\n");
70baa489faSSeongJae Park 		return;
71baa489faSSeongJae Park 	}
72baa489faSSeongJae Park 	munmap(mem, len);
73baa489faSSeongJae Park 
74baa489faSSeongJae Park 	len = mlock_limit_max * 2;
75baa489faSSeongJae Park 	mem = mmap(NULL, len, prot, mode, fd, 0);
76baa489faSSeongJae Park 	if (mem != MAP_FAILED) {
77baa489faSSeongJae Park 		fail("unexpected mlock limit violation\n");
78baa489faSSeongJae Park 		munmap(mem, len);
79baa489faSSeongJae Park 		return;
80baa489faSSeongJae Park 	}
81baa489faSSeongJae Park 
82baa489faSSeongJae Park 	pass("mlock limit is respected\n");
83baa489faSSeongJae Park }
84baa489faSSeongJae Park 
test_vmsplice(int fd,const char * desc)85c139ca42SDavid Hildenbrand static void test_vmsplice(int fd, const char *desc)
86c139ca42SDavid Hildenbrand {
87c139ca42SDavid Hildenbrand 	ssize_t transferred;
88c139ca42SDavid Hildenbrand 	struct iovec iov;
89c139ca42SDavid Hildenbrand 	int pipefd[2];
90c139ca42SDavid Hildenbrand 	char *mem;
91c139ca42SDavid Hildenbrand 
92c139ca42SDavid Hildenbrand 	if (pipe(pipefd)) {
93c139ca42SDavid Hildenbrand 		fail("pipe failed: %s\n", strerror(errno));
94c139ca42SDavid Hildenbrand 		return;
95c139ca42SDavid Hildenbrand 	}
96c139ca42SDavid Hildenbrand 
97c139ca42SDavid Hildenbrand 	mem = mmap(NULL, page_size, prot, mode, fd, 0);
98c139ca42SDavid Hildenbrand 	if (mem == MAP_FAILED) {
99c139ca42SDavid Hildenbrand 		fail("Unable to mmap secret memory\n");
100c139ca42SDavid Hildenbrand 		goto close_pipe;
101c139ca42SDavid Hildenbrand 	}
102c139ca42SDavid Hildenbrand 
103c139ca42SDavid Hildenbrand 	/*
104c139ca42SDavid Hildenbrand 	 * vmsplice() may use GUP-fast, which must also fail. Prefault the
105c139ca42SDavid Hildenbrand 	 * page table, so GUP-fast could find it.
106c139ca42SDavid Hildenbrand 	 */
107c139ca42SDavid Hildenbrand 	memset(mem, PATTERN, page_size);
108c139ca42SDavid Hildenbrand 
109c139ca42SDavid Hildenbrand 	iov.iov_base = mem;
110c139ca42SDavid Hildenbrand 	iov.iov_len = page_size;
111c139ca42SDavid Hildenbrand 	transferred = vmsplice(pipefd[1], &iov, 1, 0);
112c139ca42SDavid Hildenbrand 
113c139ca42SDavid Hildenbrand 	if (transferred < 0 && errno == EFAULT)
114c139ca42SDavid Hildenbrand 		pass("vmsplice is blocked as expected with %s\n", desc);
115c139ca42SDavid Hildenbrand 	else
116c139ca42SDavid Hildenbrand 		fail("vmsplice: unexpected memory access with %s\n", desc);
117c139ca42SDavid Hildenbrand 
118c139ca42SDavid Hildenbrand 	munmap(mem, page_size);
119c139ca42SDavid Hildenbrand close_pipe:
120c139ca42SDavid Hildenbrand 	close(pipefd[0]);
121c139ca42SDavid Hildenbrand 	close(pipefd[1]);
122c139ca42SDavid Hildenbrand }
123c139ca42SDavid Hildenbrand 
try_process_vm_read(int fd,int pipefd[2])124baa489faSSeongJae Park static void try_process_vm_read(int fd, int pipefd[2])
125baa489faSSeongJae Park {
126baa489faSSeongJae Park 	struct iovec liov, riov;
127baa489faSSeongJae Park 	char buf[64];
128baa489faSSeongJae Park 	char *mem;
129baa489faSSeongJae Park 
130baa489faSSeongJae Park 	if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
131baa489faSSeongJae Park 		fail("pipe write: %s\n", strerror(errno));
132baa489faSSeongJae Park 		exit(KSFT_FAIL);
133baa489faSSeongJae Park 	}
134baa489faSSeongJae Park 
135baa489faSSeongJae Park 	liov.iov_len = riov.iov_len = sizeof(buf);
136baa489faSSeongJae Park 	liov.iov_base = buf;
137baa489faSSeongJae Park 	riov.iov_base = mem;
138baa489faSSeongJae Park 
139baa489faSSeongJae Park 	if (process_vm_readv(getppid(), &liov, 1, &riov, 1, 0) < 0) {
140baa489faSSeongJae Park 		if (errno == ENOSYS)
141baa489faSSeongJae Park 			exit(KSFT_SKIP);
142baa489faSSeongJae Park 		exit(KSFT_PASS);
143baa489faSSeongJae Park 	}
144baa489faSSeongJae Park 
145baa489faSSeongJae Park 	exit(KSFT_FAIL);
146baa489faSSeongJae Park }
147baa489faSSeongJae Park 
try_ptrace(int fd,int pipefd[2])148baa489faSSeongJae Park static void try_ptrace(int fd, int pipefd[2])
149baa489faSSeongJae Park {
150baa489faSSeongJae Park 	pid_t ppid = getppid();
151baa489faSSeongJae Park 	int status;
152baa489faSSeongJae Park 	char *mem;
153baa489faSSeongJae Park 	long ret;
154baa489faSSeongJae Park 
155baa489faSSeongJae Park 	if (read(pipefd[0], &mem, sizeof(mem)) < 0) {
156baa489faSSeongJae Park 		perror("pipe write");
157baa489faSSeongJae Park 		exit(KSFT_FAIL);
158baa489faSSeongJae Park 	}
159baa489faSSeongJae Park 
160baa489faSSeongJae Park 	ret = ptrace(PTRACE_ATTACH, ppid, 0, 0);
161baa489faSSeongJae Park 	if (ret) {
162baa489faSSeongJae Park 		perror("ptrace_attach");
163baa489faSSeongJae Park 		exit(KSFT_FAIL);
164baa489faSSeongJae Park 	}
165baa489faSSeongJae Park 
166baa489faSSeongJae Park 	ret = waitpid(ppid, &status, WUNTRACED);
167baa489faSSeongJae Park 	if ((ret != ppid) || !(WIFSTOPPED(status))) {
168baa489faSSeongJae Park 		fprintf(stderr, "weird waitppid result %ld stat %x\n",
169baa489faSSeongJae Park 			ret, status);
170baa489faSSeongJae Park 		exit(KSFT_FAIL);
171baa489faSSeongJae Park 	}
172baa489faSSeongJae Park 
173baa489faSSeongJae Park 	if (ptrace(PTRACE_PEEKDATA, ppid, mem, 0))
174baa489faSSeongJae Park 		exit(KSFT_PASS);
175baa489faSSeongJae Park 
176baa489faSSeongJae Park 	exit(KSFT_FAIL);
177baa489faSSeongJae Park }
178baa489faSSeongJae Park 
check_child_status(pid_t pid,const char * name)179baa489faSSeongJae Park static void check_child_status(pid_t pid, const char *name)
180baa489faSSeongJae Park {
181baa489faSSeongJae Park 	int status;
182baa489faSSeongJae Park 
183baa489faSSeongJae Park 	waitpid(pid, &status, 0);
184baa489faSSeongJae Park 
185baa489faSSeongJae Park 	if (WIFEXITED(status) && WEXITSTATUS(status) == KSFT_SKIP) {
186baa489faSSeongJae Park 		skip("%s is not supported\n", name);
187baa489faSSeongJae Park 		return;
188baa489faSSeongJae Park 	}
189baa489faSSeongJae Park 
190baa489faSSeongJae Park 	if ((WIFEXITED(status) && WEXITSTATUS(status) == KSFT_PASS) ||
191baa489faSSeongJae Park 	    WIFSIGNALED(status)) {
192baa489faSSeongJae Park 		pass("%s is blocked as expected\n", name);
193baa489faSSeongJae Park 		return;
194baa489faSSeongJae Park 	}
195baa489faSSeongJae Park 
196baa489faSSeongJae Park 	fail("%s: unexpected memory access\n", name);
197baa489faSSeongJae Park }
198baa489faSSeongJae Park 
test_remote_access(int fd,const char * name,void (* func)(int fd,int pipefd[2]))199baa489faSSeongJae Park static void test_remote_access(int fd, const char *name,
200baa489faSSeongJae Park 			       void (*func)(int fd, int pipefd[2]))
201baa489faSSeongJae Park {
202baa489faSSeongJae Park 	int pipefd[2];
203baa489faSSeongJae Park 	pid_t pid;
204baa489faSSeongJae Park 	char *mem;
205baa489faSSeongJae Park 
206baa489faSSeongJae Park 	if (pipe(pipefd)) {
207baa489faSSeongJae Park 		fail("pipe failed: %s\n", strerror(errno));
208baa489faSSeongJae Park 		return;
209baa489faSSeongJae Park 	}
210baa489faSSeongJae Park 
211baa489faSSeongJae Park 	pid = fork();
212baa489faSSeongJae Park 	if (pid < 0) {
213baa489faSSeongJae Park 		fail("fork failed: %s\n", strerror(errno));
214baa489faSSeongJae Park 		return;
215baa489faSSeongJae Park 	}
216baa489faSSeongJae Park 
217baa489faSSeongJae Park 	if (pid == 0) {
218baa489faSSeongJae Park 		func(fd, pipefd);
219baa489faSSeongJae Park 		return;
220baa489faSSeongJae Park 	}
221baa489faSSeongJae Park 
222baa489faSSeongJae Park 	mem = mmap(NULL, page_size, prot, mode, fd, 0);
223baa489faSSeongJae Park 	if (mem == MAP_FAILED) {
224baa489faSSeongJae Park 		fail("Unable to mmap secret memory\n");
225baa489faSSeongJae Park 		return;
226baa489faSSeongJae Park 	}
227baa489faSSeongJae Park 
228baa489faSSeongJae Park 	memset(mem, PATTERN, page_size);
229baa489faSSeongJae Park 
230baa489faSSeongJae Park 	if (write(pipefd[1], &mem, sizeof(mem)) < 0) {
231baa489faSSeongJae Park 		fail("pipe write: %s\n", strerror(errno));
232baa489faSSeongJae Park 		return;
233baa489faSSeongJae Park 	}
234baa489faSSeongJae Park 
235baa489faSSeongJae Park 	check_child_status(pid, name);
236baa489faSSeongJae Park }
237baa489faSSeongJae Park 
test_process_vm_read(int fd)238baa489faSSeongJae Park static void test_process_vm_read(int fd)
239baa489faSSeongJae Park {
240baa489faSSeongJae Park 	test_remote_access(fd, "process_vm_read", try_process_vm_read);
241baa489faSSeongJae Park }
242baa489faSSeongJae Park 
test_ptrace(int fd)243baa489faSSeongJae Park static void test_ptrace(int fd)
244baa489faSSeongJae Park {
245baa489faSSeongJae Park 	test_remote_access(fd, "ptrace", try_ptrace);
246baa489faSSeongJae Park }
247baa489faSSeongJae Park 
set_cap_limits(rlim_t max)248baa489faSSeongJae Park static int set_cap_limits(rlim_t max)
249baa489faSSeongJae Park {
250baa489faSSeongJae Park 	struct rlimit new;
251baa489faSSeongJae Park 	cap_t cap = cap_init();
252baa489faSSeongJae Park 
253baa489faSSeongJae Park 	new.rlim_cur = max;
254baa489faSSeongJae Park 	new.rlim_max = max;
255baa489faSSeongJae Park 	if (setrlimit(RLIMIT_MEMLOCK, &new)) {
256baa489faSSeongJae Park 		perror("setrlimit() returns error");
257baa489faSSeongJae Park 		return -1;
258baa489faSSeongJae Park 	}
259baa489faSSeongJae Park 
260baa489faSSeongJae Park 	/* drop capabilities including CAP_IPC_LOCK */
261baa489faSSeongJae Park 	if (cap_set_proc(cap)) {
262baa489faSSeongJae Park 		perror("cap_set_proc() returns error");
263baa489faSSeongJae Park 		return -2;
264baa489faSSeongJae Park 	}
265baa489faSSeongJae Park 
266baa489faSSeongJae Park 	return 0;
267baa489faSSeongJae Park }
268baa489faSSeongJae Park 
prepare(void)269baa489faSSeongJae Park static void prepare(void)
270baa489faSSeongJae Park {
271baa489faSSeongJae Park 	struct rlimit rlim;
272baa489faSSeongJae Park 
273baa489faSSeongJae Park 	page_size = sysconf(_SC_PAGE_SIZE);
274baa489faSSeongJae Park 	if (!page_size)
275baa489faSSeongJae Park 		ksft_exit_fail_msg("Failed to get page size %s\n",
276baa489faSSeongJae Park 				   strerror(errno));
277baa489faSSeongJae Park 
278baa489faSSeongJae Park 	if (getrlimit(RLIMIT_MEMLOCK, &rlim))
279baa489faSSeongJae Park 		ksft_exit_fail_msg("Unable to detect mlock limit: %s\n",
280baa489faSSeongJae Park 				   strerror(errno));
281baa489faSSeongJae Park 
282baa489faSSeongJae Park 	mlock_limit_cur = rlim.rlim_cur;
283baa489faSSeongJae Park 	mlock_limit_max = rlim.rlim_max;
284baa489faSSeongJae Park 
285baa489faSSeongJae Park 	printf("page_size: %ld, mlock.soft: %ld, mlock.hard: %ld\n",
286baa489faSSeongJae Park 	       page_size, mlock_limit_cur, mlock_limit_max);
287baa489faSSeongJae Park 
288baa489faSSeongJae Park 	if (page_size > mlock_limit_cur)
289baa489faSSeongJae Park 		mlock_limit_cur = page_size;
290baa489faSSeongJae Park 	if (page_size > mlock_limit_max)
291baa489faSSeongJae Park 		mlock_limit_max = page_size;
292baa489faSSeongJae Park 
293baa489faSSeongJae Park 	if (set_cap_limits(mlock_limit_max))
294baa489faSSeongJae Park 		ksft_exit_fail_msg("Unable to set mlock limit: %s\n",
295baa489faSSeongJae Park 				   strerror(errno));
296baa489faSSeongJae Park }
297baa489faSSeongJae Park 
298c139ca42SDavid Hildenbrand #define NUM_TESTS 6
299baa489faSSeongJae Park 
main(int argc,char * argv[])300baa489faSSeongJae Park int main(int argc, char *argv[])
301baa489faSSeongJae Park {
302baa489faSSeongJae Park 	int fd;
303baa489faSSeongJae Park 
304baa489faSSeongJae Park 	prepare();
305baa489faSSeongJae Park 
306baa489faSSeongJae Park 	ksft_print_header();
307baa489faSSeongJae Park 	ksft_set_plan(NUM_TESTS);
308baa489faSSeongJae Park 
309baa489faSSeongJae Park 	fd = memfd_secret(0);
310baa489faSSeongJae Park 	if (fd < 0) {
311baa489faSSeongJae Park 		if (errno == ENOSYS)
312baa489faSSeongJae Park 			ksft_exit_skip("memfd_secret is not supported\n");
313baa489faSSeongJae Park 		else
314baa489faSSeongJae Park 			ksft_exit_fail_msg("memfd_secret failed: %s\n",
315baa489faSSeongJae Park 					   strerror(errno));
316baa489faSSeongJae Park 	}
317c139ca42SDavid Hildenbrand 	if (ftruncate(fd, page_size))
318c139ca42SDavid Hildenbrand 		ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno));
319baa489faSSeongJae Park 
320baa489faSSeongJae Park 	test_mlock_limit(fd);
321baa489faSSeongJae Park 	test_file_apis(fd);
322c139ca42SDavid Hildenbrand 	/*
323c139ca42SDavid Hildenbrand 	 * We have to run the first vmsplice test before any secretmem page was
324c139ca42SDavid Hildenbrand 	 * allocated for this fd.
325c139ca42SDavid Hildenbrand 	 */
326c139ca42SDavid Hildenbrand 	test_vmsplice(fd, "fresh page");
327c139ca42SDavid Hildenbrand 	test_vmsplice(fd, "existing page");
328baa489faSSeongJae Park 	test_process_vm_read(fd);
329baa489faSSeongJae Park 	test_ptrace(fd);
330baa489faSSeongJae Park 
331baa489faSSeongJae Park 	close(fd);
332baa489faSSeongJae Park 
333baa489faSSeongJae Park 	ksft_finished();
334baa489faSSeongJae Park }
335