summaryrefslogtreecommitdiff
path: root/rts/linker/SymbolExtras.c
diff options
context:
space:
mode:
authorBen Gamari <ben@smart-cactus.org>2019-10-28 00:34:09 -0400
committerBen Gamari <ben@smart-cactus.org>2019-11-02 11:00:36 -0400
commita3134cf9bf27acebb99da17ef0edc3f94b77afbb (patch)
treec5fcacd63ae556fabb15d723187fad3170b37220 /rts/linker/SymbolExtras.c
parent9980fb58f613ee3363c7e4cb86453e542c6c69aa (diff)
downloadhaskell-wip/linker-noexec.tar.gz
rts/linker: Ensure that code isn't writablewip/linker-noexec
For many years the linker would simply map all of its memory with PROT_READ|PROT_WRITE|PROT_EXEC. However operating systems have been becoming increasingly reluctant to accept this practice (e.g. #17353 and #12657) and for good reason: writable code is ripe for exploitation. 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 contain executable code. Moreover, to make all of this possible it was necessary to redesign the m32 allocator. First, we gave (in an earlier commit) each ObjectCode its own m32_allocator. This was necessary since code loading and symbol resolution/relocation are currently interleaved, meaning that it is not possible to enforce W^X when symbols from different objects reside in the same page. We then redesigned the m32 allocator to take advantage of the fact that all of the pages allocated with the allocator die at the same time (namely, when the owning ObjectCode is unloaded). This makes a number of things simpler (e.g. no more page reference counting; the interface provided by the allocator for freeing is simpler). See Note [M32 Allocator] for details.
Diffstat (limited to 'rts/linker/SymbolExtras.c')
-rw-r--r--rts/linker/SymbolExtras.c31
1 files changed, 30 insertions, 1 deletions
diff --git a/rts/linker/SymbolExtras.c b/rts/linker/SymbolExtras.c
index 04a678898f..b7b558cb41 100644
--- a/rts/linker/SymbolExtras.c
+++ b/rts/linker/SymbolExtras.c
@@ -57,6 +57,8 @@ int ocAllocateExtras(ObjectCode* oc, int count, int first, int bssSize)
if (count > 0 || bssSize > 0) {
if (!RTS_LINKER_USE_MMAP) {
+ /* N.B. We currently can't mark symbol extras as non-executable in this
+ * case. */
// round up to the nearest 4
int aligned = (oc->fileSize + 3) & ~3;
@@ -72,6 +74,8 @@ int ocAllocateExtras(ObjectCode* oc, int count, int first, int bssSize)
oc->symbol_extras = (SymbolExtra *) (oc->image + aligned);
} else if (USE_CONTIGUOUS_MMAP || RtsFlags.MiscFlags.linkerAlwaysPic) {
/* Keep image, bssExtras and symbol_extras contiguous */
+ /* N.B. We currently can't mark symbol extras as non-executable in this
+ * case. */
size_t n = roundUpToPage(oc->fileSize);
bssSize = roundUpToAlign(bssSize, 8);
size_t allocated_size = n + bssSize + extras_size;
@@ -93,7 +97,9 @@ int ocAllocateExtras(ObjectCode* oc, int count, int first, int bssSize)
return 0;
}
} else {
- oc->symbol_extras = m32_alloc(oc->m32, extras_size, 8);
+ /* m32_allocator_flush ensures that these are marked as executable when
+ * we finish building them. */
+ oc->symbol_extras = m32_alloc(oc->rx_m32, extras_size, 8);
if (oc->symbol_extras == NULL) return 0;
}
}
@@ -120,6 +126,29 @@ int ocAllocateExtras(ObjectCode* oc, int count, int first, int bssSize)
return 1;
}
+/**
+ * Mark the symbol extras as non-writable.
+ */
+void ocProtectExtras(ObjectCode* oc)
+{
+ if (oc->n_symbol_extras == 0) return;
+
+ if (!RTS_LINKER_USE_MMAP) {
+ /*
+ * In this case the symbol extras were allocated via malloc. All we can do
+ * in this case is hope that the platform doesn't mark such allocations as
+ * non-executable.
+ */
+ } else if (USE_CONTIGUOUS_MMAP || RtsFlags.MiscFlags.linkerAlwaysPic) {
+ mmapForLinkerMarkExecutable(oc->symbol_extras, sizeof(SymbolExtra) * oc->n_symbol_extras);
+ } else {
+ /*
+ * The symbol extras were allocated via m32. They will be protected when
+ * the m32 allocator is finalized
+ */
+ }
+}
+
#if !defined(arm_HOST_ARCH)
SymbolExtra* makeSymbolExtra( ObjectCode const* oc,