about summary refs log tree commit diff
path: root/scripts
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2020-03-02 14:24:27 +0100
committerFlorian Weimer <fweimer@redhat.com>2020-03-02 14:25:20 +0100
commit0499a353a6e196f468e7ec554cb13c82011f0e36 (patch)
tree581108932370bf01836d379db99bafc4330e3cf7 /scripts
parentb5b7fb76e15c0db545aa11a3ce88f836e5d01a19 (diff)
downloadglibc-0499a353a6e196f468e7ec554cb13c82011f0e36.tar.gz
glibc-0499a353a6e196f468e7ec554cb13c82011f0e36.tar.xz
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.
Diffstat (limited to 'scripts')
-rw-r--r--scripts/check-wx-segment.py85
1 files changed, 85 insertions, 0 deletions
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()