From b2e680a109d2a8fbc84e58a2c31b619671a78e8c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 29 May 2014 12:49:42 -0700 Subject: [PATCH] NO_DYNAMIC_EXECUTION option to disable features using eval() or new Function() --- emcc | 2 ++ src/preamble.js | 17 +++++++++++++---- src/runtime.js | 4 ++++ src/settings.js | 5 +++++ src/shell.js | 6 ++++++ tests/test_other.py | 10 ++++++++++ 6 files changed, 40 insertions(+), 4 deletions(-) diff --git a/emcc b/emcc index e55990dfb..37a17ab1c 100755 --- a/emcc +++ b/emcc @@ -1320,6 +1320,8 @@ try: logging.warning('disabling closure because debug info was requested') closure = False + assert not (shared.Settings.NO_DYNAMIC_EXECUTION and closure), 'cannot have both NO_DYNAMIC_EXECUTION and closure compiler enabled at the same time' + if closure: shared.Settings.CLOSURE_COMPILER = 1 assert os.path.exists(shared.CLOSURE_COMPILER), logging.error('fatal: Closure compiler (%s) does not exist', shared.CLOSURE_COMPILER) diff --git a/src/preamble.js b/src/preamble.js index 58b442abf..bba2fc460 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -312,10 +312,15 @@ var globalScope = this; // Returns the C function with a specified identifier (for C++, you need to do manual name mangling) function getCFunc(ident) { - try { - var func = Module['_' + ident]; // closure exported function - if (!func) func = eval('_' + ident); // explicit lookup - } catch(e) { + var func = Module['_' + ident]; // closure exported function + if (!func) { +#if NO_DYNAMIC_EXECUTION == 0 + try { + func = eval('_' + ident); // explicit lookup + } catch(e) {} +#else + abort('NO_DYNAMIC_EXECUTION was set, cannot eval - ccall/cwrap are not functional'); +#endif } assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)'); return func; @@ -458,7 +463,11 @@ var cwrap, ccall; funcstr += JSsource['stackRestore'].body + ';'; } funcstr += 'return ret})'; +#if NO_DYNAMIC_EXECUTION == 0 return eval(funcstr); +#else + abort('NO_DYNAMIC_EXECUTION was set, cannot eval - ccall is not functional'); +#endif }; })(); Module["cwrap"] = cwrap; diff --git a/src/runtime.js b/src/runtime.js index 4466a3080..96b122949 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -418,12 +418,16 @@ var Runtime = { abort('invalid EM_ASM input |' + source + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)'); } } +#if NO_DYNAMIC_EXECUTION == 0 try { var evalled = eval('(function(' + args.join(',') + '){ ' + source + ' })'); // new Function does not allow upvars in node } catch(e) { Module.printErr('error in executing inline EM_ASM code: ' + e + ' on: \n\n' + source + '\n\nwith args |' + args + '| (make sure to use the right one out of EM_ASM, EM_ASM_ARGS, etc.)'); throw e; } +#else + abort('NO_DYNAMIC_EXECUTION was set, cannot eval, so EM_ASM is not functional'); +#endif return Runtime.asmConstCache[code] = evalled; }, diff --git a/src/settings.js b/src/settings.js index 3289eace6..bdb149e38 100644 --- a/src/settings.js +++ b/src/settings.js @@ -502,6 +502,11 @@ var JS_CHUNK_SIZE = 10240; // Used as a maximum size before breaking up expressi var EXPORT_NAME = 'Module'; // Global variable to export the module as for environments without a standardized module // loading system (e.g. the browser and SM shell). +var NO_DYNAMIC_EXECUTION = 0; // When enabled, we do not emit eval() and new Function(), which disables some functionality + // (causing runtime errors if attempted to be used), but allows the emitted code to be + // acceptable in places that disallow dynamic code execution (chrome packaged app, non- + // privileged firefox app, etc.) + var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler var COMPILER_ASSERTIONS = 0; // costly (slow) compile-time assertions diff --git a/src/shell.js b/src/shell.js index e1c0eb54f..279a34618 100644 --- a/src/shell.js +++ b/src/shell.js @@ -96,7 +96,9 @@ else if (ENVIRONMENT_IS_SHELL) { this['{{{ EXPORT_NAME }}}'] = Module; +#if CLOSURE_COMPILER eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined"); // wipe out the SpiderMonkey shell 'gc' function, which can confuse closure (uses it as a minified name, and it is then initted to a non-falsey value unexpectedly) +#endif } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { Module['read'] = function read(url) { @@ -139,7 +141,11 @@ else { } function globalEval(x) { +#if NO_DYNAMIC_EXECUTION == 0 eval.call(null, x); +#else + throw 'NO_DYNAMIC_EXECUTION was set, cannot eval'; +#endif } if (!Module['load'] == 'undefined' && Module['read']) { Module['load'] = function load(f) { diff --git a/tests/test_other.py b/tests/test_other.py index 03859a4e7..cbb9ebc4e 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -2889,3 +2889,13 @@ int main(int argc, char **argv) { else: self.assertContained('hello, world!', run_js('a.out.js')) + def test_no_dynamic_execution(self): + cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-O1', '-s', 'NO_DYNAMIC_EXECUTION=1'] + stdout, stderr = Popen(cmd, stderr=PIPE).communicate() + self.assertContained('hello, world!', run_js('a.out.js')) + src = open('a.out.js').read() + assert 'eval(' not in src + assert 'eval.' not in src + assert 'new Function' not in src + +