mgorny created this revision. mgorny added reviewers: ro, delcypher, thakis, saugustine, rnk, compnerd. Herald added subscribers: llvm-commits, kristof.beyls, krytarowski, dberris. Herald added a project: LLVM.
Fix clear_cache_test to work on NetBSD with PaX MPROTECT enabled, that is when creating W+X mmaps is prohibited. Use the recommended solution: create two mappings for the same memory area, make one of them RW, while the other RX. Copy the function into the RW area but run it from the RX area. In order to implement this, I've split the pointer variables to 'write_buffer' and 'execution_buffer'. Both are separate pointers on NetBSD, while they have the same value on other systems. I've also split the memcpy_f() into two: new memcpy_f() that only takes care of copying memory and discards the (known) result of memcpy(), and realign_f() that applies ARM realignment to the given pointer. Again, there should be no difference on non-NetBSD systems but on NetBSD copying is done on write_buffer, while realignment on pointer to the execution_buffer. I have tested this change on NetBSD and Linux. https://reviews.llvm.org/D72578 Files: compiler-rt/test/builtins/Unit/clear_cache_test.c Index: compiler-rt/test/builtins/Unit/clear_cache_test.c =================================================================== --- compiler-rt/test/builtins/Unit/clear_cache_test.c +++ compiler-rt/test/builtins/Unit/clear_cache_test.c @@ -1,6 +1,6 @@ // REQUIRES: native-run // UNSUPPORTED: arm, aarch64 -// RUN: %clang_builtins %s %librt -o %t && %run_nomprotect %t +// RUN: %clang_builtins %s %librt -o %t && %run %t // REQUIRES: librt_has_clear_cache //===-- clear_cache_test.c - Test clear_cache -----------------------------===// // @@ -30,28 +30,56 @@ static int func1() { return 1; } static int func2() { return 2; } -void *__attribute__((noinline)) +void __attribute__((noinline)) memcpy_f(void *dst, const void *src, size_t n) { // ARM and MIPS nartually align functions, but use the LSB for ISA selection // (THUMB, MIPS16/uMIPS respectively). Ensure that the ISA bit is ignored in // the memcpy #if defined(__arm__) || defined(__mips__) - return (void *)((uintptr_t)memcpy(dst, (void *)((uintptr_t)src & ~1), n) | - ((uintptr_t)src & 1)); + memcpy(dst, (void *)((uintptr_t)src & ~1), n); +#else + memcpy(dst, (void *)((uintptr_t)src), n); +#endif +} + +// Realign the 'dst' pointer as if it has been returned by memcpy() above. +// We need to split it because we're using two mappings for the same area. +void *__attribute__((noinline)) +realign_f(void *dst, const void *src, size_t n) { +#if defined(__arm__) || defined(__mips__) + return (void *)((uintptr_t)dst | ((uintptr_t)src & 1)); #else - return memcpy(dst, (void *)((uintptr_t)src), n); + return dst; #endif } int main() { const int kSize = 128; -#if !defined(_WIN32) +#if defined(__NetBSD__) + // we need to create separate RW and RX mappings to satisfy MPROTECT + uint8_t *write_buffer = mmap(0, kSize, + PROT_MPROTECT(PROT_READ | PROT_WRITE | + PROT_EXEC), + MAP_ANON | MAP_PRIVATE, -1, 0); + if (write_buffer == MAP_FAILED) + return 1; + uint8_t *execution_buffer = mremap(write_buffer, kSize, NULL, kSize, + MAP_REMAPDUP); + if (execution_buffer == MAP_FAILED) + return 1; + + if (mprotect(write_buffer, kSize, PROT_READ | PROT_WRITE) == -1) + return 1; + if (mprotect(execution_buffer, kSize, PROT_READ | PROT_EXEC) == -1) + return 1; +#elif !defined(_WIN32) uint8_t *execution_buffer = mmap(0, kSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); if (execution_buffer == MAP_FAILED) return 1; + uint8_t *write_buffer = execution_buffer; #else HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, kSize, NULL); @@ -62,16 +90,19 @@ mapping, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0); if (execution_buffer == NULL) return 1; + uint8_t *write_buffer = execution_buffer; #endif // verify you can copy and execute a function - pfunc f1 = (pfunc)memcpy_f(execution_buffer, func1, kSize); + memcpy_f(write_buffer, func1, kSize); + pfunc f1 = (pfunc)realign_f(execution_buffer, func1, kSize); __clear_cache(execution_buffer, execution_buffer + kSize); if ((*f1)() != 1) return 1; // verify you can overwrite a function with another - pfunc f2 = (pfunc)memcpy_f(execution_buffer, func2, kSize); + memcpy_f(write_buffer, func2, kSize); + pfunc f2 = (pfunc)realign_f(execution_buffer, func2, kSize); __clear_cache(execution_buffer, execution_buffer + kSize); if ((*f2)() != 2) return 1;
Index: compiler-rt/test/builtins/Unit/clear_cache_test.c =================================================================== --- compiler-rt/test/builtins/Unit/clear_cache_test.c +++ compiler-rt/test/builtins/Unit/clear_cache_test.c @@ -1,6 +1,6 @@ // REQUIRES: native-run // UNSUPPORTED: arm, aarch64 -// RUN: %clang_builtins %s %librt -o %t && %run_nomprotect %t +// RUN: %clang_builtins %s %librt -o %t && %run %t // REQUIRES: librt_has_clear_cache //===-- clear_cache_test.c - Test clear_cache -----------------------------===// // @@ -30,28 +30,56 @@ static int func1() { return 1; } static int func2() { return 2; } -void *__attribute__((noinline)) +void __attribute__((noinline)) memcpy_f(void *dst, const void *src, size_t n) { // ARM and MIPS nartually align functions, but use the LSB for ISA selection // (THUMB, MIPS16/uMIPS respectively). Ensure that the ISA bit is ignored in // the memcpy #if defined(__arm__) || defined(__mips__) - return (void *)((uintptr_t)memcpy(dst, (void *)((uintptr_t)src & ~1), n) | - ((uintptr_t)src & 1)); + memcpy(dst, (void *)((uintptr_t)src & ~1), n); +#else + memcpy(dst, (void *)((uintptr_t)src), n); +#endif +} + +// Realign the 'dst' pointer as if it has been returned by memcpy() above. +// We need to split it because we're using two mappings for the same area. +void *__attribute__((noinline)) +realign_f(void *dst, const void *src, size_t n) { +#if defined(__arm__) || defined(__mips__) + return (void *)((uintptr_t)dst | ((uintptr_t)src & 1)); #else - return memcpy(dst, (void *)((uintptr_t)src), n); + return dst; #endif } int main() { const int kSize = 128; -#if !defined(_WIN32) +#if defined(__NetBSD__) + // we need to create separate RW and RX mappings to satisfy MPROTECT + uint8_t *write_buffer = mmap(0, kSize, + PROT_MPROTECT(PROT_READ | PROT_WRITE | + PROT_EXEC), + MAP_ANON | MAP_PRIVATE, -1, 0); + if (write_buffer == MAP_FAILED) + return 1; + uint8_t *execution_buffer = mremap(write_buffer, kSize, NULL, kSize, + MAP_REMAPDUP); + if (execution_buffer == MAP_FAILED) + return 1; + + if (mprotect(write_buffer, kSize, PROT_READ | PROT_WRITE) == -1) + return 1; + if (mprotect(execution_buffer, kSize, PROT_READ | PROT_EXEC) == -1) + return 1; +#elif !defined(_WIN32) uint8_t *execution_buffer = mmap(0, kSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); if (execution_buffer == MAP_FAILED) return 1; + uint8_t *write_buffer = execution_buffer; #else HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, kSize, NULL); @@ -62,16 +90,19 @@ mapping, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0); if (execution_buffer == NULL) return 1; + uint8_t *write_buffer = execution_buffer; #endif // verify you can copy and execute a function - pfunc f1 = (pfunc)memcpy_f(execution_buffer, func1, kSize); + memcpy_f(write_buffer, func1, kSize); + pfunc f1 = (pfunc)realign_f(execution_buffer, func1, kSize); __clear_cache(execution_buffer, execution_buffer + kSize); if ((*f1)() != 1) return 1; // verify you can overwrite a function with another - pfunc f2 = (pfunc)memcpy_f(execution_buffer, func2, kSize); + memcpy_f(write_buffer, func2, kSize); + pfunc f2 = (pfunc)realign_f(execution_buffer, func2, kSize); __clear_cache(execution_buffer, execution_buffer + kSize); if ((*f2)() != 2) return 1;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits