This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[HELP NEEDED] elf: Add elf/check-wx-segment, a test for the presence of WX segments
- From: Florian Weimer <fweimer at redhat dot com>
- To: John David Anglin <danglin at gcc dot gnu dot org>, Helge Deller <deller at gmx dot de>, Carlos O'Donell <carlos at redhat dot com>, David S. Miller <davem at davemloft dot net>
- Cc: libc-alpha at sourceware dot org
- Date: Fri, 28 Feb 2020 11:07:52 +0100
- Subject: [HELP NEEDED] elf: Add elf/check-wx-segment, a test for the presence of WX segments
Would you please help me to add proper comments to the xfails for the
hppa and sparc ports?
Currently, both (sets of) targets have an writable and executable load
segment. Is this because the lack of secure PLT support, like
originally on POWER?
The new test passes everywhere else.
Thanks,
Florian
8<------------------------------------------------------------------8<
Writable, executable segments defeat security hardening. The
existing check for DT_TEXTREL does not catch this.
-----
elf/Makefile | 7 +++
scripts/check-wx-segment.py | 85 +++++++++++++++++++++++++++++++++++
sysdeps/sparc/Makefile | 4 ++
sysdeps/unix/sysv/linux/hppa/Makefile | 7 ++-
4 files changed, 101 insertions(+), 2 deletions(-)
diff --git a/elf/Makefile b/elf/Makefile
index a137143db7..da689a2c7b 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -402,6 +402,7 @@ tests-special += $(objpfx)tst-pathopt.out $(objpfx)tst-rtld-load-self.out \
$(objpfx)tst-rtld-preload.out
endif
tests-special += $(objpfx)check-textrel.out $(objpfx)check-execstack.out \
+ $(objpfx)check-wx-segment.out \
$(objpfx)check-localplt.out $(objpfx)check-initfini.out
endif
@@ -1180,6 +1181,12 @@ $(objpfx)check-execstack.out: $(..)scripts/check-execstack.awk \
$(evaluate-test)
generated += check-execstack.out
+$(objpfx)check-wx-segment.out: $(..)scripts/check-wx-segment.py \
+ $(all-built-dso:=.phdr)
+ $(PYTHON) $^ --xfail="$(check-wx-segment-xfail)" > $@; \
+ $(evaluate-test)
+generated += check-wx-segment.out
+
$(objpfx)tst-dlmodcount: $(libdl)
$(objpfx)tst-dlmodcount.out: $(test-modules)
diff --git a/scripts/check-wx-segment.py b/scripts/check-wx-segment.py
new file mode 100644
index 0000000000..c3553eeef9
--- /dev/null
+++ b/scripts/check-wx-segment.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python3
+# Check ELF program headers for WX segments.
+# Copyright (C) 2020 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+#
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+"""Check that the program headers do not contain write-exec segments."""
+
+import argparse
+import os.path
+import re
+import sys
+
+# Regular expression to extract the RWE flags field. The
+# address/offset columns have varying width.
+RE_LOAD = re.compile(
+ r'^ LOAD +(?:0x[0-9a-fA-F]+ +){5}([R ][W ][ E]) +0x[0-9a-fA-F]+\n\Z')
+
+def process_file(path, inp, xfail):
+ """Analyze one input file."""
+
+ errors = 0
+ for line in inp:
+ error = None
+ if line.startswith(' LOAD '):
+ match = RE_LOAD.match(line)
+ if match is None:
+ error = 'Invalid LOAD line'
+ else:
+ flags, = match.groups()
+ if 'W' in flags and 'E' in flags:
+ if xfail:
+ print('{}: warning: WX segment (as expected)'.format(
+ path))
+ else:
+ error = 'WX segment'
+
+ if error is not None:
+ print('{}: error: {}: {!r}'.format(path, error, line.strip()))
+ errors += 1
+
+ if xfail and errors == 0:
+ print('{}: warning: missing expected WX segment'.format(path))
+ return errors
+
+
+def main():
+ """The main entry point."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('--xfail',
+ help='Run this number of jobs in parallel',
+ type=str, default='')
+ parser.add_argument('phdrs',
+ help='Files containing readelf -Wl output',
+ nargs='*')
+ opts = parser.parse_args(sys.argv)
+
+ xfails = set(opts.xfail.split(' '))
+ xfails_all = opts.xfail.strip() == '*'
+
+ errors = 0
+ for path in opts.phdrs:
+ xfail = ((os.path.basename(path) + '.phdrs') in xfails
+ or xfails_all)
+ with open(path) as inp:
+ errors += process_file(path, inp, xfail)
+ if errors > 0:
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/sysdeps/sparc/Makefile b/sysdeps/sparc/Makefile
index 38b33af6e0..3ceb8d0298 100644
--- a/sysdeps/sparc/Makefile
+++ b/sysdeps/sparc/Makefile
@@ -16,6 +16,10 @@ CPPFLAGS-crti.S += -fPIC
CPPFLAGS-crtn.S += -fPIC
endif
+ifeq ($(subdir),elf)
+test-xfail-check-wx-segment = *
+endif # $(subdir) == elf
+
# nscd uses atomic_spin_nop which in turn requires cpu_relax
ifeq ($(subdir),nscd)
routines += cpu_relax
diff --git a/sysdeps/unix/sysv/linux/hppa/Makefile b/sysdeps/unix/sysv/linux/hppa/Makefile
index e1637f54f5..1e7340418b 100644
--- a/sysdeps/unix/sysv/linux/hppa/Makefile
+++ b/sysdeps/unix/sysv/linux/hppa/Makefile
@@ -3,9 +3,12 @@ ifeq ($(subdir),stdlib)
gen-as-const-headers += ucontext_i.sym
endif
+ifeq ($(subdir),elf)
# Supporting non-executable stacks on HPPA requires changes to both
# the Linux kernel and glibc. The kernel currently needs an executable
# stack for syscall restarts and signal returns.
-ifeq ($(subdir),elf)
test-xfail-check-execstack = yes
-endif
+
+test-xfail-check-wx-segment = *
+
+endif # $(subdir) == elf