xref: /linux/tools/testing/selftests/mm/seal_elf.c (revision 96f30c8f0aa9923aa39b30bcaefeacf88b490231)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 #include <sys/mman.h>
4 #include <stdint.h>
5 #include <asm-generic/unistd.h>
6 #include <string.h>
7 #include <sys/time.h>
8 #include <sys/resource.h>
9 #include <stdbool.h>
10 #include "../kselftest.h"
11 #include <syscall.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <fcntl.h>
16 #include <sys/ioctl.h>
17 #include <sys/vfs.h>
18 #include <sys/stat.h>
19 #include "mseal_helpers.h"
20 
21 /*
22  * define sys_xyx to call syscall directly.
23  */
24 static int sys_mseal(void *start, size_t len)
25 {
26 	int sret;
27 
28 	errno = 0;
29 	sret = syscall(__NR_mseal, start, len, 0);
30 	return sret;
31 }
32 
33 static void *sys_mmap(void *addr, unsigned long len, unsigned long prot,
34 	unsigned long flags, unsigned long fd, unsigned long offset)
35 {
36 	void *sret;
37 
38 	errno = 0;
39 	sret = (void *) syscall(__NR_mmap, addr, len, prot,
40 		flags, fd, offset);
41 	return sret;
42 }
43 
44 static inline int sys_mprotect(void *ptr, size_t size, unsigned long prot)
45 {
46 	int sret;
47 
48 	errno = 0;
49 	sret = syscall(__NR_mprotect, ptr, size, prot);
50 	return sret;
51 }
52 
53 static bool seal_support(void)
54 {
55 	int ret;
56 	void *ptr;
57 	unsigned long page_size = getpagesize();
58 
59 	ptr = sys_mmap(NULL, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
60 	if (ptr == (void *) -1)
61 		return false;
62 
63 	ret = sys_mseal(ptr, page_size);
64 	if (ret < 0)
65 		return false;
66 
67 	return true;
68 }
69 
70 const char somestr[4096] = {"READONLY"};
71 
72 static void test_seal_elf(void)
73 {
74 	int ret;
75 	FILE *maps;
76 	char line[512];
77 	uintptr_t  addr_start, addr_end;
78 	char prot[5];
79 	char filename[256];
80 	unsigned long page_size = getpagesize();
81 	unsigned long long ptr = (unsigned long long) somestr;
82 	char *somestr2 = (char *)somestr;
83 
84 	/*
85 	 * Modify the protection of readonly somestr
86 	 */
87 	if (((unsigned long long)ptr % page_size) != 0)
88 		ptr = (unsigned long long)ptr & ~(page_size - 1);
89 
90 	ksft_print_msg("somestr = %s\n", somestr);
91 	ksft_print_msg("change protection to rw\n");
92 	ret = sys_mprotect((void *)ptr, page_size, PROT_READ|PROT_WRITE);
93 	FAIL_TEST_IF_FALSE(!ret);
94 	*somestr2 = 'A';
95 	ksft_print_msg("somestr is modified to: %s\n", somestr);
96 	ret = sys_mprotect((void *)ptr, page_size, PROT_READ);
97 	FAIL_TEST_IF_FALSE(!ret);
98 
99 	maps = fopen("/proc/self/maps", "r");
100 	FAIL_TEST_IF_FALSE(maps);
101 
102 	/*
103 	 * apply sealing to elf binary
104 	 */
105 	while (fgets(line, sizeof(line), maps)) {
106 		if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*u %255[^\n]",
107 			&addr_start, &addr_end, prot, filename) == 4) {
108 			if (strlen(filename)) {
109 				/*
110 				 * seal the mapping if read only.
111 				 */
112 				if (strstr(prot, "r-")) {
113 					ret = sys_mseal((void *)addr_start, addr_end - addr_start);
114 					FAIL_TEST_IF_FALSE(!ret);
115 					ksft_print_msg("sealed: %lx-%lx %s %s\n",
116 						addr_start, addr_end, prot, filename);
117 					if ((uintptr_t) somestr >= addr_start &&
118 						(uintptr_t) somestr <= addr_end)
119 						ksft_print_msg("mapping for somestr found\n");
120 				}
121 			}
122 		}
123 	}
124 	fclose(maps);
125 
126 	ret = sys_mprotect((void *)ptr, page_size, PROT_READ | PROT_WRITE);
127 	FAIL_TEST_IF_FALSE(ret < 0);
128 	ksft_print_msg("somestr is sealed, mprotect is rejected\n");
129 
130 	REPORT_TEST_PASS();
131 }
132 
133 int main(int argc, char **argv)
134 {
135 	bool test_seal = seal_support();
136 
137 	ksft_print_header();
138 	ksft_print_msg("pid=%d\n", getpid());
139 
140 	if (!test_seal)
141 		ksft_exit_skip("sealing not supported, check CONFIG_64BIT\n");
142 
143 	ksft_set_plan(1);
144 
145 	test_seal_elf();
146 
147 	ksft_finished();
148 }
149