ld execstack option

Linux ELF binaries store a flag to indicate whether they want their stack to be executable or not. The Linux loader will check that flag and adjust appropriately the stack via an mprotect system call. But who decides about setting or resetting this flag when generating a binary? The answer is the linker. The linker checks all object files and decides based on them if it should make the stack executable or not.

The decision is based on the following algorithm:

  1. If one of the object files does not contain a section named .note.GNU-stack, then the stack of the generated ELF binary will be executable to maintain backward compatibility.
  2. If one of the object files contains a section named .note.GNU-stack which is marked as containing code, then the stack will be marked as executable.
  3. Otherwise (if all object files contain a section named .note.GNU-stack which is marked as not containing code) the stack will be marked as non executable.

The patch that added this mechanism to binutils has been announced here: https://sourceware.org/ml/binutils/2003-05/msg00741.html

Below, I present a test case that explores the different options, using a C program that copies a function on its stack and executes it.

#include <alloca.h>
#include <stdio.h>
#include <string.h>

void test(char *msg, int len)
{
	asm("syscall" : : "a"(1), "D"(1), "S"(msg), "d"(len));
}

int main(void)
{
	char *before = "before\n";
	char *from_stack = "from_stack\n";
	char *after = "after\n";

	test(before, strlen(before));
	char *stack_ptr = alloca(1024);
	memcpy(stack_ptr, test, 1024);
	((void(*)(char*,int))stack_ptr)(from_stack, strlen(from_stack));
	test(after, strlen(after));
	return 0;
}

I use the following Bash script to try different configurations:

#!/bin/bash

FILE=test_execstack

run() {
  readelf -l $FILE | grep -A1 GNU_STACK
  ./$FILE
}
header() {
  echo -e "\x1b[32m== $* ==\x1b[0m"
}

header 'Attempt #1 (SEGFAULT)'
cc -o $FILE $FILE.c
run

header 'Attempt #2 (SEGFAULT)'
echo '.section .note.GNU-stack,""' | as -o noexec.o -
cc -o $FILE $FILE.c noexec.o
run

header 'Attempt #3 (SEGFAULT)'
echo '' | as -o empty.o -
cc -o $FILE $FILE.c empty.o -Wl,-z,noexecstack
run

header 'Attempt #4'
echo '' | as -o empty.o -
cc -o $FILE $FILE.c empty.o
run

header 'Attempt #5'
echo '.section .note.GNU-stack,"x"' | as -o exec.o -
cc -o $FILE $FILE.c exec.o
run

header 'Attempt #6'
cc -o $FILE $FILE.c -Wl,-z,execstack
run

The results of running the above script are the following:

== Attempt #1 (SEGFAULT) ==
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
before
./test.sh: line 5: 21078 Segmentation fault      ./$FILE
== Attempt #2 (SEGFAULT) ==
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
before
./test.sh: line 5: 21090 Segmentation fault      ./$FILE
== Attempt #3 (SEGFAULT) ==
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     10
before
./test.sh: line 5: 21101 Segmentation fault      ./$FILE
== Attempt #4 ==
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    10
before
from_stack
after
== Attempt #5 ==
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    10
before
from_stack
after
== Attempt #6 ==
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RWE    10
before
from_stack
after

Attempts 1 to 3 fail with segmentation fault because of the attempt to execute an instruction on the stack. The binary contains a program header named GNU_STACK with RW flags, which informs the Linux loader to disallow execution on the stack.

Attempts 4 to 6 succeed and the GNU_STACK program header has RWE flags.

The different ways to produce an ELF binary with or without stack execution protection are illustrated in the Bash script.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: