Skip to content

Commit

Permalink
Add options to print, clear and set executable stack state
Browse files Browse the repository at this point in the history
Add options the modify the state of the executable flag of the GNU_STACK
program header. That header indicates whether the object is requiring an
executable stack.
  • Loading branch information
cgzones committed Jan 27, 2023
1 parent 5908e16 commit a01d762
Show file tree
Hide file tree
Showing 7 changed files with 396 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Makefile
/tests/libbig-dynstr.debug
/tests/contiguous-note-sections
/tests/simple-pie
/tests/simple-execstack

.direnv/
.vscode/
Expand Down
9 changes: 9 additions & 0 deletions patchelf.1
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ This means that when a shared library has an entry point (so that it
can be run as an executable), the debugger does not connect to it correctly and
symbols are not resolved.

.IP "--print-execstack"
Prints the state of the executable flag of the GNU_STACK program header, if present.

.IP "--clear-execstack"
Clears the executable flag of the GNU_STACK program header, or adds a new header.

.IP "--set-execstack"
Sets the executable flag of the GNU_STACK program header, or adds a new header.

.IP "--output FILE"
Set the output file name. If not specified, the input will be modified in place.

Expand Down
120 changes: 120 additions & 0 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1890,6 +1890,104 @@ void ElfFile<ElfFileParamNames>::clearSymbolVersions(const std::set<std::string>
this->rewriteSections();
}

template<ElfFileParams>
void ElfFile<ElfFileParamNames>::modifyExecstack(ExecstackMode op)
{
if (op == ExecstackMode::clear || op == ExecstackMode::set) {
size_t nullhdr = (size_t)-1;

for (size_t i = 0; i < phdrs.size(); i++) {
auto & header = phdrs[i];
const auto type = rdi(header.p_type);
if (type != PT_GNU_STACK) {
if (!nullhdr && type == PT_NULL)
nullhdr = i;
continue;
}

if (op == ExecstackMode::clear && (rdi(header.p_flags) & PF_X) == PF_X) {
debug("simple execstack clear of header %zu\n", i);

wri(header.p_flags, rdi(header.p_flags) & ~PF_X);

* ((Elf_Phdr *) (fileContents->data() + rdi(hdr()->e_phoff)) + i) = header;
changed = true;
} else if (op == ExecstackMode::set && (rdi(header.p_flags) & PF_X) != PF_X) {
debug("simple execstack set of header %zu\n", i);

wri(header.p_flags, rdi(header.p_flags) | PF_X);

* ((Elf_Phdr *) (fileContents->data() + rdi(hdr()->e_phoff)) + i) = header;
changed = true;
} else {
debug("execstack already in requested state\n");
}

return;
}

if (nullhdr != (size_t)-1) {
debug("replacement execstack of header %zu\n", nullhdr);

auto & header = phdrs[nullhdr];
wri(header.p_type, PT_GNU_STACK);
wri(header.p_offset, 0);
wri(header.p_vaddr, 0);
wri(header.p_paddr, 0);
wri(header.p_filesz, 0);
wri(header.p_memsz, 0);
wri(header.p_flags, PF_R | PF_W | (op == ExecstackMode::set ? PF_X : 0));
wri(header.p_align, getPageSize());

* ((Elf_Phdr *) (fileContents->data() + rdi(hdr()->e_phoff)) + nullhdr) = header;
changed = true;
return;
}

debug("header addition for execstack\n");

Elf_Addr firstPage = 0;
for (const auto & phdr : phdrs) {
if (rdi(phdr.p_type) == PT_PHDR) {
firstPage = rdi(phdr.p_vaddr) - rdi(phdr.p_offset);
break;
}
}

Elf_Phdr new_phdr;
wri(new_phdr.p_type, PT_GNU_STACK);
wri(new_phdr.p_offset, 0);
wri(new_phdr.p_vaddr, 0);
wri(new_phdr.p_paddr, 0);
wri(new_phdr.p_filesz, 0);
wri(new_phdr.p_memsz, 0);
wri(new_phdr.p_flags, PF_R | PF_W | (op == ExecstackMode::set ? PF_X : 0));
wri(new_phdr.p_align, getPageSize());
phdrs.push_back(new_phdr);

wri(hdr()->e_phnum, rdi(hdr()->e_phnum) + 1);

changed = true;
rewriteHeaders(firstPage + rdi(hdr()->e_phoff));
return;
}

char result = '?';

for (const auto & header : phdrs) {
if (rdi(header.p_type) != PT_GNU_STACK)
continue;

if ((rdi(header.p_flags) & PF_X) == PF_X)
result = 'X';
else
result = '-';
break;
}

printf("execstack: %c\n", result);
}

static bool printInterpreter = false;
static bool printOsAbi = false;
static bool setOsAbi = false;
Expand All @@ -1912,6 +2010,9 @@ static std::set<std::string> neededLibsToAdd;
static std::set<std::string> symbolsToClearVersion;
static bool printNeeded = false;
static bool noDefaultLib = false;
static bool printExecstack = false;
static bool clearExecstack = false;
static bool setExecstack = false;

template<class ElfFile>
static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, const std::string & fileName)
Expand All @@ -1937,6 +2038,13 @@ static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, con
if (printRPath)
elfFile.modifyRPath(elfFile.rpPrint, {}, "");

if (printExecstack)
elfFile.modifyExecstack(ElfFile::ExecstackMode::print);
else if (clearExecstack)
elfFile.modifyExecstack(ElfFile::ExecstackMode::clear);
else if (setExecstack)
elfFile.modifyExecstack(ElfFile::ExecstackMode::set);

if (shrinkRPath)
elfFile.modifyRPath(elfFile.rpShrink, allowedRpathPrefixes, "");
else if (removeRPath)
Expand Down Expand Up @@ -2019,6 +2127,9 @@ void showHelp(const std::string & progName)
[--no-sort]\t\tDo not sort program+section headers; useful for debugging patchelf.\n\
[--clear-symbol-version SYMBOL]\n\
[--add-debug-tag]\n\
[--print-execstack]\t\tPrints whether the object requests an executable stack\n\
[--clear-execstack]\n\
[--set-execstack]\n\
[--output FILE]\n\
[--debug]\n\
[--version]\n\
Expand Down Expand Up @@ -2127,6 +2238,15 @@ int mainWrapped(int argc, char * * argv)
if (++i == argc) error("missing argument");
symbolsToClearVersion.insert(resolveArgument(argv[i]));
}
else if (arg == "--print-execstack") {
printExecstack = true;
}
else if (arg == "--clear-execstack") {
clearExecstack = true;
}
else if (arg == "--set-execstack") {
setExecstack = true;
}
else if (arg == "--output") {
if (++i == argc) error("missing argument");
outputFileName = resolveArgument(argv[i]);
Expand Down
4 changes: 4 additions & 0 deletions src/patchelf.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ class ElfFile

void clearSymbolVersions(const std::set<std::string> & syms);

enum class ExecstackMode { print, set, clear };

void modifyExecstack(ExecstackMode op);

private:

/* Convert an integer in big or little endian representation (as
Expand Down
18 changes: 14 additions & 4 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
LIBS =

check_PROGRAMS = simple-pie simple main too-many-strtab main-scoped big-dynstr no-rpath contiguous-note-sections
check_PROGRAMS = simple-pie simple simple-execstack main too-many-strtab main-scoped big-dynstr no-rpath contiguous-note-sections

no_rpath_arch_TESTS = \
no-rpath-amd64.sh \
Expand Down Expand Up @@ -43,7 +43,9 @@ src_TESTS = \
replace-needed.sh \
replace-add-needed.sh \
add-debug-tag.sh \
empty-note.sh
empty-note.sh \
print-execstack.sh \
modify-execstack.sh

build_TESTS = \
$(no_rpath_arch_TESTS)
Expand Down Expand Up @@ -71,10 +73,15 @@ export NIX_LDFLAGS=
simple_SOURCES = simple.c
# no -fpic for simple.o
simple_CFLAGS =
simple_LDFLAGS = -Wl,-z,noexecstack

simple_pie_SOURCES = simple.c
simple_pie_CFLAGS = -fPIC -pie

simple_execstack_SOURCES = simple.c
simple_execstack_CFLAGS =
simple_execstack_LDFLAGS = -Wl,-z,execstack

main_SOURCES = main.c
main_LDADD = -lfoo $(AM_LDADD)
main_DEPENDENCIES = libfoo.so
Expand Down Expand Up @@ -108,7 +115,7 @@ check_DATA = libbig-dynstr.debug
# - without libtool, only archives (static libraries) can be built by automake
# - with libtool, it is difficult to control options
# - with libtool, it is not possible to compile convenience *dynamic* libraries :-(
check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libbuildid.so libtoomanystrtab.so \
check_PROGRAMS += libfoo.so libfoo-scoped.so libbar.so libbar-scoped.so libsimple.so libsimple-execstack.so libbuildid.so libtoomanystrtab.so \
phdr-corruption.so

libbuildid_so_SOURCES = simple.c
Expand All @@ -131,7 +138,10 @@ libbar_scoped_so_SOURCES = bar.c
libbar_scoped_so_LDFLAGS = $(LDFLAGS_sharedlib)

libsimple_so_SOURCES = simple.c
libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib)
libsimple_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,noexecstack

libsimple_execstack_so_SOURCES = simple.c
libsimple_execstack_so_LDFLAGS = $(LDFLAGS_sharedlib) -Wl,-z,execstack

too_many_strtab_SOURCES = too-many-strtab.c too-many-strtab2.s
libtoomanystrtab_so_SOURCES = too-many-strtab.c too-many-strtab2.s
Expand Down
Loading

0 comments on commit a01d762

Please sign in to comment.