• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from autotest_lib.client.bin import test
6from autotest_lib.client.common_lib import error
7
8import logging
9import os
10import errno
11
12class security_RuntimeExecStack(test.test):
13    """Tests that processes have non-executable stacks
14
15    Examines the /proc/$pid/maps file of all running processes for the
16    stack segments' markings. If "x" is found, it fails.
17    """
18    version = 1
19
20    def check_no_exec_stack(self, maps):
21        """Reads process memory map and checks there are no executable stacks.
22
23        Args:
24            @param maps: opened /proc/<pid>/maps file
25
26        Returns:
27          A tuple containing the error code and a string (usually a single line)
28          with debug information. Error code could be:
29            0: ok: stack not executable (second element will be None)
30            1: error: stack is executable
31            2: error: stack is not writable
32            3: error: stack not found
33        """
34        contents = ''
35        stack_count = 0
36        for line in maps:
37            line = line.strip()
38            contents += line + '\n'
39
40            if '[stack' not in line:
41                continue
42            stack_count += 1
43
44            perms = line.split(' ', 2)[1]
45
46            # Stack segment is executable.
47            if 'x' in perms:
48                return 1, line
49
50            # Sanity check we have stack segment perms.
51            if not 'w' in perms:
52                return 2, line
53
54        if stack_count > 0:
55            # Stack segments are non-executable.
56            return 0, None
57        else:
58            # Should be impossible: no stack segment seen.
59            return 3, contents
60
61    def run_once(self):
62        failed = set([])
63
64        for pid in os.listdir('/proc'):
65            maps_path = '/proc/%s/maps' % (pid)
66            # Is this a pid directory?
67            if not os.path.exists(maps_path):
68                continue
69            # Is this a kernel thread?
70            try:
71                os.readlink('/proc/%s/exe' % (pid))
72            except OSError, e:
73                if e.errno == errno.ENOENT:
74                    continue
75            try:
76                maps = open(maps_path)
77                cmd = open('/proc/%s/cmdline' % (pid)).read()
78            except IOError:
79                # Allow the path to vanish out from under us. If
80                # we've failed for any other reason, raise the failure.
81                if os.path.exists(maps_path):
82                    raise
83                logging.debug('ignored: pid %s vanished', pid)
84                continue
85
86            # Clean up cmdline for reporting.
87            cmd = cmd.replace('\x00', ' ')
88            exe = cmd
89            if ' ' in exe:
90                exe = exe[:exe.index(' ')]
91
92            # Check the stack segment.
93            stack, report = self.check_no_exec_stack(maps)
94
95            # Report outcome.
96            if stack == 0:
97                logging.debug('ok: %s %s', pid, exe)
98            else:
99                logging.info('FAIL: %s %s %s', pid, cmd, report)
100                failed.add(exe)
101
102        if len(failed) != 0:
103            msg = 'Bad stacks segments: %s' % (', '.join(failed))
104            raise error.TestFail(msg)
105