xref: /linux/tools/testing/selftests/mm/seal_elf.c (revision 3ea5eb68b9d624935108b5e696859304edfac202)
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 inline int sys_mprotect(void *ptr, size_t size, unsigned long prot)
34 {
35 	int sret;
36 
37 	errno = 0;
38 	sret = syscall(__NR_mprotect, ptr, size, prot);
39 	return sret;
40 }
41 
42 static bool seal_support(void)
43 {
44 	int ret;
45 	void *ptr;
46 	unsigned long page_size = getpagesize();
47 
48 	ptr = mmap(NULL, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
49 	if (ptr == (void *) -1)
50 		return false;
51 
52 	ret = sys_mseal(ptr, page_size);
53 	if (ret < 0)
54 		return false;
55 
56 	return true;
57 }
58 
59 const char somestr[4096] = {"READONLY"};
60 
61 static void test_seal_elf(void)
62 {
63 	int ret;
64 	FILE *maps;
65 	char line[512];
66 	uintptr_t  addr_start, addr_end;
67 	char prot[5];
68 	char filename[256];
69 	unsigned long page_size = getpagesize();
70 	unsigned long long ptr = (unsigned long long) somestr;
71 	char *somestr2 = (char *)somestr;
72 
73 	/*
74 	 * Modify the protection of readonly somestr
75 	 */
76 	if (((unsigned long long)ptr % page_size) != 0)
77 		ptr = (unsigned long long)ptr & ~(page_size - 1);
78 
79 	ksft_print_msg("somestr = %s\n", somestr);
80 	ksft_print_msg("change protection to rw\n");
81 	ret = sys_mprotect((void *)ptr, page_size, PROT_READ|PROT_WRITE);
82 	FAIL_TEST_IF_FALSE(!ret);
83 	*somestr2 = 'A';
84 	ksft_print_msg("somestr is modified to: %s\n", somestr);
85 	ret = sys_mprotect((void *)ptr, page_size, PROT_READ);
86 	FAIL_TEST_IF_FALSE(!ret);
87 
88 	maps = fopen("/proc/self/maps", "r");
89 	FAIL_TEST_IF_FALSE(maps);
90 
91 	/*
92 	 * apply sealing to elf binary
93 	 */
94 	while (fgets(line, sizeof(line), maps)) {
95 		if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*u %255[^\n]",
96 			&addr_start, &addr_end, prot, filename) == 4) {
97 			if (strlen(filename)) {
98 				/*
99 				 * seal the mapping if read only.
100 				 */
101 				if (strstr(prot, "r-")) {
102 					ret = sys_mseal((void *)addr_start, addr_end - addr_start);
103 					FAIL_TEST_IF_FALSE(!ret);
104 					ksft_print_msg("sealed: %lx-%lx %s %s\n",
105 						addr_start, addr_end, prot, filename);
106 					if ((uintptr_t) somestr >= addr_start &&
107 						(uintptr_t) somestr <= addr_end)
108 						ksft_print_msg("mapping for somestr found\n");
109 				}
110 			}
111 		}
112 	}
113 	fclose(maps);
114 
115 	ret = sys_mprotect((void *)ptr, page_size, PROT_READ | PROT_WRITE);
116 	FAIL_TEST_IF_FALSE(ret < 0);
117 	ksft_print_msg("somestr is sealed, mprotect is rejected\n");
118 
119 	REPORT_TEST_PASS();
120 }
121 
122 int main(int argc, char **argv)
123 {
124 	bool test_seal = seal_support();
125 
126 	ksft_print_header();
127 	ksft_print_msg("pid=%d\n", getpid());
128 
129 	if (!test_seal)
130 		ksft_exit_skip("sealing not supported, check CONFIG_64BIT\n");
131 
132 	ksft_set_plan(1);
133 
134 	test_seal_elf();
135 
136 	ksft_finished();
137 }
138