Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | # vim: set ts=8 sts=4 et sw=4 tw=79: |
michael@0 | 2 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 5 | |
michael@0 | 6 | #---------------------------------------------------------------------------- |
michael@0 | 7 | # All heap allocations in SpiderMonkey must go through js_malloc, js_calloc, |
michael@0 | 8 | # js_realloc, and js_free. This is so that any embedder who uses a custom |
michael@0 | 9 | # allocator (by defining JS_USE_CUSTOM_ALLOCATOR) will see all heap allocation |
michael@0 | 10 | # go through that custom allocator. |
michael@0 | 11 | # |
michael@0 | 12 | # Therefore, the presence of any calls to "vanilla" allocation/free functions |
michael@0 | 13 | # (e.g. malloc(), free()) is a bug. |
michael@0 | 14 | # |
michael@0 | 15 | # This script checks for the presence of such disallowed vanilla |
michael@0 | 16 | # allocation/free function in SpiderMonkey when it's built as a library. It |
michael@0 | 17 | # relies on |nm| from the GNU binutils, and so only works on Linux, but one |
michael@0 | 18 | # platform is good enough to catch almost all violations. |
michael@0 | 19 | # |
michael@0 | 20 | # This checking is only 100% reliable in a JS_USE_CUSTOM_ALLOCATOR build in |
michael@0 | 21 | # which the default definitions of js_malloc et al (in Utility.h) -- which call |
michael@0 | 22 | # malloc et al -- are replaced with empty definitions. This is because the |
michael@0 | 23 | # presence and possible inlining of the default js_malloc et al can cause |
michael@0 | 24 | # malloc/calloc/realloc/free calls show up in unpredictable places. |
michael@0 | 25 | # |
michael@0 | 26 | # Unfortunately, that configuration cannot be tested on Mozilla's standard |
michael@0 | 27 | # testing infrastructure. Instead, by default this script only tests that none |
michael@0 | 28 | # of the other vanilla allocation/free functions (operator new, memalign, etc) |
michael@0 | 29 | # are present. If given the --aggressive flag, it will also check for |
michael@0 | 30 | # malloc/calloc/realloc/free. |
michael@0 | 31 | # |
michael@0 | 32 | # Note: We don't check for |operator delete| and |operator delete[]|. These |
michael@0 | 33 | # can be present somehow due to virtual destructors, but this is not too |
michael@0 | 34 | # because vanilla delete/delete[] calls don't make sense without corresponding |
michael@0 | 35 | # vanilla new/new[] calls, and any explicit calls will be caught by Valgrind's |
michael@0 | 36 | # mismatched alloc/free checking. |
michael@0 | 37 | #---------------------------------------------------------------------------- |
michael@0 | 38 | |
michael@0 | 39 | from __future__ import print_function |
michael@0 | 40 | |
michael@0 | 41 | import argparse |
michael@0 | 42 | import re |
michael@0 | 43 | import subprocess |
michael@0 | 44 | import sys |
michael@0 | 45 | |
michael@0 | 46 | # The obvious way to implement this script is to search for occurrences of |
michael@0 | 47 | # malloc et al, succeed if none are found, and fail is some are found. |
michael@0 | 48 | # However, "none are found" does not necessarily mean "none are present" -- |
michael@0 | 49 | # this script could be buggy. (Or the output format of |nm| might change in |
michael@0 | 50 | # the future.) |
michael@0 | 51 | # |
michael@0 | 52 | # So jsutil.cpp deliberately contains a (never-called) function that contains a |
michael@0 | 53 | # single use of all the vanilla allocation/free functions. And this script |
michael@0 | 54 | # fails if it (a) finds uses of those functions in files other than jsutil.cpp, |
michael@0 | 55 | # *or* (b) fails to find them in jsutil.cpp. |
michael@0 | 56 | |
michael@0 | 57 | # Tracks overall success of the test. |
michael@0 | 58 | has_failed = False |
michael@0 | 59 | |
michael@0 | 60 | |
michael@0 | 61 | def fail(msg): |
michael@0 | 62 | print('TEST-UNEXPECTED-FAIL | check_vanilla_allocations.py |', msg) |
michael@0 | 63 | global has_failed |
michael@0 | 64 | has_failed = True |
michael@0 | 65 | |
michael@0 | 66 | |
michael@0 | 67 | def main(): |
michael@0 | 68 | parser = argparse.ArgumentParser() |
michael@0 | 69 | parser.add_argument('--aggressive', action='store_true', |
michael@0 | 70 | help='also check for malloc, calloc, realloc and free') |
michael@0 | 71 | parser.add_argument('file', type=str, |
michael@0 | 72 | help='name of the file to check') |
michael@0 | 73 | args = parser.parse_args() |
michael@0 | 74 | |
michael@0 | 75 | # Run |nm|. Options: |
michael@0 | 76 | # -u: show only undefined symbols |
michael@0 | 77 | # -C: demangle symbol names |
michael@0 | 78 | # -l: show a filename and line number for each undefined symbol |
michael@0 | 79 | cmd = ['nm', '-u', '-C', '-l', args.file] |
michael@0 | 80 | lines = subprocess.check_output(cmd, universal_newlines=True, |
michael@0 | 81 | stderr=subprocess.PIPE).split('\n') |
michael@0 | 82 | |
michael@0 | 83 | # alloc_fns contains all the vanilla allocation/free functions that we look |
michael@0 | 84 | # for. Regexp chars are escaped appropriately. |
michael@0 | 85 | |
michael@0 | 86 | alloc_fns = [ |
michael@0 | 87 | # Matches |operator new(unsigned T)|, where |T| is |int| or |long|. |
michael@0 | 88 | r'operator new\(unsigned', |
michael@0 | 89 | |
michael@0 | 90 | # Matches |operator new[](unsigned T)|, where |T| is |int| or |long|. |
michael@0 | 91 | r'operator new\[\]\(unsigned', |
michael@0 | 92 | |
michael@0 | 93 | r'memalign', |
michael@0 | 94 | # These two aren't available on all Linux configurations. |
michael@0 | 95 | #r'posix_memalign', |
michael@0 | 96 | #r'aligned_alloc', |
michael@0 | 97 | r'valloc', |
michael@0 | 98 | r'strdup' |
michael@0 | 99 | ] |
michael@0 | 100 | |
michael@0 | 101 | if args.aggressive: |
michael@0 | 102 | alloc_fns += [ |
michael@0 | 103 | r'malloc', |
michael@0 | 104 | r'calloc', |
michael@0 | 105 | r'realloc', |
michael@0 | 106 | r'free' |
michael@0 | 107 | ] |
michael@0 | 108 | |
michael@0 | 109 | # This is like alloc_fns, but regexp chars are not escaped. |
michael@0 | 110 | alloc_fns_unescaped = [fn.translate(None, r'\\') for fn in alloc_fns] |
michael@0 | 111 | |
michael@0 | 112 | # This regexp matches the relevant lines in the output of |nm|, which look |
michael@0 | 113 | # like the following. |
michael@0 | 114 | # |
michael@0 | 115 | # U malloc /path/to/objdir/dist/include/js/Utility.h:142 |
michael@0 | 116 | # |
michael@0 | 117 | alloc_fns_re = r'U (' + r'|'.join(alloc_fns) + r').*\/([\w\.]+):(\d+)$' |
michael@0 | 118 | |
michael@0 | 119 | # This tracks which allocation/free functions have been seen in jsutil.cpp. |
michael@0 | 120 | jsutil_cpp = set([]) |
michael@0 | 121 | |
michael@0 | 122 | for line in lines: |
michael@0 | 123 | m = re.search(alloc_fns_re, line) |
michael@0 | 124 | if m is None: |
michael@0 | 125 | continue |
michael@0 | 126 | |
michael@0 | 127 | fn = m.group(1) |
michael@0 | 128 | filename = m.group(2) |
michael@0 | 129 | linenum = m.group(3) |
michael@0 | 130 | if filename == 'jsutil.cpp': |
michael@0 | 131 | jsutil_cpp.add(fn) |
michael@0 | 132 | else: |
michael@0 | 133 | # An allocation is present in a non-special file. Fail! |
michael@0 | 134 | fail("'" + fn + "' present at " + filename + ':' + linenum) |
michael@0 | 135 | |
michael@0 | 136 | |
michael@0 | 137 | # Check that all functions we expect are used in jsutil.cpp. (This will |
michael@0 | 138 | # fail if the function-detection code breaks at any point.) |
michael@0 | 139 | for fn in alloc_fns_unescaped: |
michael@0 | 140 | if fn not in jsutil_cpp: |
michael@0 | 141 | fail("'" + fn + "' isn't used as expected in jsutil.cpp") |
michael@0 | 142 | else: |
michael@0 | 143 | jsutil_cpp.remove(fn) |
michael@0 | 144 | |
michael@0 | 145 | # This should never happen, but check just in case. |
michael@0 | 146 | if jsutil_cpp: |
michael@0 | 147 | fail('unexpected allocation fns used in jsutil.cpp: ' + |
michael@0 | 148 | ', '.join(jsutil_cpp)) |
michael@0 | 149 | |
michael@0 | 150 | if has_failed: |
michael@0 | 151 | sys.exit(1) |
michael@0 | 152 | |
michael@0 | 153 | print('TEST-PASS | check_vanilla_allocations.py | ok') |
michael@0 | 154 | sys.exit(0) |
michael@0 | 155 | |
michael@0 | 156 | |
michael@0 | 157 | if __name__ == '__main__': |
michael@0 | 158 | main() |
michael@0 | 159 |