Skip to content

Commit

Permalink
[ELF] Protect the last page of a RELRO segment
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rui314 committed Jan 24, 2022
1 parent b2ef045 commit 0a0f9b3
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 6 deletions.
6 changes: 6 additions & 0 deletions elf/output-chunks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
30 changes: 24 additions & 6 deletions test/elf/copyrel-relro.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

0 comments on commit 0a0f9b3

Please sign in to comment.