1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2024 Ampere Computing LLC 3 4 #define _GNU_SOURCE 5 6 #include <errno.h> 7 #include <fcntl.h> 8 #include <signal.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <ucontext.h> 13 #include <sys/mman.h> 14 #include <sys/stat.h> 15 #include <sys/types.h> 16 #include <sys/wait.h> 17 18 #include "kselftest.h" 19 #include "mte_common_util.h" 20 #include "mte_def.h" 21 22 #define TAG_CHECK_ON 0 23 #define TAG_CHECK_OFF 1 24 25 static unsigned long default_huge_page_size(void) 26 { 27 unsigned long hps = 0; 28 char *line = NULL; 29 size_t linelen = 0; 30 FILE *f = fopen("/proc/meminfo", "r"); 31 32 if (!f) 33 return 0; 34 while (getline(&line, &linelen, f) > 0) { 35 if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { 36 hps <<= 10; 37 break; 38 } 39 } 40 41 free(line); 42 fclose(f); 43 return hps; 44 } 45 46 static bool is_hugetlb_allocated(void) 47 { 48 unsigned long hps = 0; 49 char *line = NULL; 50 size_t linelen = 0; 51 FILE *f = fopen("/proc/meminfo", "r"); 52 53 if (!f) 54 return false; 55 while (getline(&line, &linelen, f) > 0) { 56 if (sscanf(line, "Hugetlb: %lu kB", &hps) == 1) { 57 hps <<= 10; 58 break; 59 } 60 } 61 62 free(line); 63 fclose(f); 64 65 if (hps > 0) 66 return true; 67 68 return false; 69 } 70 71 static void write_sysfs(char *str, unsigned long val) 72 { 73 FILE *f; 74 75 f = fopen(str, "w"); 76 if (!f) { 77 ksft_print_msg("ERR: missing %s\n", str); 78 return; 79 } 80 fprintf(f, "%lu", val); 81 fclose(f); 82 } 83 84 static void allocate_hugetlb() 85 { 86 write_sysfs("/proc/sys/vm/nr_hugepages", 2); 87 } 88 89 static void free_hugetlb() 90 { 91 write_sysfs("/proc/sys/vm/nr_hugepages", 0); 92 } 93 94 static int check_child_tag_inheritance(char *ptr, int size, int mode) 95 { 96 int i, parent_tag, child_tag, fault, child_status; 97 pid_t child; 98 99 parent_tag = MT_FETCH_TAG((uintptr_t)ptr); 100 fault = 0; 101 102 child = fork(); 103 if (child == -1) { 104 ksft_print_msg("FAIL: child process creation\n"); 105 return KSFT_FAIL; 106 } else if (child == 0) { 107 mte_initialize_current_context(mode, (uintptr_t)ptr, size); 108 /* Do copy on write */ 109 memset(ptr, '1', size); 110 mte_wait_after_trig(); 111 if (cur_mte_cxt.fault_valid == true) { 112 fault = 1; 113 goto check_child_tag_inheritance_err; 114 } 115 for (i = 0; i < size; i += MT_GRANULE_SIZE) { 116 child_tag = MT_FETCH_TAG((uintptr_t)(mte_get_tag_address(ptr + i))); 117 if (parent_tag != child_tag) { 118 ksft_print_msg("FAIL: child mte tag (%d) mismatch\n", i); 119 fault = 1; 120 goto check_child_tag_inheritance_err; 121 } 122 } 123 check_child_tag_inheritance_err: 124 _exit(fault); 125 } 126 /* Wait for child process to terminate */ 127 wait(&child_status); 128 if (WIFEXITED(child_status)) 129 fault = WEXITSTATUS(child_status); 130 else 131 fault = 1; 132 return (fault) ? KSFT_FAIL : KSFT_PASS; 133 } 134 135 static int check_mte_memory(char *ptr, int size, int mode, int tag_check) 136 { 137 mte_initialize_current_context(mode, (uintptr_t)ptr, size); 138 memset(ptr, '1', size); 139 mte_wait_after_trig(); 140 if (cur_mte_cxt.fault_valid == true) 141 return KSFT_FAIL; 142 143 return KSFT_PASS; 144 } 145 146 static int check_hugetlb_memory_mapping(int mem_type, int mode, int mapping, int tag_check) 147 { 148 char *ptr, *map_ptr; 149 int result; 150 unsigned long map_size; 151 152 map_size = default_huge_page_size(); 153 154 mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); 155 map_ptr = (char *)mte_allocate_memory(map_size, mem_type, mapping, false); 156 if (check_allocated_memory(map_ptr, map_size, mem_type, false) != KSFT_PASS) 157 return KSFT_FAIL; 158 159 mte_initialize_current_context(mode, (uintptr_t)map_ptr, map_size); 160 /* Only mte enabled memory will allow tag insertion */ 161 ptr = mte_insert_tags((void *)map_ptr, map_size); 162 if (!ptr || cur_mte_cxt.fault_valid == true) { 163 ksft_print_msg("FAIL: Insert tags on anonymous mmap memory\n"); 164 munmap((void *)map_ptr, map_size); 165 return KSFT_FAIL; 166 } 167 result = check_mte_memory(ptr, map_size, mode, tag_check); 168 mte_clear_tags((void *)ptr, map_size); 169 mte_free_memory((void *)map_ptr, map_size, mem_type, false); 170 if (result == KSFT_FAIL) 171 return KSFT_FAIL; 172 173 return KSFT_PASS; 174 } 175 176 static int check_clear_prot_mte_flag(int mem_type, int mode, int mapping) 177 { 178 char *map_ptr; 179 int prot_flag, result; 180 unsigned long map_size; 181 182 prot_flag = PROT_READ | PROT_WRITE; 183 mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); 184 map_size = default_huge_page_size(); 185 map_ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping, 186 0, 0); 187 if (check_allocated_memory_range(map_ptr, map_size, mem_type, 188 0, 0) != KSFT_PASS) 189 return KSFT_FAIL; 190 /* Try to clear PROT_MTE property and verify it by tag checking */ 191 if (mprotect(map_ptr, map_size, prot_flag)) { 192 mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type, 193 0, 0); 194 ksft_print_msg("FAIL: mprotect not ignoring clear PROT_MTE property\n"); 195 return KSFT_FAIL; 196 } 197 result = check_mte_memory(map_ptr, map_size, mode, TAG_CHECK_ON); 198 mte_free_memory_tag_range((void *)map_ptr, map_size, mem_type, 0, 0); 199 if (result != KSFT_PASS) 200 return KSFT_FAIL; 201 202 return KSFT_PASS; 203 } 204 205 static int check_child_hugetlb_memory_mapping(int mem_type, int mode, int mapping) 206 { 207 char *ptr; 208 int result; 209 unsigned long map_size; 210 211 map_size = default_huge_page_size(); 212 213 mte_switch_mode(mode, MTE_ALLOW_NON_ZERO_TAG); 214 ptr = (char *)mte_allocate_memory_tag_range(map_size, mem_type, mapping, 215 0, 0); 216 if (check_allocated_memory_range(ptr, map_size, mem_type, 217 0, 0) != KSFT_PASS) 218 return KSFT_FAIL; 219 result = check_child_tag_inheritance(ptr, map_size, mode); 220 mte_free_memory_tag_range((void *)ptr, map_size, mem_type, 0, 0); 221 if (result == KSFT_FAIL) 222 return result; 223 224 return KSFT_PASS; 225 } 226 227 int main(int argc, char *argv[]) 228 { 229 int err; 230 231 err = mte_default_setup(); 232 if (err) 233 return err; 234 235 /* Register signal handlers */ 236 mte_register_signal(SIGBUS, mte_default_handler); 237 mte_register_signal(SIGSEGV, mte_default_handler); 238 239 allocate_hugetlb(); 240 241 if (!is_hugetlb_allocated()) { 242 ksft_print_msg("ERR: Unable allocate hugetlb pages\n"); 243 return KSFT_FAIL; 244 } 245 246 /* Set test plan */ 247 ksft_set_plan(12); 248 249 mte_enable_pstate_tco(); 250 251 evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF), 252 "Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check off\n"); 253 254 mte_disable_pstate_tco(); 255 evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_NONE_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_OFF), 256 "Check hugetlb memory with private mapping, no error mode, mmap memory and tag check off\n"); 257 258 evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), 259 "Check hugetlb memory with private mapping, sync error mode, mmap memory and tag check on\n"); 260 evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), 261 "Check hugetlb memory with private mapping, sync error mode, mmap/mprotect memory and tag check on\n"); 262 evaluate_test(check_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), 263 "Check hugetlb memory with private mapping, async error mode, mmap memory and tag check on\n"); 264 evaluate_test(check_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB, TAG_CHECK_ON), 265 "Check hugetlb memory with private mapping, async error mode, mmap/mprotect memory and tag check on\n"); 266 267 evaluate_test(check_clear_prot_mte_flag(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), 268 "Check clear PROT_MTE flags with private mapping, sync error mode and mmap memory\n"); 269 evaluate_test(check_clear_prot_mte_flag(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), 270 "Check clear PROT_MTE flags with private mapping and sync error mode and mmap/mprotect memory\n"); 271 272 evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), 273 "Check child hugetlb memory with private mapping, precise mode and mmap memory\n"); 274 evaluate_test(check_child_hugetlb_memory_mapping(USE_MMAP, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), 275 "Check child hugetlb memory with private mapping, precise mode and mmap memory\n"); 276 evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_SYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), 277 "Check child hugetlb memory with private mapping, precise mode and mmap/mprotect memory\n"); 278 evaluate_test(check_child_hugetlb_memory_mapping(USE_MPROTECT, MTE_ASYNC_ERR, MAP_PRIVATE | MAP_HUGETLB), 279 "Check child hugetlb memory with private mapping, precise mode and mmap/mprotect memory\n"); 280 281 mte_restore_setup(); 282 free_hugetlb(); 283 ksft_print_cnts(); 284 return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL; 285 } 286