tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

callgraph.js (8977B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 loadRelativeToScript('utility.js');
      6 loadRelativeToScript('annotations.js');
      7 loadRelativeToScript('CFG.js');
      8 
      9 // Map from csu => set of immediate subclasses
     10 var subclasses = new Map();
     11 
     12 // Map from csu => set of immediate superclasses
     13 var superclasses = new Map();
     14 
     15 // Map from "csu.name:nargs" => set of full method name
     16 var virtualDefinitions = new Map();
     17 
     18 // Every virtual method declaration, anywhere.
     19 //
     20 // Map from csu => Set of function-info.
     21 // function-info: {
     22 //   name : simple string
     23 //   typedfield : "name:nargs" ("mangled" field name)
     24 //   field: full Field datastructure
     25 //   annotations : Set of [annotation-name, annotation-value] 2-element arrays
     26 //   inherited : whether the method is inherited from a base class
     27 //   pureVirtual : whether the method is pure virtual on this CSU
     28 //   dtor : if this is a virtual destructor with a definition in this class or
     29 //     a superclass, then the full name of the definition as if it were defined
     30 //     in this class. This is weird, but it's how gcc emits it. We will add a
     31 //     synthetic call from this function to its immediate base classes' dtors,
     32 //     so even if the function does not actually exist and is inherited from a
     33 //     base class, we will get a path to the inherited function. (Regular
     34 //     virtual methods are *not* claimed to exist when they don't.)
     35 // }
     36 var virtualDeclarations = new Map();
     37 
     38 var virtualResolutionsSeen = new Set();
     39 
     40 var ID = {
     41    jscode: 1,
     42    anyfunc: 2,
     43    nogcfunc: 3,
     44    gc: 4,
     45 };
     46 
     47 // map is a map from names to sets of entries.
     48 function addToNamedSet(map, name, entry)
     49 {
     50    if (!map.has(name))
     51      map.set(name, new Set());
     52    const s = map.get(name);
     53    s.add(entry);
     54    return s;
     55 }
     56 
     57 // CSU is "Class/Struct/Union"
     58 function processCSU(csuName, csu)
     59 {
     60    if (!("FunctionField" in csu))
     61        return;
     62 
     63    for (const {Base} of (csu.CSUBaseClass || [])) {
     64        addToNamedSet(subclasses, Base, csuName);
     65        addToNamedSet(superclasses, csuName, Base);
     66    }
     67 
     68    for (const {Field, Variable} of csu.FunctionField) {
     69        // Virtual method
     70        const info = Field[0];
     71        const name = info.Name[0];
     72        const annotations = new Set();
     73        const funcInfo = {
     74            name,
     75            typedfield: typedField(info),
     76            field: info,
     77            annotations,
     78            inherited: (info.FieldCSU.Type.Name != csuName), // Always false for virtual dtors
     79            pureVirtual: Boolean(Variable),
     80            dtor: false,
     81        };
     82 
     83        if (Variable && isSyntheticVirtualDestructor(name)) {
     84            // This is one of gcc's artificial dtors.
     85            funcInfo.dtor = Variable.Name[0];
     86            funcInfo.pureVirtual = false;
     87        }
     88 
     89        addToNamedSet(virtualDeclarations, csuName, funcInfo);
     90        if ('Annotation' in info) {
     91            for (const {Name: [annType, annValue]} of info.Annotation) {
     92                annotations.add([annType, annValue]);
     93            }
     94        }
     95 
     96        if (Variable) {
     97            // Note: not dealing with overloading correctly.
     98            const name = Variable.Name[0];
     99            addToNamedSet(virtualDefinitions, fieldKey(csuName, Field[0]), name);
    100        }
    101    }
    102 }
    103 
    104 // Return a list of all callees that the given edge might be a call to. Each
    105 // one is represented by an object with a 'kind' field that is one of
    106 // ('direct', 'field', 'resolved-field', 'indirect', 'unknown'), though note
    107 // that 'resolved-field' is really a global record of virtual method
    108 // resolutions, indepedent of this particular edge.
    109 function translateCallees(edge)
    110 {
    111    if (edge.Kind != "Call")
    112        return [];
    113 
    114    const callee = edge.Exp[0];
    115    if (callee.Kind == "Var") {
    116        assert(callee.Variable.Kind == "Func");
    117        return [{'kind': 'direct', 'name': callee.Variable.Name[0]}];
    118    }
    119 
    120    // At some point, we were intentionally invoking invalid function pointers
    121    // (as in, a small integer cast to a function pointer type) to convey a
    122    // small amount of information in the crash address.
    123    if (callee.Kind == "Int")
    124        return []; // Intentional crash
    125 
    126    assert(callee.Kind == "Drf");
    127    let called = callee.Exp[0];
    128    let indirection = 1;
    129    if (called.Kind == "Drf") {
    130        // This is probably a reference to a function pointer (`func*&`). It
    131        // would be possible to determine that for certain by looking up the
    132        // variable's type, which is doable but unnecessary. Indirect calls
    133        // are assumed to call anything (any function in the codebase) unless they
    134        // are annotated otherwise, and the `funkyName` annotation applies to
    135        // `(**funkyName)(args)` as well as `(*funkyName)(args)`, it's ok.
    136        called = called.Exp[0];
    137        indirection += 1;
    138    }
    139 
    140    if (called.Kind == "Var") {
    141        // indirect call through a variable. Note that the `indirection` field is
    142        // currently unused by the later analysis. It is the number of dereferences
    143        // applied to the variable before invoking the resulting function.
    144        //
    145        // The variable name passed through is the simplified one, since that is
    146        // what annotations.js uses and we don't want the annotation to be missed
    147        // if eg there is another variable of the same name in a sibling scope such
    148        // that the fully decorated name no longer matches.
    149        const [decorated, bare] = called.Variable.Name;
    150        return [{'kind': "indirect", 'variable': bare, indirection}];
    151    }
    152 
    153    if (called.Kind != "Fld") {
    154        // unknown call target.
    155        return [{'kind': "unknown"}];
    156    }
    157 
    158    // Return one 'field' callee record giving the full description of what's
    159    // happening here (which is either a virtual method call, or a call through
    160    // a function pointer stored in a field), and then boil the call down to a
    161    // synthetic function that incorporates both the name of the field and the
    162    // static type of whatever you're calling the method on. Both refer to the
    163    // same call; they're just different ways of describing it.
    164    const callees = [];
    165    const field = called.Field;
    166    const staticCSU = getFieldCallInstanceCSU(edge, field);
    167    callees.push({'kind': "field", 'csu': field.FieldCSU.Type.Name, staticCSU,
    168                  'field': field.Name[0], 'fieldKey': fieldKey(staticCSU, field),
    169                  'isVirtual': ("FieldInstanceFunction" in field)});
    170    callees.push({'kind': "direct", 'name': fieldKey(staticCSU, field)});
    171 
    172    return callees;
    173 }
    174 
    175 function getCallees(body, edge, scopeAttrs, functionBodies) {
    176    const calls = [];
    177 
    178    // getCallEdgeProperties can set the ATTR_REPLACED attribute, which
    179    // means that the call in the edge has been replaced by zero or
    180    // more edges to other functions. This is used when the original
    181    // edge will end up calling through a function pointer or something
    182    // (eg ~shared_ptr<T> calls a function pointer that can only be
    183    // T::~T()). The original call edges are left in the graph in case
    184    // they are useful for other purposes.
    185    for (const callee of translateCallees(edge)) {
    186        if (callee.kind != "direct") {
    187            calls.push({ callee, attrs: scopeAttrs });
    188        } else {
    189            const edgeInfo = getCallEdgeProperties(body, edge, callee.name, functionBodies);
    190            for (const extra of (edgeInfo.extraCalls || [])) {
    191                calls.push({ attrs: scopeAttrs | extra.attrs, callee: { name: extra.name, 'kind': "direct", } });
    192            }
    193            calls.push({ callee, attrs: scopeAttrs | edgeInfo.attrs});
    194        }
    195    }
    196 
    197    return calls;
    198 }
    199 
    200 function loadTypes(type_xdb_filename) {
    201    const xdb = xdbLibrary();
    202    xdb.open(type_xdb_filename);
    203 
    204    const minStream = xdb.min_data_stream();
    205    const maxStream = xdb.max_data_stream();
    206 
    207    for (var csuIndex = minStream; csuIndex <= maxStream; csuIndex++) {
    208        const csu = xdb.read_key(csuIndex);
    209        const data = xdb.read_entry(csu);
    210        const json = JSON.parse(data.readString());
    211        processCSU(csu.readString(), json[0]);
    212 
    213        xdb.free_string(csu);
    214        xdb.free_string(data);
    215    }
    216 }
    217 
    218 function loadTypesWithCache(type_xdb_filename, cache_filename) {
    219    try {
    220        const cacheAB = os.file.readFile(cache_filename, "binary");
    221        const cb = serialize();
    222        cb.clonebuffer = cacheAB.buffer;
    223        const cacheData = deserialize(cb);
    224        subclasses = cacheData.subclasses;
    225        superclasses = cacheData.superclasses;
    226        virtualDefinitions = cacheData.virtualDefinitions;
    227    } catch (e) {
    228        loadTypes(type_xdb_filename);
    229        const cb = serialize({subclasses, superclasses, virtualDefinitions});
    230        os.file.writeTypedArrayToFile(cache_filename,
    231                                      new Uint8Array(cb.arraybuffer));
    232    }
    233 }