Add options to print, clear and set executable stack state
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.
This commit is contained in:
parent
5908e16cd5
commit
f7d304eeb1
|
@ -32,6 +32,7 @@ Makefile
|
|||
/tests/libbig-dynstr.debug
|
||||
/tests/contiguous-note-sections
|
||||
/tests/simple-pie
|
||||
/tests/simple-execstack
|
||||
|
||||
.direnv/
|
||||
.vscode/
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
105
src/patchelf.cc
105
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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue