diff options
author | Florian Weimer <fweimer@redhat.com> | 2020-03-02 14:24:27 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2020-03-02 14:25:20 +0100 |
commit | 0499a353a6e196f468e7ec554cb13c82011f0e36 (patch) | |
tree | 581108932370bf01836d379db99bafc4330e3cf7 | |
parent | b5b7fb76e15c0db545aa11a3ce88f836e5d01a19 (diff) | |
download | glibc-0499a353a6e196f468e7ec554cb13c82011f0e36.tar glibc-0499a353a6e196f468e7ec554cb13c82011f0e36.tar.gz glibc-0499a353a6e196f468e7ec554cb13c82011f0e36.tar.bz2 glibc-0499a353a6e196f468e7ec554cb13c82011f0e36.zip |
elf: Add elf/check-wx-segment, a test for the presence of WX segments
Writable, executable segments defeat security hardening. The
existing check for DT_TEXTREL does not catch this.
hppa and SPARC currently keep the PLT in an RWX load segment.
-rw-r--r-- | elf/Makefile | 7 | ||||
-rw-r--r-- | scripts/check-wx-segment.py | 85 | ||||
-rw-r--r-- | sysdeps/sparc/Makefile | 9 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/hppa/Makefile | 9 |
4 files changed, 108 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..e1fa79387c --- /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='Mark input files as XFAILed ("*" for all)', + 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..c123b527b5 100644 --- a/sysdeps/sparc/Makefile +++ b/sysdeps/sparc/Makefile @@ -16,6 +16,15 @@ CPPFLAGS-crti.S += -fPIC CPPFLAGS-crtn.S += -fPIC endif +ifeq ($(subdir),elf) + +# Lazy binding on SPARC rewrites the PLT sequence. See the Solaris +# Linker and Libraries Guide, section SPARC: Procedure Linkage Table. +# <https://docs.oracle.com/cd/E19455-01/816-0559/chapter6-1236/index.html> +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..c89ec83182 100644 --- a/sysdeps/unix/sysv/linux/hppa/Makefile +++ b/sysdeps/unix/sysv/linux/hppa/Makefile @@ -3,9 +3,14 @@ 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 + +# On hppa, the PLT is executable because it contains an executable +# trampoline used during lazy binding. +test-xfail-check-wx-segment = * + +endif # $(subdir) == elf |