From 86589b893c092ae900723e76848525f20f6cafbf Mon Sep 17 00:00:00 2001 From: GHC GitLab CI Date: Fri, 28 Jan 2022 22:33:52 -0500 Subject: rts: Generalize mmapForLinkerMarkExecutable Renamed to mprotectForLinker and allowed setting of arbitrary protection modes. --- rts/ExecPage.c | 2 +- rts/Linker.c | 56 ++++++++++++++++++++++++++++++++++++++--------- rts/LinkerInternals.h | 10 ++++++++- rts/linker/Elf.c | 2 +- rts/linker/M32Alloc.c | 2 +- rts/linker/MachO.c | 4 ++-- rts/linker/SymbolExtras.c | 2 +- 7 files changed, 61 insertions(+), 17 deletions(-) diff --git a/rts/ExecPage.c b/rts/ExecPage.c index 6f5b6e281a..24d4d65bad 100644 --- a/rts/ExecPage.c +++ b/rts/ExecPage.c @@ -15,7 +15,7 @@ ExecPage *allocateExecPage() { } void freezeExecPage(ExecPage *page) { - mmapForLinkerMarkExecutable(page, getPageSize()); + mprotectForLinker(page, getPageSize(), MEM_READ_EXECUTE); flushExec(getPageSize(), page); } diff --git a/rts/Linker.c b/rts/Linker.c index c79f184f23..6468e7818d 100644 --- a/rts/Linker.c +++ b/rts/Linker.c @@ -1008,6 +1008,17 @@ resolveSymbolAddr (pathchar* buffer, int size, #endif /* OBJFORMAT_PEi386 */ } +static const char *memoryAccessDescription(MemoryAccess mode) +{ + switch (mode) { + case MEM_NO_ACCESS: return "no-access"; + case MEM_READ_ONLY: return "read-only"; + case MEM_READ_WRITE: return "read-write"; + case MEM_READ_EXECUTE: return "read-execute"; + default: barf("invalid MemoryAccess"); + } +} + #if defined(mingw32_HOST_OS) // @@ -1028,16 +1039,29 @@ munmapForLinker (void *addr, size_t bytes, const char *caller) } } +/** + * Change the allowed access modes of a region of memory previously allocated + * with mmapAnonForLinker. + */ void -mmapForLinkerMarkExecutable(void *start, size_t len) +mprotectForLinker(void *start, size_t len, MemoryAccess mode) { DWORD old; if (len == 0) { return; } - if (VirtualProtect(start, len, PAGE_EXECUTE_READ, &old) == 0) { - sysErrorBelch("mmapForLinkerMarkExecutable: failed to protect %zd bytes at %p", - len, start); + DWORD prot; + switch (mode) { + case MEM_NO_ACCESS: prot = PAGE_NOACCESS; break; + case MEM_READ_ONLY: prot = PAGE_READONLY; break; + case MEM_READ_WRITE: prot = PAGE_READWRITE; break; + case MEM_READ_EXECUTE: prot = PAGE_EXECUTE_READ; break; + default: barf("invalid MemoryAccess"); + } + + if (VirtualProtect(start, len, prot, &old) == 0) { + sysErrorBelch("mprotectForLinker: failed to protect %zd bytes at %p as %s", + len, start, memoryAccessDescription(mode)); ASSERT(false); } } @@ -1189,7 +1213,7 @@ void munmapForLinker (void *addr, size_t bytes, const char *caller) * * Consequently mmapForLinker now maps its memory with PROT_READ|PROT_WRITE. * After the linker has finished filling/relocating the mapping it must then - * call mmapForLinkerMarkExecutable on the sections of the mapping which + * call mprotectForLinker on the sections of the mapping which * contain executable code. * * Note that the m32 allocator handles protection of its allocations. For this @@ -1205,16 +1229,28 @@ void munmapForLinker (void *addr, size_t bytes, const char *caller) * Mark an portion of a mapping previously reserved by mmapForLinker * as executable (but not writable). */ -void mmapForLinkerMarkExecutable(void *start, size_t len) +void mprotectForLinker(void *start, size_t len, MemoryAccess mode) { if (len == 0) { return; } IF_DEBUG(linker_verbose, - debugBelch("mmapForLinkerMarkExecutable: protecting %" FMT_Word - " bytes starting at %p\n", (W_)len, start)); - if (mprotect(start, len, PROT_READ|PROT_EXEC) == -1) { - barf("mmapForLinkerMarkExecutable: mprotect: %s\n", strerror(errno)); + debugBelch("mprotectForLinker: protecting %" FMT_Word + " bytes starting at %p as %s\n", + (W_)len, start, memoryAccessDescription(mode))); + + int prot; + switch (mode) { + case MEM_NO_ACCESS: prot = 0; break; + case MEM_READ_ONLY: prot = PROT_READ; break; + case MEM_READ_WRITE: prot = PROT_READ | PROT_WRITE; break; + case MEM_READ_EXECUTE: prot = PROT_READ | PROT_EXEC; break; + default: barf("invalid MemoryAccess"); + } + + if (mprotect(start, len, prot) == -1) { + sysErrorBelch("mprotectForLinker: failed to protect %zd bytes at %p as %s", + len, start, memoryAccessDescription(mode)); } } #endif diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h index d2836310cb..af72ad587a 100644 --- a/rts/LinkerInternals.h +++ b/rts/LinkerInternals.h @@ -375,9 +375,17 @@ void exitLinker( void ); void freeObjectCode (ObjectCode *oc); SymbolAddr* loadSymbol(SymbolName *lbl, RtsSymbolInfo *pinfo); +/** Access modes for mprotectForLinker */ +typedef enum { + MEM_NO_ACCESS, + MEM_READ_ONLY, + MEM_READ_WRITE, + MEM_READ_EXECUTE, +} MemoryAccess; + void *mmapAnonForLinker (size_t bytes); void *mmapForLinker (size_t bytes, uint32_t prot, uint32_t flags, int fd, int offset); -void mmapForLinkerMarkExecutable (void *start, size_t len); +void mprotectForLinker(void *start, size_t len, MemoryAccess mode); void munmapForLinker (void *addr, size_t bytes, const char *caller); void addProddableBlock ( ObjectCode* oc, void* start, int size ); diff --git a/rts/linker/Elf.c b/rts/linker/Elf.c index 76145dbbf4..10ac5d2896 100644 --- a/rts/linker/Elf.c +++ b/rts/linker/Elf.c @@ -1821,7 +1821,7 @@ ocMprotect_Elf( ObjectCode *oc ) if (section->alloc != SECTION_M32) { // N.B. m32 handles protection of its allocations during // flushing. - mmapForLinkerMarkExecutable(section->mapped_start, section->mapped_size); + mprotectForLinker(section->mapped_start, section->mapped_size, MEM_READ_EXECUTE); } break; default: diff --git a/rts/linker/M32Alloc.c b/rts/linker/M32Alloc.c index 6d42ea3599..c4496a7f7d 100644 --- a/rts/linker/M32Alloc.c +++ b/rts/linker/M32Alloc.c @@ -362,7 +362,7 @@ m32_allocator_flush(m32_allocator *alloc) { while (page != NULL) { struct m32_page_t *next = m32_filled_page_get_next(page); m32_allocator_push_filled_list(&alloc->protected_list, page); - mmapForLinkerMarkExecutable(page, page->filled_page.size); + mprotectForLinker(page, page->filled_page.size, MEM_READ_EXECUTE); page = next; } alloc->unprotected_list = NULL; diff --git a/rts/linker/MachO.c b/rts/linker/MachO.c index 02f2a30605..79d14a2d55 100644 --- a/rts/linker/MachO.c +++ b/rts/linker/MachO.c @@ -1436,7 +1436,7 @@ ocMprotect_MachO( ObjectCode *oc ) if(segment->size == 0) continue; if(segment->prot == SEGMENT_PROT_RX) { - mmapForLinkerMarkExecutable(segment->start, segment->size); + mprotectForLinker(segment->start, segment->size, MEM_READ_EXECUTE); } } @@ -1451,7 +1451,7 @@ ocMprotect_MachO( ObjectCode *oc ) if(section->alloc == SECTION_M32) continue; switch (section->kind) { case SECTIONKIND_CODE_OR_RODATA: { - mmapForLinkerMarkExecutable(section->mapped_start, section->mapped_size); + mprotectForLinker(section->mapped_start, section->mapped_size, MEM_READ_EXECUTE); break; } default: diff --git a/rts/linker/SymbolExtras.c b/rts/linker/SymbolExtras.c index ddb58e4a4e..5c04e9b3a8 100644 --- a/rts/linker/SymbolExtras.c +++ b/rts/linker/SymbolExtras.c @@ -142,7 +142,7 @@ void ocProtectExtras(ObjectCode* oc) * non-executable. */ } else if (USE_CONTIGUOUS_MMAP || RtsFlags.MiscFlags.linkerAlwaysPic) { - mmapForLinkerMarkExecutable(oc->symbol_extras, sizeof(SymbolExtra) * oc->n_symbol_extras); + mprotectForLinker(oc->symbol_extras, sizeof(SymbolExtra) * oc->n_symbol_extras, MEM_READ_EXECUTE); } else { /* * The symbol extras were allocated via m32. They will be protected when -- cgit v1.2.1