1
1
mirror of https://github.com/rui314/mold.git synced 2024-10-04 16:48:04 +03:00

[ELF] Protect the last page of a RELRO segment

PT_GNU_RELRO works on page granularity. We always align the begining
of a RELRO segment to a page boundary, but the end was not. Since the
runtime conservatively align _down_ it to a page boundary, the last
page weren't be marked as read-only.

This patch makes the size of a RELRO always a multiple of the page size.
This commit is contained in:
Rui Ueyama 2022-01-24 09:16:28 +09:00
parent b2ef045c03
commit 0a0f9b3ad5
2 changed files with 30 additions and 6 deletions

View File

@ -255,6 +255,12 @@ std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
i++;
while (i < ctx.chunks.size() && is_relro(ctx, ctx.chunks[i]))
append(ctx.chunks[i++]);
// RELRO works on page granularity, so align it up to the next
// page boundary.
assert(i == ctx.chunks.size() ||
ctx.chunks[i]->shdr.sh_addr % ctx.page_size == 0);
vec.back().p_memsz = align_to(vec.back().p_memsz, ctx.page_size);
}
}

View File

@ -11,11 +11,32 @@ t=out/test/elf/$testname
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
extern const char readonly[100];
extern char readwrite[100];
static int segv = 0;
static jmp_buf buf;
void handler(int sig) {
segv = 1;
longjmp(buf, 1);
}
int main() {
return readonly[0] + readwrite[0];
signal(SIGSEGV, handler);
readwrite[0] = 5;
int x = segv;
if (setjmp(buf) == 0)
*(char *)(readonly) = 5;
int y = segv;
printf("sigsegv %d %d\n", x, y);
}
EOF
@ -24,10 +45,7 @@ const char readonly[100] = "abc";
char readwrite[100] = "abc";
EOF
$CC -B. $t/a.o $t/b.so -o $t/exe
readelf -a $t/exe > $t/log
grep -Pqz '(?s)\[(\d+)\] .dynbss.rel.ro .* \1 readonly' $t/log
grep -Pqz '(?s)\[(\d+)\] .dynbss .* \1 readwrite' $t/log
$CC -B. $t/a.o $t/b.so -o $t/exe -no-pie
$t/exe | grep -q '^sigsegv 0 1$'
echo OK