summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-02-20 21:24:57 +0000
committerGitHub <noreply@github.com>2023-02-20 21:24:57 +0000
commit365e1e01868f453ee01c5aaafd78631818ddf7f0 (patch)
tree29d40b2263b62f74c15ad43749096e96a67d2c0c
parent85b3460be02ade89f57418415d08546f002b03b3 (diff)
parentf7d304eeb1f46a15d8fb6564459ed003edbf9bb3 (diff)
downloadpatchelf-365e1e01868f453ee01c5aaafd78631818ddf7f0.tar.gz
Merge #456
456: Add options to print, clear and set executable stack state r=Mic92 a=cgzones Co-authored-by: Christian Göttsche <cgzones@googlemail.com>
-rw-r--r--.gitignore1
-rw-r--r--patchelf.19
-rw-r--r--src/patchelf.cc105
-rw-r--r--src/patchelf.h6
-rw-r--r--tests/Makefile.am18
-rwxr-xr-xtests/modify-execstack.sh225
-rwxr-xr-xtests/print-execstack.sh23
7 files changed, 380 insertions, 7 deletions
diff --git a/.gitignore b/.gitignore
index c3f60a5..423e8ee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,7 @@ Makefile
/tests/libbig-dynstr.debug
/tests/contiguous-note-sections
/tests/simple-pie
+/tests/simple-execstack
.direnv/
.vscode/
diff --git a/patchelf.1 b/patchelf.1
index 7c8f402..3d46f1f 100644
--- a/patchelf.1
+++ b/patchelf.1
@@ -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.
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 2bb84eb..e91e2ab 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -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",
@@ -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;
@@ -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)
@@ -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)
@@ -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\
@@ -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]);
diff --git a/src/patchelf.h b/src/patchelf.h
index f4eec6f..c3096ff 100644
--- a/src/patchelf.h
+++ b/src/patchelf.h
@@ -105,7 +105,7 @@ private:
public:
- void rewriteSections();
+ void rewriteSections(bool force = false);
std::string getInterpreter();
@@ -139,6 +139,10 @@ public:
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
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 219f238..9d36645 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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 \
@@ -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)
@@ -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
@@ -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
@@ -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
diff --git a/tests/modify-execstack.sh b/tests/modify-execstack.sh
new file mode 100755
index 0000000..6baae48
--- /dev/null
+++ b/tests/modify-execstack.sh
@@ -0,0 +1,225 @@
+#! /bin/sh -e
+SCRATCH=scratch/$(basename $0 .sh)
+PATCHELF=$(readlink -f "../src/patchelf")
+
+rm -rf ${SCRATCH}
+mkdir -p ${SCRATCH}
+
+cp simple ${SCRATCH}/
+cp simple-execstack ${SCRATCH}/
+cp libsimple.so ${SCRATCH}/
+cp libsimple-execstack.so ${SCRATCH}/
+
+cd ${SCRATCH}
+
+
+## simple
+
+cp simple backup
+
+if ! ${PATCHELF} --print-execstack simple | grep -q 'execstack: -'; then
+ echo "[simple] wrong initial execstack detection"
+ ${PATCHELF} --print-execstack simple
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack simple; then
+ echo "[simple] failed noop initial clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack simple; then
+ echo "[simple] failed set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack simple | grep -q 'execstack: X'; then
+ echo "[simple] wrong execstack detection after set"
+ ${PATCHELF} --print-execstack simple
+ exit 1
+fi
+
+if diff simple backup; then
+ echo "[simple] no change after set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack simple; then
+ echo "[simple] failed noop set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack simple; then
+ echo "[simple] failed clear after set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack simple | grep -q 'execstack: -'; then
+ echo "[simple] wrong execstack detection after clear after set"
+ ${PATCHELF} --print-execstack simple
+ exit 1
+fi
+
+if ! diff simple backup; then
+ echo "[simple] change against backup after clear after set"
+ exit 1
+fi
+
+
+## simple-execstack
+
+cp simple-execstack backup
+
+if ! ${PATCHELF} --print-execstack simple-execstack | grep -q 'execstack: X'; then
+ echo "[simple-execstack] wrong initial execstack detection"
+ ${PATCHELF} --print-execstack simple-execstack
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack simple-execstack; then
+ echo "[simple-execstack] failed noop initial set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack simple-execstack; then
+ echo "[simple-execstack] failed clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack simple-execstack | grep -q 'execstack: -'; then
+ echo "[simple-execstack] wrong execstack detection after clear"
+ ${PATCHELF} --print-execstack simple-execstack
+ exit 1
+fi
+
+if diff simple-execstack backup; then
+ echo "[simple-execstack] no change after set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack simple-execstack; then
+ echo "[simple-execstack] failed noop clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack simple-execstack; then
+ echo "[simple-execstack] failed set after clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack simple-execstack | grep -q 'execstack: X'; then
+ echo "[simple-execstack] wrong execstack detection after set after clear"
+ ${PATCHELF} --print-execstack simple-execstack
+ exit 1
+fi
+
+if ! diff simple-execstack backup; then
+ echo "[simple-execstack] change against backup after set after clear"
+ exit 1
+fi
+
+
+## libsimple.so
+
+cp libsimple.so backup
+
+if ! ${PATCHELF} --print-execstack libsimple.so | grep -q 'execstack: -'; then
+ echo "[libsimple.so] wrong initial execstack detection"
+ ${PATCHELF} --print-execstack libsimple.so
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack libsimple.so; then
+ echo "[libsimple.so] failed noop initial clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack libsimple.so; then
+ echo "[libsimple.so] failed set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack libsimple.so | grep -q 'execstack: X'; then
+ echo "[libsimple.so] wrong execstack detection after set"
+ ${PATCHELF} --print-execstack libsimple.so
+ exit 1
+fi
+
+if diff libsimple.so backup; then
+ echo "[libsimple.so] no change after set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack libsimple.so; then
+ echo "[libsimple.so] failed noop set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack libsimple.so; then
+ echo "[libsimple.so] failed clear after set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack libsimple.so | grep -q 'execstack: -'; then
+ echo "[libsimple.so] wrong execstack detection after clear after set"
+ ${PATCHELF} --print-execstack libsimple.so
+ exit 1
+fi
+
+if ! diff libsimple.so backup; then
+ echo "[libsimple.so] change against backup after clear after set"
+ exit 1
+fi
+
+
+## libsimple-execstack.so
+
+cp libsimple-execstack.so backup
+
+if ! ${PATCHELF} --print-execstack libsimple-execstack.so | grep -q 'execstack: X'; then
+ echo "[libsimple-execstack.so] wrong initial execstack detection"
+ ${PATCHELF} --print-execstack libsimple-execstack.so
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack libsimple-execstack.so; then
+ echo "[libsimple-execstack.so] failed noop initial set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack libsimple-execstack.so; then
+ echo "[libsimple-execstack.so] failed clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack libsimple-execstack.so | grep -q 'execstack: -'; then
+ echo "[libsimple-execstack.so] wrong execstack detection after clear"
+ ${PATCHELF} --print-execstack libsimple-execstack.so
+ exit 1
+fi
+
+if diff libsimple-execstack.so backup; then
+ echo "[libsimple-execstack.so] no change after set"
+ exit 1
+fi
+
+if ! ${PATCHELF} --clear-execstack libsimple-execstack.so; then
+ echo "[libsimple-execstack.so] failed noop clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --set-execstack libsimple-execstack.so; then
+ echo "[libsimple-execstack.so] failed set after clear"
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack libsimple-execstack.so | grep -q 'execstack: X'; then
+ echo "[libsimple-execstack.so] wrong execstack detection after set after clear"
+ ${PATCHELF} --print-execstack libsimple-execstack.so
+ exit 1
+fi
+
+if ! diff libsimple-execstack.so backup; then
+ echo "[libsimple-execstack.so] change against backup after set after clear"
+ exit 1
+fi
diff --git a/tests/print-execstack.sh b/tests/print-execstack.sh
new file mode 100755
index 0000000..e34fad8
--- /dev/null
+++ b/tests/print-execstack.sh
@@ -0,0 +1,23 @@
+#! /bin/sh -e
+SCRATCH=scratch/$(basename $0 .sh)
+PATCHELF=$(readlink -f "../src/patchelf")
+
+rm -rf ${SCRATCH}
+mkdir -p ${SCRATCH}
+
+cp simple ${SCRATCH}/
+cp simple-execstack ${SCRATCH}/
+
+cd ${SCRATCH}
+
+if ! ${PATCHELF} --print-execstack simple | grep -q 'execstack: -'; then
+ echo "wrong execstack detection"
+ ${PATCHELF} --print-execstack simple
+ exit 1
+fi
+
+if ! ${PATCHELF} --print-execstack simple-execstack | grep -q 'execstack: X'; then
+ echo "wrong execstack detection"
+ ${PATCHELF} --print-execstack simple-execstack
+ exit 1
+fi