Home | History | Annotate | Download | only in security_kASLR
      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 
      5 import logging, os
      6 
      7 from autotest_lib.client.bin import utils
      8 from autotest_lib.client.common_lib import error
      9 from autotest_lib.server import test
     10 
     11 class 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