1# Copyright (c) 2013 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 5import logging, os 6 7from autotest_lib.client.bin import utils 8from autotest_lib.client.common_lib import error 9from autotest_lib.server import test 10 11class security_kASLR(test.test): 12 """Tests the kASLR entropy seen across many reboots of a device 13 """ 14 version = 1 15 target_symbol = 'sys_exit' 16 reboot_count = 100 17 reboot_timeout = 60 18 19 def _reboot_machine(self): 20 """Reboot the client machine. 21 22 We'll wait until the client is down, then up again. 23 """ 24 boot_id = self._client.get_boot_id() 25 self._client.run('reboot &') 26 self._client.wait_for_restart(old_boot_id=boot_id, 27 timeout=self.reboot_timeout, 28 down_timeout=self.reboot_timeout, 29 down_warning=self.reboot_timeout) 30 31 def _read_kallsyms(self, filename): 32 """Fetch /proc/kallsyms from client and return lines in the file 33 34 @param filename: The file to write 'cat /proc/kallsyms' into. 35 """ 36 37 f = open(filename, 'w') 38 self._client.run('cat /proc/kallsyms', stdout_tee=f) 39 f.close() 40 41 return utils.read_file(filename) 42 43 def _parse_kallsyms(self, kallsyms): 44 """ Parse the contents of each line of kallsyms, extracting 45 symbol addresses into a returned hash. 46 47 @param kallsyms: string of kallsyms contents 48 """ 49 symbols = {} 50 for line in kallsyms.splitlines(): 51 addr, symtype, symbol = line.strip().split(' ', 2) 52 # Just keep regular text symbols for now. 53 if symtype != "T": 54 continue 55 symbols.setdefault(symbol, addr) 56 return symbols 57 58 def run_once(self, host=None): 59 """Run the test. 60 61 @param host: The client machine to connect to; should be a Host object. 62 """ 63 assert host is not None, "The host must be specified." 64 65 self._client = host 66 67 # Report client configuration, to help debug any problems. 68 kernel_ver = self._client.run('uname -r').stdout.rstrip() 69 arch = utils.get_arch(self._client.run) 70 logging.info("Starting kASLR tests for '%s' on '%s'", 71 kernel_ver, arch) 72 73 # Make sure we're expecting kernel ASLR at all. 74 if utils.compare_versions(kernel_ver, "3.8") < 0: 75 logging.info("kASLR not available on this kernel") 76 return 77 if arch.startswith('arm'): 78 logging.info("kASLR not available on this architecture") 79 return 80 81 kallsyms_filename = os.path.join(self.resultsdir, 'kallsyms') 82 address_count = {} 83 84 count = 0 85 while True: 86 kallsyms = self._read_kallsyms(kallsyms_filename) 87 symbols = self._parse_kallsyms(kallsyms) 88 89 assert symbols.has_key(self.target_symbol), \ 90 "The '%s' symbol is missing!?" % (self.target_symbol) 91 92 addr = symbols[self.target_symbol] 93 logging.debug("Reboot %d: Symbol %s @ %s", \ 94 count, self.target_symbol, addr) 95 96 address_count.setdefault(addr, 0) 97 address_count[addr] += 1 98 99 count += 1 100 if count == self.reboot_count: 101 break 102 self._reboot_machine() 103 104 unique = len(address_count) 105 logging.info("Unique kernel offsets: %d", unique) 106 highest = 0 107 for addr in address_count: 108 logging.debug("Address %s: %d", addr, address_count[addr]) 109 if address_count[addr] > highest: 110 highest = address_count[addr] 111 if unique < 2: 112 raise error.TestFail("kASLR not functioning") 113 if unique < (self.reboot_count / 3): 114 raise error.TestFail("kASLR entropy seems very low") 115 if highest > (unique / 10): 116 raise error.TestFail("kASLR entropy seems to clump") 117