From 462291339d55d6ca15322db723dba30cac7e84ea Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 10 May 2017 20:59:04 -0700 Subject: [PATCH] timers: do not use user object call/apply Timers should work even if the user has monkey-patched `.call()` and `.apply()` to undesirable values. Refs: https://github.com/nodejs/node/issues/12956 --- lib/timers.js | 12 ++++---- test/parallel/test-timers-user-call.js | 40 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-timers-user-call.js diff --git a/lib/timers.js b/lib/timers.js index fb9984abf45e05..dd650d3c9acd56 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -485,20 +485,20 @@ function ontimeout(timer) { if (typeof callback !== 'function') return promiseResolve(callback, args[0]); if (!args) - callback.call(timer); + timer._onTimeout(); else { switch (args.length) { case 1: - callback.call(timer, args[0]); + timer._onTimeout(args[0]); break; case 2: - callback.call(timer, args[0], args[1]); + timer._onTimeout(args[0], args[1]); break; case 3: - callback.call(timer, args[0], args[1], args[2]); + timer._onTimeout(args[0], args[1], args[2]); break; default: - callback.apply(timer, args); + Function.prototype.apply.call(callback, timer, args); } } if (timer._repeat) @@ -806,7 +806,7 @@ function runCallback(timer) { return timer._callback(argv[0], argv[1], argv[2]); // more than 3 arguments run slower with .apply default: - return timer._callback.apply(timer, argv); + return Function.prototype.apply.call(timer._callback, timer, argv); } } diff --git a/test/parallel/test-timers-user-call.js b/test/parallel/test-timers-user-call.js new file mode 100644 index 00000000000000..4ff24e688b5aa3 --- /dev/null +++ b/test/parallel/test-timers-user-call.js @@ -0,0 +1,40 @@ +// Make sure `setTimeout()` and friends don't throw if the user-supplied +// function has .call() and .apply() monkey-patched to undesirable values. + +// Refs: https://github.com/nodejs/node/issues/12956 + +'use strict'; + +const common = require('../common'); + +{ + const fn = common.mustCall(10); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + setTimeout(fn, 1); + setTimeout(fn, 1, 'oneArg'); + setTimeout(fn, 1, 'two', 'args'); + setTimeout(fn, 1, 'three', '(3)', 'args'); + setTimeout(fn, 1, 'more', 'than', 'three', 'args'); + + setImmediate(fn, 1); + setImmediate(fn, 1, 'oneArg'); + setImmediate(fn, 1, 'two', 'args'); + setImmediate(fn, 1, 'three', '(3)', 'args'); + setImmediate(fn, 1, 'more', 'than', 'three', 'args'); +} + +{ + const testInterval = (...args) => { + const fn = common.mustCall(() => { clearInterval(interval); }); + fn.call = 'not a function'; + fn.apply = 'also not a function'; + const interval = setInterval(fn, 1, ...args); + }; + + testInterval(); + testInterval('oneArg'); + testInterval('two', 'args'); + testInterval('three', '(3)', 'args'); + testInterval('more', 'than', 'three', 'args'); +}