/* * Copyright 2014 Samsung Information Systems America, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Author: Koushik Sen // do not remove the following comment // JALANGI DO NOT INSTRUMENT /** * @file A template for writing a Jalangi 2 analysis * @author Koushik Sen * */ (function (sandbox) { /** *
* This file is a template for writing a custom Jalangi 2 analysis. Simply copy this file and rewrite the * callbacks that you need to implement in your analysis. Other callbacks should be removed from the file. *
* ** In the following methods (also called as callbacks) one can choose to not return anything. * If all of the callbacks return nothing, we get a passive analysis where the * concrete execution happens unmodified and callbacks can be used to observe the execution. * One can choose to return suitable objects with specified properties in some callbacks * to modify the behavior of the concrete execution. For example, one could set the skip * property of the object returned from {@link MyAnalysis#putFieldPre} to true to skip the actual putField operation. * Similarly, one could set the result field of the object returned from a {@link MyAnalysis#write} callback * to modify the value that is actually written to a variable. The result field of the object * returned from a {@link MyAnalysis#conditional} callback can be suitably set to change the control-flow of the * program execution. In {@link MyAnalysis#functionExit} and {@link MyAnalysis#scriptExit}, * one can set the isBacktrack property of the returned object to true to reexecute the body of * the function from the beginning. This in conjunction with the ability to change the * control-flow of a program enables us to explore the different paths of a function in * symbolic execution. *
* ** Note that if process.exit() is called, then an execution terminates abnormally and a callback to * {@link MyAnalysis#endExecution} will be skipped. *
* *
* An analysis can access the source map, which maps instruction identifiers to source locations,
* using the global object stored in J$.smap
. Jalangi 2
* assigns a unique id, called sid
, to each JavaScript
* script loaded at runtime. J$.smap
maps each sid
to an object, say
* iids
, containing source map information for the script whose id is sid
.
* iids
has the following properties: "originalCodeFileName"
(stores the path of the original
* script file), "instrumentedCodeFileName"
(stores the path of the instrumented script file),
* "url"
(is optional and stores the URL of the script if it is set during instrumentation
* using the --url option),
* "evalSid"
(stores the sid of the script in which the eval is called in case the current script comes from
* an eval
function call),
* "evalIid"
(iid of the eval
function call in case the current script comes from an
* eval
function call), "nBranches"
(the number of conditional statements
* in the script),
* and "code"
(a string denoting the original script code if the code is instrumented with the
* --inlineSource option).
* iids
also maps each iid
(which stands for instruction id, an unique id assigned
* to each callback function inserted by Jalangi2) to an array containing
* [beginLineNumber, beginColumnNumber, endLineNumber, endColumnNumber]
. The mapping from iids
* to arrays is only available if the code is instrumented with
* the --inlineIID option.
*
* In each callback described below, iid
denotes the unique static instruction id of the callback in the script.
* Two callback functions inserted in two different scripts may have the same iid. In a callback function, one can access
* the current script id using J$.sid
. One can call J$.getGlobalIID(iid)
to get a string, called
* giid
, that statically identifies the
* callback throughout the program. J$.getGlobalIID(iid)
returns the string J$.sid+":"+iid
.
* J$.iidToLocation(giid)
returns a string
* containing the original script file path, begin and end line numbers and column numbers of the code snippet
* for which the callback with giid
was inserted.
*
*
* A number of sample analyses can be found at {@link ../src/js/sample_analyses/}. Refer to {@link ../README.md} for instructions * on running an analysis. *
* * * * @global * @class */ function MyAnalysis() { /** * This callback is called before a function, method, or constructor invocation. * Note that a method invocation also triggers a {@link MyAnalysis#getFieldPre} and a * {@link MyAnalysis#getField} callbacks. * * @example * y.f(a, b, c) * * // the above call roughly gets instrumented as follows: * * var skip = false; * var aret = analysis.invokeFunPre(113, f, y, [a, b, c], false, true); * if (aret) { * f = aret.f; * y = aret.y; * args = aret.args; * skip = aret.skip * } * if (!skip) { * f.apply(y, args); * } * * @param {number} iid - Static unique instruction identifier of this callback * @param {function} f - The function object that going to be invoked * @param {object} base - The receiver object for the function f * @param {Array} args - The array of arguments passed to f * @param {boolean} isConstructor - True if f is invoked as a constructor * @param {boolean} isMethod - True if f is invoked as a method * @param {number} functionIid - The iid (i.e. the unique instruction identifier) where the function was created * @param {number} functionSid - The sid (i.e. the unique script identifier) where the function was created * {@link MyAnalysis#functionEnter} when the function f is executed. The functionIid can be * treated as the static identifier of the function f. Note that a given function code block can * create several function objects, but each such object has a common functionIid, which is the iid * that is passed to {@link MyAnalysis#functionEnter} when the function executes. * @returns {{f: function, base: Object, args: Array, skip: boolean}|undefined} - If an object is returned and * the skip property of the object is true, then the invocation operation is skipped. * Original f, base, and args are replaced with that from the returned object if * an object is returned. * */ this.invokeFunPre = function (iid, f, base, args, isConstructor, isMethod, functionIid, functionSid) { console.log("Attempting to call xyz") return {f: f, base: base, args: args, skip: false}; }; /** * This callback is called after a function, method, or constructor invocation. * * @example * x = y.f(a, b, c) * * // the above call roughly gets instrumented as follows: * * var skip = false; * var aret = analysis.invokeFunPre(113, f, y, [a, b, c], false, true); * if (aret) { * f = aret.f; * y = aret.y; * args = aret.args; * skip = aret.skip * } * if (!skip) { * result =f.apply(y, args); * } * aret = analysis.invokeFun(117, f, y, args, result, false, true); * if (aret) { * x = aret.result * } else { * x = result; * } * * @param {number} iid - Static unique instruction identifier of this callback * @param {function} f - The function object that was invoked * @param {*} base - The receiver object for the function f * @param {Array} args - The array of arguments passed to f * @param {*} result - The value returned by the invocation * @param {boolean} isConstructor - True if f is invoked as a constructor * @param {boolean} isMethod - True if f is invoked as a method * @param {number} functionIid - The iid (i.e. the unique instruction identifier) where the function was created * @param {number} functionSid - The sid (i.e. the unique script identifier) where the function was created * {@link MyAnalysis#functionEnter} when the function f is executed. functionIid can be treated as the * static identifier of the function f. Note that a given function code block can create several function * objects, but each such object has a common functionIid, which is the iid that is passed to * {@link MyAnalysis#functionEnter} when the function executes. * @returns {{result: *}| undefined} - If an object is returned, the return value of the invoked function is * replaced with the value stored in the result property of the object. This enables one to change the * value that is returned by the actual function invocation. * */ this.invokeFun = function (iid, f, base, args, result, isConstructor, isMethod, functionIid, functionSid) { return {result: result}; }; /** * This callback is called after the creation of a literal. A literal can be a function literal, an object literal, * an array literal, a number, a string, a boolean, a regular expression, null, NaN, Infinity, or undefined. * * @example * x = "Hello" * * // the above call roughly gets instrumented as follows: * * var result = "Hello"; * var aret = analysis.literal(201, result, false); * if (aret) { * result = aret.result; * } * x = result; * * * @param {number} iid - Static unique instruction identifier of this callback * @param {*} val - The literal value * @param {boolean} hasGetterSetter - True if the literal is an object and the object defines getters and setters * @returns {{result: *} | undefined} - If the function returns an object, then the original literal value is * replaced with the value stored in the result property of the object. * */ this.literal = function (iid, val, hasGetterSetter) { return {result: val}; }; /** * This callback is called when a for-in loop is used to iterate the properties of an object. * *@example * for (x in y) { } * * // the above call roughly gets instrumented as follows: * * var aret = analysis.forinObject(iid, y); * if (aret) { * y = aret.result; * } * for (x in y) {} * * @param {number} iid - Static unique instruction identifier of this callback * @param {*} val - Objects whose properties are iterated in a for-in loop. * @returns {{result: *} | undefined} - If the function returns an object, then the original object whose * properties are being iterated is replaced with the value stored in the result property of the * returned object. * */ this.forinObject = function (iid, val) { return {result: val}; }; /** * This callback is triggered at the beginning of a scope for every local variable declared in the scope, for * every formal parameter, for every function defined using a function statement, for arguments * variable, and for the formal parameter passed in a catch statement. * * @param {number} iid - Static unique instruction identifier of this callback * @param {string} name - Name of the variable that is declared * @param {*} val - Initial value of the variable that is declared. Variables can be local variables, function * parameters, catch parameters, arguments, or functions defined using function statements. Variables * declared with var have undefined as initial values and cannot be changed by returning a * different value from this callback. On the beginning of an execution of a function, a declare * callback is called on the arguments variable. * @param {boolean} isArgument - True if the variable is arguments or a formal parameter. * @param {number} argumentIndex - Index of the argument in the function call. Indices start from 0. If the * variable is not a formal parameter, then argumentIndex is -1. * @param {boolean} isCatchParam - True if the variable is a parameter of a catch statement. * @returns {{result: *} | undefined} - If the function returns an object, then the original initial value is * replaced with the value stored in the result property of the object. This does not apply to local * variables declared with var. * */ this.declare = function (iid, name, val, isArgument, argumentIndex, isCatchParam) { return {result: val}; }; /** * This callback is called before a property of an object is accessed. * * @param {number} iid - Static unique instruction identifier of this callback * @param {*} base - Base object * @param {string|*} offset - Property * @param {boolean} isComputed - True if property is accessed using square brackets. For example, * isComputed is true if the get field operation is o[p], and false * if the get field operation is o.p * @param {boolean} isOpAssign - True if the operation is of the formo.p op= e
* @param {boolean} isMethodCall - True if the get field operation is part of a method call (e.g. o.p())
* @returns {{base: *, offset: *, skip: boolean} | undefined} - If an object is returned and the skip
* property of the object is true, then the get field operation is skipped. Original base and
* offset are replaced with that from the returned object if an object is returned.
*
*/
this.getFieldPre = function (iid, base, offset, isComputed, isOpAssign, isMethodCall) {
return {base: base, offset: offset, skip: false};
};
/**
* This callback is called after a property of an object is accessed.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} base - Base object
* @param {string|*} offset - Property
* @param {*} val - Value of base[offset]
* @param {boolean} isComputed - True if property is accessed using square brackets. For example,
* isComputed is true if the get field operation is o[p], and false
* if the get field operation is o.p
* @param {boolean} isOpAssign - True if the operation is of the form o.p op= e
* @param {boolean} isMethodCall - True if the get field operation is part of a method call (e.g. o.p())
* @returns {{result: *} | undefined} - If an object is returned, the value of the get field operation is
* replaced with the value stored in the result property of the object.
*/
this.getField = function (iid, base, offset, val, isComputed, isOpAssign, isMethodCall) {
return {result: val};
};
/**
* This callback is called before a property of an object is written.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} base - Base object
* @param {*} offset - Property
* @param {*} val - Value to be stored in base[offset]
* @param {boolean} isComputed - True if property is accessed using square brackets. For example,
* isComputed is true if the get field operation is o[p], and false
* if the get field operation is o.p
* @param {boolean} isOpAssign - True if the operation is of the form o.p op= e
* @returns {{base: *, offset: *, val: *, skip: boolean} | undefined} - If an object is returned and the skip
* property is true, then the put field operation is skipped. Original base, offset, and
* val are replaced with that from the returned object if an object is returned.
*/
this.putFieldPre = function (iid, base, offset, val, isComputed, isOpAssign) {
return {base: base, offset: offset, val: val, skip: false};
};
/**
* This callback is called after a property of an object is written.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} base - Base object
* @param {*} offset - Property
* @param {*} val - Value to be stored in base[offset]
* @param {boolean} isComputed - True if property is accessed using square brackets. For example,
* isComputed is true if the get field operation is o[p], and false
* if the get field operation is o.p
* @param {boolean} isOpAssign - True if the operation is of the form o.p op= e
* @returns {{result: *} | undefined} - If an object is returned, the result of the put field operation is
* replaced with the value stored in the result property of the object.
*/
this.putField = function (iid, base, offset, val, isComputed, isOpAssign) {
return {result: val};
};
/**
* This callback is called after a variable is read.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {string} name - Name of the variable being read
* @param {*} val - Value read from the variable
* @param {boolean} isGlobal - True if the variable is not declared using var (e.g. console)
* @param {boolean} isScriptLocal - True if the variable is declared in the global scope using var
* @returns {{result: *} | undefined} - If an object is returned, the result of the read operation is
* replaced with the value stored in the result property of the object.
*/
this.read = function (iid, name, val, isGlobal, isScriptLocal) {
return {result: val};
};
/**
* This callback is called before a variable is written.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {string} name - Name of the variable being read
* @param {*} val - Value to be written to the variable
* @param {*} lhs - Value stored in the variable before the write operation
* @param {boolean} isGlobal - True if the variable is not declared using var (e.g. console)
* @param {boolean} isScriptLocal - True if the variable is declared in the global scope using var
* @returns {{result: *} | undefined} - If an object is returned, the result of the write operation is
* replaced with the value stored in the result property of the object.
*/
this.write = function (iid, name, val, lhs, isGlobal, isScriptLocal) {
return {result: val};
};
/**
* This callback is called before a value is returned from a function using the return keyword.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} val - Value to be returned
* @returns {{result: *} | undefined} - If an object is returned, the value to be returned is
* replaced with the value stored in the result property of the object.
*/
this._return = function (iid, val) {
return {result: val};
};
/**
* This callback is called before a value is thrown using the throw keyword.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} val - Value to be thrown
* @returns {{result: *} | undefined} - If an object is returned, the value to be thrown is
* replaced with the value stored in the result property of the object.
*/
this._throw = function (iid, val) {
return {result: val};
};
/**
* This callback is called when a with statement is executed
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} val - Value used as an argument to with
* @returns {{result: *} | undefined} - If an object is returned, the value to be used in with is
* replaced with the value stored in the result property of the object.
*/
this._with = function (iid, val) {
return {result: val};
};
/**
* This callback is called before the execution of a function body starts.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {function} f - The function object whose body is about to get executed
* @param {*} dis - The value of the this variable in the function body
* @param {Array} args - List of the arguments with which the function is called
* @returns {undefined} - Any return value is ignored
*/
this.functionEnter = function (iid, f, dis, args) {
};
/**
* This callback is called when the execution of a function body completes
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} returnVal - The value returned by the function
* @param {{exception:*} | undefined} wrappedExceptionVal - If this parameter is an object, the function
* execution has thrown an uncaught exception and the exception is being stored in the exception
* property of the parameter
* @returns {{returnVal: *, wrappedExceptionVal: *, isBacktrack: boolean}} If an object is returned, then the
* actual returnVal and wrappedExceptionVal.exception are replaced with that from the
* returned object. If an object is returned and the property isBacktrack is set, then the control-flow
* returns to the beginning of the function body instead of returning to the caller. The property
* isBacktrack can be set to true to repeatedly execute the function body as in MultiSE
* symbolic execution.
*/
this.functionExit = function (iid, returnVal, wrappedExceptionVal) {
return {returnVal: returnVal, wrappedExceptionVal: wrappedExceptionVal, isBacktrack: false};
};
/**
* This callback is called before the execution of a JavaScript file
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {string} instrumentedFileName - Name of the instrumented script file
* @param {string} originalFileName - Name of the original script file
*/
this.scriptEnter = function (iid, instrumentedFileName, originalFileName) {
};
/**
* This callback is called when the execution of a JavaScript file completes
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {{exception:*} | undefined} wrappedExceptionVal - If this parameter is an object, the script
* execution has thrown an uncaught exception and the exception is being stored in the exception
* property of the parameter
* @returns {{wrappedExceptionVal: *, isBacktrack: boolean}} - If an object is returned, then the
* actual wrappedExceptionVal.exception is replaced with that from the
* returned object. If an object is returned and the property isBacktrack is set, then the control-flow
* returns to the beginning of the script body. The property
* isBacktrack can be set to true to repeatedly execute the script body as in MultiSE
* symbolic execution.
*/
this.scriptExit = function (iid, wrappedExceptionVal) {
return {wrappedExceptionVal: wrappedExceptionVal, isBacktrack: false};
};
/**
* This callback is called before a binary operation. Binary operations include +, -, *, /, %, &, |, ^,
* <<, >>, >>>, <, >, <=, >=, ==, !=, ===, !==, instanceof, delete, in. No callback for delete x
* because this operation cannot be performed reflectively.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {string} op - Operation to be performed
* @param {*} left - Left operand
* @param {*} right - Right operand
* @param {boolean} isOpAssign - True if the binary operation is part of an expression of the form
* x op= e
* @param {boolean} isSwitchCaseComparison - True if the binary operation is part of comparing the discriminant
* with a consequent in a switch statement.
* @param {boolean} isComputed - True if the operation is of the form delete x[p]
, and false
* otherwise (even if the operation if of the form delete x.p
)
* @returns {{op: string, left: *, right: *, skip: boolean}|undefined} - If an object is returned and the
* skip property is true, then the binary operation is skipped. Original op, left,
* and right are replaced with that from the returned object if an object is returned.
*/
this.binaryPre = function (iid, op, left, right, isOpAssign, isSwitchCaseComparison, isComputed) {
return {op: op, left: left, right: right, skip: false};
};
/**
* This callback is called after a binary operation. Binary operations include +, -, *, /, %, &, |, ^,
* <<, >>, >>>, <, >, <=, >=, ==, !=, ===, !==, instanceof, delete, in.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {string} op - Operation to be performed
* @param {*} left - Left operand
* @param {*} right - Right operand
* @param {*} result - The result of the binary operation
* @param {boolean} isOpAssign - True if the binary operation is part of an expression of the form
* x op= e
* @param {boolean} isSwitchCaseComparison - True if the binary operation is part of comparing the discriminant
* with a consequent in a switch statement.
* @param {boolean} isComputed - True if the operation is of the form delete x[p]
, and false
* otherwise (even if the operation if of the form delete x.p
)
* @returns {{result: *}|undefined} - If an object is returned, the result of the binary operation is
* replaced with the value stored in the result property of the object.
*/
this.binary = function (iid, op, left, right, result, isOpAssign, isSwitchCaseComparison, isComputed) {
return {result: result};
};
/**
* This callback is called before a unary operation. Unary operations include +, -, ~, !, typeof, void.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {string} op - Operation to be performed
* @param {*} left - Left operand
* @returns {{op: *, left: *, skip: boolean} | undefined} If an object is returned and the
* skip property is true, then the unary operation is skipped. Original op and left
* are replaced with that from the returned object if an object is returned.
*/
this.unaryPre = function (iid, op, left) {
return {op: op, left: left, skip: false};
};
/**
* This callback is called after a unary operation. Unary operations include +, -, ~, !, typeof, void.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {string} op - Operation to be performed
* @param {*} left - Left operand
* @param {*} result - The result of the unary operation
* @returns {{result: *}|undefined} - If an object is returned, the result of the unary operation is
* replaced with the value stored in the result property of the object.
*
*/
this.unary = function (iid, op, left, result) {
return {result: result};
};
/**
* This callback is called after a condition check before branching. Branching can happen in various statements
* including if-then-else, switch-case, while, for, ||, &&, ?:.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} result - The value of the conditional expression
* @returns {{result: *}|undefined} - If an object is returned, the result of the conditional expression is
* replaced with the value stored in the result property of the object.
*/
this.conditional = function (iid, result) {
return {result: result};
};
/**
* This callback is called before a string passed as an argument to eval or Function is instrumented.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} code - Code that is going to get instrumented
* @param {boolean} isDirect - true if this is a direct call to eval
* @returns {{code: *, skip: boolean}} - If an object is returned and the
* skip property is true, then the instrumentation of code is skipped.
* Original code is replaced with that from the returned object if an object is returned.
*/
this.instrumentCodePre = function (iid, code, isDirect) {
return {code: code, skip: false};
};
/**
* This callback is called after a string passed as an argument to eval or Function is instrumented.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @param {*} newCode - Instrumented code
* @param {Object} newAst - The AST of the instrumented code
* @param {boolean} isDirect - true if this is a direct call to eval
* @returns {{result: *}|undefined} - If an object is returned, the instrumented code is
* replaced with the value stored in the result property of the object.
*/
this.instrumentCode = function (iid, newCode, newAst, isDirect) {
return {result: newCode};
};
/**
* This callback is called when an expression is evaluated and its value is discarded. For example, this
* callback is called when an expression statement completes its execution.
*
* @param {number} iid - Static unique instruction identifier of this callback
* @returns {undefined} - Any return value is ignored
*/
this.endExpression = function (iid) {
};
/**
* This callback is called when an execution terminates in node.js. In a browser environment, the callback is
* called if ChainedAnalyses.js or ChainedAnalysesNoCheck.js is used and Alt-Shift-T is pressed.
*
* @returns {undefined} - Any return value is ignored
*/
this.endExecution = function () {
};
/**
* This callback is called only when instrumented with J$.Config.ENABLE_SAMPLING = true
* This callback is called before the body of a function, method, or constructor is executed
* if returns true, instrumented function body is executed, else uninstrumented function body is executed
* @param {number} iid - Static unique instruction identifier of this callback
* @param {function} f - The function whose body is being executed
* @param {number} functionIid - The iid (i.e. the unique instruction identifier) where the function was created
* @param {number} functionSid - The sid (i.e. the unique script identifier) where the function was created
* {@link MyAnalysis#functionEnter} when the function f is executed. The functionIid can be
* treated as the static identifier of the function f. Note that a given function code block can
* create several function objects, but each such object has a common functionIid, which is the iid
* that is passed to {@link MyAnalysis#functionEnter} when the function executes.
* @returns {boolean} - If true is returned the instrumented function body is executed, otherwise the
* uninstrumented function body is executed.
*/
this.runInstrumentedFunctionBody = function (iid, f, functionIid, functionSid) {
return false;
};
/**
* onReady is useful if your analysis is running on node.js (i.e., via the direct.js or jalangi.js commands)
* and needs to complete some asynchronous initialization before the instrumented program starts. In such a
* case, once the initialization is complete, invoke the cb function to start execution of the instrumented
* program.
*
* Note that this callback is not useful in the browser, as Jalangi has no control over when the
* instrumented program runs there.
* @param cb
*/
this.onReady = function (cb) {
cb();
};
}
sandbox.analysis = new MyAnalysis();
})(J$);