1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/analysis/flow.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,150 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +require({ version: '1.8' }); 1.9 +require({ after_gcc_pass: 'cfg' }); 1.10 + 1.11 +include('treehydra.js'); 1.12 + 1.13 +include('util.js'); 1.14 +include('gcc_util.js'); 1.15 +include('gcc_print.js'); 1.16 +include('unstable/adts.js'); 1.17 +include('unstable/analysis.js'); 1.18 +include('unstable/esp.js'); 1.19 + 1.20 +/* This implements the control flows-through analysis in bug 432917 */ 1.21 +var Zero_NonZero = {} 1.22 +include('unstable/zero_nonzero.js', Zero_NonZero); 1.23 + 1.24 +MapFactory.use_injective = true; 1.25 + 1.26 +// Print a trace for each function analyzed 1.27 +let TRACE_FUNCTIONS = 0; 1.28 +// Trace operation of the ESP analysis, use 2 or 3 for more detail 1.29 +let TRACE_ESP = 0; 1.30 +// Print time-taken stats 1.31 +let TRACE_PERF = 0; 1.32 + 1.33 +function process_tree(fndecl) { 1.34 + // At this point we have a function we want to analyze 1.35 + if (TRACE_FUNCTIONS) { 1.36 + print('* function ' + decl_name(fndecl)); 1.37 + print(' ' + loc_string(location_of(fndecl))); 1.38 + } 1.39 + if (TRACE_PERF) timer_start(fstring); 1.40 + 1.41 + let cfg = function_decl_cfg(fndecl); 1.42 + 1.43 + try { 1.44 + let trace = TRACE_ESP; 1.45 + let a = new FlowCheck(cfg, trace); 1.46 + a.run(); 1.47 + } catch (e if e == "skip") { 1.48 + return 1.49 + } 1.50 + print("checked " + decl_name(fndecl)) 1.51 + if (cfg.x_exit_block_ptr.stateIn.substates) 1.52 + for each (let substate in cfg.x_exit_block_ptr.stateIn.substates.getValues()) { 1.53 + for each (let v in substate.getVariables()) { 1.54 + let var_state= substate.get (v) 1.55 + let blame = substate.getBlame(v) 1.56 + if (var_state != ESP.TOP && typeof var_state == 'string') 1.57 + error(decl_name(fndecl) + ": Control did not flow through " +var_state, location_of(blame)) 1.58 + } 1.59 + } 1.60 + 1.61 + if (TRACE_PERF) timer_stop(fstring); 1.62 +} 1.63 + 1.64 +let track_return_loc = 0; 1.65 +const FLOW_THROUGH = "MUST_FLOW_THROUGH" 1.66 + 1.67 +function FlowCheck(cfg, trace) { 1.68 + let found = create_decl_set(); // ones we already found 1.69 + for (let bb in cfg_bb_iterator(cfg)) { 1.70 + for (let isn in bb_isn_iterator(bb)) { 1.71 + switch (isn.tree_code()) { 1.72 + case GIMPLE_CALL: { 1.73 + let fn = gimple_call_fndecl(isn) 1.74 + if (!fn || decl_name(fn) != FLOW_THROUGH) 1.75 + continue; 1.76 + this.must_flow_fn = fn 1.77 + break 1.78 + } 1.79 + case GIMPLE_RETURN: 1.80 + let ret_expr = return_expr(isn); 1.81 + if (track_return_loc && ret_expr) { 1.82 + TREE_CHECK(ret_expr, VAR_DECL, RESULT_DECL); 1.83 + this.rval = ret_expr; 1.84 + } 1.85 + break; 1.86 + } 1.87 + } 1.88 + } 1.89 + if (!this.must_flow_fn) 1.90 + throw "skip" 1.91 + 1.92 + let psvar_list = [new ESP.PropVarSpec(this.must_flow_fn, true)] 1.93 + 1.94 + if (this.rval) 1.95 + psvar_list.push(new ESP.PropVarSpec(this.rval)) 1.96 + 1.97 + this.zeroNonzero = new Zero_NonZero.Zero_NonZero() 1.98 + ESP.Analysis.call(this, cfg, psvar_list, Zero_NonZero.meet, trace); 1.99 +} 1.100 + 1.101 +FlowCheck.prototype = new ESP.Analysis; 1.102 + 1.103 +function char_star_arg2string(tree) { 1.104 + return TREE_STRING_POINTER(tree.tree_check(ADDR_EXPR).operands()[0].tree_check(ARRAY_REF).operands()[0]) 1.105 +} 1.106 + 1.107 +// State transition function. Mostly, we delegate everything to 1.108 +// another function as either an assignment or a call. 1.109 +FlowCheck.prototype.flowState = function(isn, state) { 1.110 + switch (TREE_CODE(isn)) { 1.111 + case GIMPLE_CALL: { 1.112 + let fn = gimple_call_fndecl(isn) 1.113 + if (fn == this.must_flow_fn) 1.114 + state.assignValue(fn, char_star_arg2string(gimple_call_arg(isn, 0)), isn) 1.115 + break 1.116 + } 1.117 + case GIMPLE_LABEL: { 1.118 + let label = decl_name(gimple_op(isn, 0)) 1.119 + for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) { 1.120 + if (label != value) continue 1.121 + // reached the goto label we wanted =D 1.122 + state.assignValue(this.must_flow_fn, ESP.TOP, isn) 1.123 + } 1.124 + break 1.125 + } 1.126 + case GIMPLE_RETURN: 1.127 + for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) { 1.128 + if (typeof value != 'string') continue 1.129 + let loc; 1.130 + if (this.rval) 1.131 + for ([value, blame] in state.yieldPreconditions(this.rval)) { 1.132 + loc = value 1.133 + break 1.134 + } 1.135 + error("return without going through label "+ value, loc); 1.136 + return 1.137 + } 1.138 + break 1.139 + case GIMPLE_ASSIGN: 1.140 + if (track_return_loc && gimple_op(isn, 0) == this.rval) { 1.141 + state.assignValue(this.rval, location_of(isn), isn) 1.142 + break 1.143 + } 1.144 + default: 1.145 + this.zeroNonzero.flowState(isn, state) 1.146 + } 1.147 +} 1.148 + 1.149 +// State transition function to apply branch filters. This is kind 1.150 +// of boilerplate--we're just handling some stuff that GCC generates. 1.151 +FlowCheck.prototype.flowStateCond = function(isn, truth, state) { 1.152 + this.zeroNonzero.flowStateCond (isn, truth, state) 1.153 +}