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