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