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