Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repl: handle unexpected error objects #12400

Closed
wants to merge 1 commit into from
Closed

Conversation

cjihrig
Copy link
Contributor

@cjihrig cjihrig commented Apr 13, 2017

This commit allows the repl's domain error handler to process
unexpected error formats, such as primitives.

Fixes: #12373

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

repl

@nodejs-github-bot nodejs-github-bot added the repl Issues and PRs related to the REPL subsystem. label Apr 13, 2017
}
top.outputStream.write((e.stack || e) + '\n');

top.outputStream.write(out + '\n');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will fail if e is a Symbol, won't it?

@@ -282,17 +282,24 @@ function REPLServer(prompt,
self._domain.on('error', function debugDomainError(e) {
debug('domain error');
const top = replMap.get(self);
var out;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't moved over to let completely yet.

lib/repl.js Outdated

if (!((e instanceof Error) && typeof e.stack === 'string')) {
out = e;
} else if (e instanceof SyntaxError) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there a chance e.stack != true || !('replace' in e.stack)?
Maybe a custom error:

function MySillyError(a, b, c) { console.log(a) }
MySillyError.prototype = SyntaxError

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to jump through hoops for extreme edge cases like this. As long as they don't crash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, just tested, no crash just TypeError: Function.prototype.toString is not generic

}
top.outputStream.write((e.stack || e) + '\n');

top.outputStream.write(out + '\n');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about if delete e.toString?

client: client_unix,
send: 'var e = new Error(\'test error\'); delete e.stack; throw e;',
expect: /^Error: test error/
},
Copy link
Contributor

@refack refack Apr 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add this monstrosity:

send: 'function MySillyError() {}; MySillyError.prototype = SyntaxError; var e = new MySillyError(); throw e',
expect: /^TypeError: Function.prototype.toString is not generic/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No crash, but INHO it's a good test
also @aqrln's

send: 'throw Symbol.for',
expect: /^function for() { [native code] }/

@refack
Copy link
Contributor

refack commented Apr 13, 2017

@cjihrig you're not a first time contributor 😁 kudos on the commitment...

I added some horrible code to try and crash your cases...

lib/repl.js Outdated
if (e instanceof SyntaxError && e.stack) {

if (!((e instanceof Error) && typeof e.stack === 'string')) {
out = e;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symbols and other bad inputs are taken care of here if I move to out = util.inspect(e);. However, it causes test-repl-sigint.js and test-repl-sigint-nested-eval.js to fail. @addaleax any idea why that would be?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that’s odd… I’ll have a look

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@addaleax I just pushed up the changes that include that and more tests, if you want to take a look.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cjihrig I think this might be the missing puzzle piece. ;)

Copy link
Contributor

@refack refack Apr 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway I would "de-morgan" it to:

(!(e instanceof Error) || typeof e.stack !== 'string')

then add the original gourd🎃 guard

(typeof e === 'undefined' || !(e instanceof Error) || typeof e.stack !== 'string')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @addaleax. The special snowflake that is the REPL. I added a length check for stack, so errors with empty stacks will print with util.inspect().

@refack
Copy link
Contributor

refack commented Apr 13, 2017

mo' tests less trouble?!

@refack
Copy link
Contributor

refack commented Apr 13, 2017

You could add the extreme edge cases as tests

send: 'function MySillyError() {}; MySillyError.prototype = SyntaxError; var e = new MySillyError(); throw e',
expect: /^TypeError: Function.prototype.toString is not generic/

also @aqrln's

send: 'throw Symbol.for',
expect: /^function for() { [native code] }/

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM and improve current status quo.

This commit allows the repl's domain error handler to process
unexpected error formats, such as primitives.
// Throws Object with bad toString() method and prints out
{
client: client_unix,
send: 'var e = { toString() { throw new Error(\'test\'); } }; throw e;',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even smarter! 👍

@cjihrig cjihrig added the semver-major PRs that contain breaking changes and should be released in the next major version. label Apr 14, 2017
if (e instanceof SyntaxError && e.stack) {

if (!((e instanceof Error) && typeof e.stack === 'string' &&
e.stack.length > 0)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how robust you want this code to be, but the typeof e.stack will crash with

function FakeError() {}
FakeError.prototype = Object.create(Error.prototype, {
  stack: { get() { throw new Error(); } }
});
var e = new FakeError();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how others feel, but I'm OK with it, at least in the context of this PR. It seems like any Node application can be crashed with a well crafted getter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that I would (personally) prefer is either using V8’s IsNativeError over instanceof Error or going full 🦆 here. Both would increase robustness in different ways :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting that whatever is done here would need to factor in compatibility with internal/errors.js

cjihrig added a commit to cjihrig/node that referenced this pull request Apr 25, 2017
Refs: nodejs#12400
PR-URL: nodejs#12546
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
evanlucas pushed a commit that referenced this pull request Apr 25, 2017
Refs: #12400
PR-URL: #12546
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
evanlucas pushed a commit that referenced this pull request May 1, 2017
Refs: #12400
PR-URL: #12546
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
evanlucas pushed a commit that referenced this pull request May 2, 2017
Refs: #12400
PR-URL: #12546
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@cjihrig cjihrig closed this May 17, 2017
@addaleax
Copy link
Member

@cjihrig Did you mean to close this?

@cjihrig
Copy link
Contributor Author

cjihrig commented Jun 6, 2017

Sorry @addaleax. Yes, I did. The move to internal/errors.js is going to prevent IsNativeError() from working correctly, and I didn't want to go full on duck typing.

@TimothyGu
Copy link
Member

@cjihrig

The move to internal/errors.js is going to prevent IsNativeError() from working correctly

Can you elaborate? The Node.js error classes all extend from the native error classes, and generally V8's methods recognize subclasses.

@cjihrig
Copy link
Contributor Author

cjihrig commented Jun 19, 2017

Hm, good question. I tested this before, but maybe I did something wrong. process.binding('util').isNativeError() does appear to work with internal/errors.

@TimothyGu TimothyGu mentioned this pull request Jul 18, 2017
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
repl Issues and PRs related to the REPL subsystem. semver-major PRs that contain breaking changes and should be released in the next major version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants