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

src: no abort from GetFD if object isn't wrapped #6184

Merged
merged 5 commits into from
May 24, 2016

Conversation

trevnorris
Copy link
Contributor

Checklist
  • tests and code linting passes
  • a test and/or benchmark is included
  • the commit message follows commit guidelines
Affected core subsystem(s)

stream_base, base_object

Description of change

Previously the 0-index slot from
v8::Object::GetAlignedPointerFromInternalField() would return random
data if Wrap() hadn't been run on the object handle. Now immediately set
the slot to nullptr so we can know for sure whether it's been set or
not. Use this information to return -1 from GetFD() instead of aborting.

R=@bnoordhuis
R=@indutny

CI: https://ci.nodejs.org/job/node-test-pull-request/2254/

@trevnorris trevnorris added the c++ Issues and PRs that require attention from people who are familiar with C++. label Apr 13, 2016
});
});

dgram.createSocket('udp4').close(checkTCP);
Copy link
Member

Choose a reason for hiding this comment

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

Fun!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just curious. What do you mean by "Fun"?

@bnoordhuis
Copy link
Member

LGTM but Fedor has a point that there are probably more places where the value of wrap should be checked.

@jasnell
Copy link
Member

jasnell commented Apr 15, 2016

LGTM. +1 on seeing if this can hit the other places too.

@estliberitas estliberitas force-pushed the master branch 2 times, most recently from 7da4fd4 to c7066fb Compare April 26, 2016 05:23
@jasnell
Copy link
Member

jasnell commented Apr 28, 2016

@trevnorris
Copy link
Contributor Author

@bnoordhuis @indutny I've gone through and made it so we always check the return value of Unwrap() before accessing it. Usually performing an early return. Where it looked necessary I've tried to set the appropriate default return value without ever throwing an error.

CI: https://ci.nodejs.org/job/node-test-pull-request/2503/

JSStream* wrap = Unwrap<JSStream>(args.Holder());
WriteWrap* w = Unwrap<WriteWrap>(args[0].As<Object>());
JSStream* wrap;
// TODO(trevnorris): Verify that args[0] is actually an Object.
Copy link
Member

Choose a reason for hiding this comment

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

Why leave this as a todo?

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 wanted feedback on whether it should throw or just return early.

Copy link
Member

Choose a reason for hiding this comment

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

I'd add a CHECK. If it's not an object, there's a logic error in node somewhere, right?

@bnoordhuis
Copy link
Member

Left some comments. I do wonder if this approach isn't too blunt. I see a lot places where a nullptr wrap object is arguably an abort-worthy bug.

@trevnorris
Copy link
Contributor Author

@bnoordhuis

Left some comments. I do wonder if this approach isn't too blunt. I see a lot places where a nullptr wrap object is arguably an abort-worthy bug.

I don't disagree here. Originally all I wanted to make sure of is that it would be impossible to abort the process if inspecting the handle at any point in it's lifetime. Similar to how I originally wanted to prevent abort from the C++ getters. And, lazily, it was easier to prevent abort from every unwrap instead of reason about each location. In the hopes that this would highlight every location where it could abort, and from there we could identify which places should definitely abort.

@trevnorris
Copy link
Contributor Author

@bnoordhuis Everything's been fixed. This was a blunt approach to the problem, and I'm fine going back through and being more decisive about when we choose to abort.

@trevnorris
Copy link
Contributor Author

ping @bnoordhuis

@addaleax mind giving this a once over as well?

@@ -14,6 +14,10 @@ inline BaseObject::BaseObject(Environment* env, v8::Local<v8::Object> handle)
: handle_(env->isolate(), handle),
env_(env) {
CHECK_EQ(false, handle.IsEmpty());
// The zero field holds a pointer to the handle. Immediately set it to
// nullptr in case it's accessed by the user before contruction is complete.
Copy link
Member

Choose a reason for hiding this comment

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

nit: construction

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 for catching this.

@addaleax
Copy link
Member

I don’t know yet whether I have a strong opinion on aborting in some of these cases rather than returning silently, but I’ll sleep on that. And this LGTM either way… anything is better than segfaulting. ;)

@trevnorris
Copy link
Contributor Author

@bnoordhuis I can make a best effort at judging which locations where nullptr should cause the application to abort. As an example, I'm thinking TLSWrap would be one, since the constructor is only ever called from C++.

ContextifyScript* wrapped_script;
ASSIGN_OR_RETURN_UNWRAP(&wrapped_script,
args.Holder(),
false);
Copy link
Member

Choose a reason for hiding this comment

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

Fits on a single line.

@MylesBorins
Copy link
Contributor

@trevnorris should this be included with the other async wrap changes?

@trevnorris
Copy link
Contributor Author

@thealphanerd Yes.

@MylesBorins
Copy link
Contributor

@trevnorris it looks like this will have to be manually backported, will you be able to help with that?

@trevnorris
Copy link
Contributor Author

@thealphanerd sure. I can do that. When would you need it by?

@MylesBorins
Copy link
Contributor

@trevnorris there is no rush. But if it is in sooner it can make the cut for v4.5.0

@MylesBorins MylesBorins added this to the v4.6.2 milestone Oct 24, 2016
@MylesBorins
Copy link
Contributor

ping @trevnorris would like to include this in v4.6.2

@bnoordhuis
Copy link
Member

I'll take this.

bnoordhuis pushed a commit to bnoordhuis/io.js that referenced this pull request Oct 24, 2016
To make sure casting a class of multiple inheritance from a void* to
AsyncWrap succeeds make AsyncWrap the first inherited class.

PR-URL: nodejs#6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
bnoordhuis pushed a commit to bnoordhuis/io.js that referenced this pull request Oct 24, 2016
In case the handle is stored and accessed after the associated C++ class
was destructed, set the internal pointer to nullptr so any
getters/setters can return accordingly without aborting the application.

PR-URL: nodejs#6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
bnoordhuis pushed a commit to bnoordhuis/io.js that referenced this pull request Oct 24, 2016
Though the TLSWrap constructor is only called via TLSWrap::Wrap() (i.e.
tls_wrap.wrap()) internally, it is still exposed to JS. Don't allow the
application to abort by inspecting the instance before it has been
wrap'd by another handle.

PR-URL: nodejs#6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
bnoordhuis pushed a commit to bnoordhuis/io.js that referenced this pull request Oct 24, 2016
First cast the pointer to the child Base class before casting to the
parent class to make sure it returns the correct pointer.

PR-URL: nodejs#6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
bnoordhuis pushed a commit to bnoordhuis/io.js that referenced this pull request Oct 24, 2016
v8::Object::GetAlignedPointerFromInternalField() returns a random value
if Wrap() hasn't been run on the object handle. Causing v8 to abort if
certain getters are accessed. It's possible to access these getters and
functions during class construction through the AsyncWrap init()
callback, and also possible in a subset of those scenarios while running
the persistent handle visitor.

Mitigate this issue by manually setting the internal aligned pointer
field to nullptr in the BaseObject constructor and add necessary logic
to return appropriate values when nullptr is encountered.

PR-URL: nodejs#6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
@bnoordhuis
Copy link
Member

#9260

MylesBorins pushed a commit that referenced this pull request Oct 25, 2016
To make sure casting a class of multiple inheritance from a void* to
AsyncWrap succeeds make AsyncWrap the first inherited class.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 25, 2016
In case the handle is stored and accessed after the associated C++ class
was destructed, set the internal pointer to nullptr so any
getters/setters can return accordingly without aborting the application.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 25, 2016
Though the TLSWrap constructor is only called via TLSWrap::Wrap() (i.e.
tls_wrap.wrap()) internally, it is still exposed to JS. Don't allow the
application to abort by inspecting the instance before it has been
wrap'd by another handle.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 25, 2016
First cast the pointer to the child Base class before casting to the
parent class to make sure it returns the correct pointer.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 25, 2016
v8::Object::GetAlignedPointerFromInternalField() returns a random value
if Wrap() hasn't been run on the object handle. Causing v8 to abort if
certain getters are accessed. It's possible to access these getters and
functions during class construction through the AsyncWrap init()
callback, and also possible in a subset of those scenarios while running
the persistent handle visitor.

Mitigate this issue by manually setting the internal aligned pointer
field to nullptr in the BaseObject constructor and add necessary logic
to return appropriate values when nullptr is encountered.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 26, 2016
To make sure casting a class of multiple inheritance from a void* to
AsyncWrap succeeds make AsyncWrap the first inherited class.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 26, 2016
In case the handle is stored and accessed after the associated C++ class
was destructed, set the internal pointer to nullptr so any
getters/setters can return accordingly without aborting the application.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 26, 2016
Though the TLSWrap constructor is only called via TLSWrap::Wrap() (i.e.
tls_wrap.wrap()) internally, it is still exposed to JS. Don't allow the
application to abort by inspecting the instance before it has been
wrap'd by another handle.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 26, 2016
First cast the pointer to the child Base class before casting to the
parent class to make sure it returns the correct pointer.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
MylesBorins pushed a commit that referenced this pull request Oct 26, 2016
v8::Object::GetAlignedPointerFromInternalField() returns a random value
if Wrap() hasn't been run on the object handle. Causing v8 to abort if
certain getters are accessed. It's possible to access these getters and
functions during class construction through the AsyncWrap init()
callback, and also possible in a subset of those scenarios while running
the persistent handle visitor.

Mitigate this issue by manually setting the internal aligned pointer
field to nullptr in the BaseObject constructor and add necessary logic
to return appropriate values when nullptr is encountered.

PR-URL: #6184
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
@MylesBorins MylesBorins mentioned this pull request Oct 26, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants