1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <sys/mman.h> 4 #include <stdint.h> 5 #include <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 20 /* 21 * need those definition for manually build using gcc. 22 * gcc -I ../../../../usr/include -DDEBUG -O3 -DDEBUG -O3 seal_elf.c -o seal_elf 23 */ 24 #define FAIL_TEST_IF_FALSE(c) do {\ 25 if (!(c)) {\ 26 ksft_test_result_fail("%s, line:%d\n", __func__, __LINE__);\ 27 goto test_end;\ 28 } \ 29 } \ 30 while (0) 31 32 #define SKIP_TEST_IF_FALSE(c) do {\ 33 if (!(c)) {\ 34 ksft_test_result_skip("%s, line:%d\n", __func__, __LINE__);\ 35 goto test_end;\ 36 } \ 37 } \ 38 while (0) 39 40 41 #define TEST_END_CHECK() {\ 42 ksft_test_result_pass("%s\n", __func__);\ 43 return;\ 44 test_end:\ 45 return;\ 46 } 47 48 #ifndef u64 49 #define u64 unsigned long long 50 #endif 51 52 /* 53 * define sys_xyx to call syscall directly. 54 */ 55 static int sys_mseal(void *start, size_t len) 56 { 57 int sret; 58 59 errno = 0; 60 sret = syscall(__NR_mseal, start, len, 0); 61 return sret; 62 } 63 64 static void *sys_mmap(void *addr, unsigned long len, unsigned long prot, 65 unsigned long flags, unsigned long fd, unsigned long offset) 66 { 67 void *sret; 68 69 errno = 0; 70 sret = (void *) syscall(__NR_mmap, addr, len, prot, 71 flags, fd, offset); 72 return sret; 73 } 74 75 static inline int sys_mprotect(void *ptr, size_t size, unsigned long prot) 76 { 77 int sret; 78 79 errno = 0; 80 sret = syscall(__NR_mprotect, ptr, size, prot); 81 return sret; 82 } 83 84 static bool seal_support(void) 85 { 86 int ret; 87 void *ptr; 88 unsigned long page_size = getpagesize(); 89 90 ptr = sys_mmap(NULL, page_size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 91 if (ptr == (void *) -1) 92 return false; 93 94 ret = sys_mseal(ptr, page_size); 95 if (ret < 0) 96 return false; 97 98 return true; 99 } 100 101 const char somestr[4096] = {"READONLY"}; 102 103 static void test_seal_elf(void) 104 { 105 int ret; 106 FILE *maps; 107 char line[512]; 108 uintptr_t addr_start, addr_end; 109 char prot[5]; 110 char filename[256]; 111 unsigned long page_size = getpagesize(); 112 unsigned long long ptr = (unsigned long long) somestr; 113 char *somestr2 = (char *)somestr; 114 115 /* 116 * Modify the protection of readonly somestr 117 */ 118 if (((unsigned long long)ptr % page_size) != 0) 119 ptr = (unsigned long long)ptr & ~(page_size - 1); 120 121 ksft_print_msg("somestr = %s\n", somestr); 122 ksft_print_msg("change protection to rw\n"); 123 ret = sys_mprotect((void *)ptr, page_size, PROT_READ|PROT_WRITE); 124 FAIL_TEST_IF_FALSE(!ret); 125 *somestr2 = 'A'; 126 ksft_print_msg("somestr is modified to: %s\n", somestr); 127 ret = sys_mprotect((void *)ptr, page_size, PROT_READ); 128 FAIL_TEST_IF_FALSE(!ret); 129 130 maps = fopen("/proc/self/maps", "r"); 131 FAIL_TEST_IF_FALSE(maps); 132 133 /* 134 * apply sealing to elf binary 135 */ 136 while (fgets(line, sizeof(line), maps)) { 137 if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*u %255[^\n]", 138 &addr_start, &addr_end, prot, filename) == 4) { 139 if (strlen(filename)) { 140 /* 141 * seal the mapping if read only. 142 */ 143 if (strstr(prot, "r-")) { 144 ret = sys_mseal((void *)addr_start, addr_end - addr_start); 145 FAIL_TEST_IF_FALSE(!ret); 146 ksft_print_msg("sealed: %lx-%lx %s %s\n", 147 addr_start, addr_end, prot, filename); 148 if ((uintptr_t) somestr >= addr_start && 149 (uintptr_t) somestr <= addr_end) 150 ksft_print_msg("mapping for somestr found\n"); 151 } 152 } 153 } 154 } 155 fclose(maps); 156 157 ret = sys_mprotect((void *)ptr, page_size, PROT_READ | PROT_WRITE); 158 FAIL_TEST_IF_FALSE(ret < 0); 159 ksft_print_msg("somestr is sealed, mprotect is rejected\n"); 160 161 TEST_END_CHECK(); 162 } 163 164 int main(int argc, char **argv) 165 { 166 bool test_seal = seal_support(); 167 168 ksft_print_header(); 169 ksft_print_msg("pid=%d\n", getpid()); 170 171 if (!test_seal) 172 ksft_exit_skip("sealing not supported, check CONFIG_64BIT\n"); 173 174 ksft_set_plan(1); 175 176 test_seal_elf(); 177 178 ksft_finished(); 179 } 180