Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add options to print, clear and set executable stack state #456

Merged
merged 1 commit into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
105 changes: 103 additions & 2 deletions src/patchelf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1010,10 +1010,10 @@ void ElfFile<ElfFileParamNames>::normalizeNoteSegments()


template<ElfFileParams>
void ElfFile<ElfFileParamNames>::rewriteSections()
void ElfFile<ElfFileParamNames>::rewriteSections(bool force)
{

if (replacedSections.empty()) return;
if (!force && replacedSections.empty()) return;

for (auto & i : replacedSections)
debug("replacing section '%s' with size %d\n",
Expand Down Expand Up @@ -1890,6 +1890,85 @@ 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];
header = {};
wri(header.p_type, PT_GNU_STACK);
wri(header.p_flags, PF_R | PF_W | (op == ExecstackMode::set ? PF_X : 0));
wri(header.p_align, 0x1);

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

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

Elf_Phdr new_phdr = {};
wri(new_phdr.p_type, PT_GNU_STACK);
wri(new_phdr.p_flags, PF_R | PF_W | (op == ExecstackMode::set ? PF_X : 0));
wri(new_phdr.p_align, 0x1);
phdrs.push_back(new_phdr);

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

changed = true;
rewriteSections(true);
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 +1991,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 +2019,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 +2108,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 +2219,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
6 changes: 5 additions & 1 deletion src/patchelf.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class ElfFile

public:

void rewriteSections();
void rewriteSections(bool force = false);

std::string getInterpreter();

Expand Down 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