-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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: Malloc/Calloc size 0 returns non-null pointer #8572
Conversation
I should point out that there's also a behavior change here, since before the introduction of |
Yes, that's correct. I didn't consider that. Thanks for pointing it out. I wonder if the best path is to revert a00ccb0 in the 6.x line and release 6.6.1 soon. Meanwhile, leave a00ccb0 along with this change in 7.x. And of course review the other instances of nullptr checks... |
@@ -5263,7 +5263,7 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) { | |||
THROW_AND_RETURN_IF_NOT_BUFFER(args[1], "Salt"); | |||
|
|||
pass = static_cast<char*>(node::Malloc(passlen)); | |||
if (pass == nullptr) { | |||
if (pass == nullptr && passlen > 0) { | |||
FatalError("node::PBKDF2()", "Out of Memory"); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (passlen > 0 && pass = static_cast<char*>(node::Malloc(passlen))) {
if (pass == nullptr) {
FatalError("node::PBKDF2()", "Out of Memory");
}
}
If passlen
is 0, node::Malloc()
could be skipped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The if (pass == nullptr)
branch would never execute since you're already testing the "truthiness" of pass
up above. Besides, I'm not sure that the "assignment and test" inside a conditional is really encouraged in node core (at least in C++).
At any rate, I don't see anything wrong in doing it the way it's currently being done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mscdex The below block seems to be more clear as you concerned.
if (passlen > 0) {
pass = static_cast<char*>(node::Malloc(passlen));
if (pass == nullptr) {
FatalError("node::PBKDF2()", "Out of Memory");
}
}
And for this question,
At any rate, I don't see anything wrong in doing it the way it's currently being done.
Yes, the current being done is not wrong in anything, this is just a suggestion, when passlen
is 0 or less than that, we could decrease calls to node::Malloc()
by what I did propose :)
But I'm not sure if the compiler will optimize this or this is valuable to make this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One benefit to calling node::Malloc()
is that you're sure to either get a null or valid pointer. In most places we probably initialize the variable storing the allocated memory to nullptr
, but in a way node::Malloc()
could be seen as an additional safety precaution (in case the variable should be uninitialized or set to some other previous pointer)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mscdex Thanks for your detailed explanation, after I did read src/util-inl.h#L232-L245, I would think should we actually return a specific unique pointer for zero-sized allocation requests to replace the current one? Thus we don't need any changes on our previous check by nullptr
.
Introducing an extra normalization to return nullptr
is creating a conflicts with our previous checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that too and that could be useful, but I'm not sure how everyone else would feel about it.
The code that it replaced was broken anyway because it relied on implementation-specific behavior. If you are worried about regressions, a better fix is to avoid zero-sized allocations like this: diff --git a/src/util-inl.h b/src/util-inl.h
index 5644ee9..31411bb 100644
--- a/src/util-inl.h
+++ b/src/util-inl.h
@@ -246,11 +246,13 @@ void* Realloc(void* pointer, size_t size) {
// As per spec realloc behaves like malloc if passed nullptr.
void* Malloc(size_t size) {
+ if (size == 0) size = 1;
return Realloc(nullptr, size);
}
void* Calloc(size_t n, size_t size) {
- if ((n == 0) || (size == 0)) return nullptr;
+ if (n == 0) n = 1;
+ if (size == 0) size = 1;
CHECK_GE(n * size, n); // Overflow guard.
return calloc(n, size);
} |
I think I like @bnoordhuis's suggestion above best. That would unblock #8482 as well? Or not quite? |
Updated with @bnoordhuis's suggestion for a more complete fix. The test added here works in 6.5.0, fails in 6.6.0, and passes again with the change here, all as expected. PTAL /cc @mhdawson So that's a fix for the 6.x line. I guess the next question is whether we want behavior to be unchanged in 7.x or if we want the crypto library to emit an error event in this situation. If there's no compelling reason to make a breaking change like that in v7, then I guess this is good for both lines... |
By the way, is there an existing reasonable place to drop a C++ test that calls |
@Trott Shouldn't we open another pull-request to change |
@yorkie The only thing I see that's optional is the |
Removed superfluous style changes, edited commit message, squashed, force pushed. PTAL. |
@Trott What I did mean is that you did write in the commit message as:
This change seems a side-effect and it'd better to land in another PR? Of course this is a friendly suggestion, both are reasonable :) |
test/cctest/util.cc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change seems a side-effect and it'd better to land in another PR? Of course this is a friendly suggestion, both are reasonable :)
Maybe just split into two commits? And don’t worry about #8482, I’ll rebase on whatever comes out of this change.
What does everyone think about just returning a static 1-byte sized buffer on request of a zero-length chunk of memory? That way we definitely only allocate a 1-byte sized buffer once. |
@mscdex That’s basically @yorkie’s idea from above, right? I’d still be on board with that. |
@addaleax Basically, yes. |
If someone else wants to take over this PR to add the "only allocate a pointer once" business (file a separate PR or push a change to this branch or whatever), that would be great. (Feel free to just take any of the 12 lines of code here that's useful to you, nearly half of which I didn't write anyway. :-D ) |
Now that I think about it, there would be more work involved if we used a static buffer to make sure no call to |
I submitted the alternate solution in #8658. |
+1 on @mscdex's patch :) |
I'm going to close this in favor of #8658. If anyone thinks that's a mistake for whatever reason, feel free to comment or re-open. |
Still LGTM! :) |
Sorry for the late response, just catching up from being away. LGTM from me. |
Change `Malloc()/Calloc()` so that size zero does not return a null pointer, consistent with prior behavior. Fixes: nodejs#8571 PR-URL: nodejs#8572 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Yorkie Liu <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
Landed in d2eb7ce |
Change `Malloc()/Calloc()` so that size zero does not return a null pointer, consistent with prior behavior. Fixes: #8571 PR-URL: #8572 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Yorkie Liu <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
Change `Malloc()/Calloc()` so that size zero does not return a null pointer, consistent with prior behavior. Fixes: #8571 PR-URL: #8572 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Yorkie Liu <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
This is a security release. All Node.js users should consult the security release summary at https://nodejs.org/en/blog/vulnerability/september-2016-security-releases/ for details on patched vulnerabilities. Notable Changes Semver Minor: * openssl: - Upgrade to 1.0.2i, fixes a number of defects impacting Node.js: CVE-2016-6304 ("OCSP Status Request extension unbounded memory growth", high severity), CVE-2016-2183, CVE-2016-2178, and CVE-2016-6306. (Shigeki Ohtsu) #8714 - Upgrade to 1.0.2j, fixes a defect included in 1.0.2i resulting in a crash when using CRLs, CVE-2016-7052. (Shigeki Ohtsu) #8786 - Remove support for loading dynamic third-party engine modules. An attacker may be able to hide malicious code to be inserted into Node.js at runtime by masquerading as one of the dynamic engine modules. Originally reported by Ahmed Zaki (Skype). (Ben Noordhuis) https://github.com/nodejs/node-private/pull/73 * http: CVE-2016-5325 - Properly validate for allowable characters in the `reason` argument in `ServerResponse#writeHead()`. Fixes a possible response splitting attack vector. This introduces a new case where `throw` may occur when configuring HTTP responses, users should already be adopting try/catch here. Originally reported independently by Evan Lucas and Romain Gaucher. (Evan Lucas) https://github.com/nodejs/node-private/pull/60 Semver Patch: * buffer: Zero-fill excess bytes in new `Buffer` objects created with `Buffer.concat()` while providing a `totalLength` parameter that exceeds the total length of the original `Buffer` objects being concatenated. (Сковорода Никита Андреевич) https://github.com/nodejs/node-private/pull/64 * src: Fix regression where passing an empty password and/or salt to crypto.pbkdf2() would cause a fatal error (Rich Trott) #8572 * tls: CVE-2016-7099 - Fix invalid wildcard certificate validation check whereby a TLS server may be able to serve an invalid wildcard certificate for its hostname due to improper validation of `*.` in the wildcard string. Originally reported by Alexander Minozhenko and James Bunton (Atlassian). (Ben Noordhuis) https://github.com/nodejs/node-private/pull/75 * v8: Fix regression where a regex on a frozen object was broken (Myles Borins) #8673
This is a security release. All Node.js users should consult the security release summary at https://nodejs.org/en/blog/vulnerability/september-2016-security-releases/ for details on patched vulnerabilities. Notable Changes Semver Minor: * openssl: - Upgrade to 1.0.2i, fixes a number of defects impacting Node.js: CVE-2016-6304 ("OCSP Status Request extension unbounded memory growth", high severity), CVE-2016-2183, CVE-2016-2178, and CVE-2016-6306. (Shigeki Ohtsu) nodejs/node#8714 - Upgrade to 1.0.2j, fixes a defect included in 1.0.2i resulting in a crash when using CRLs, CVE-2016-7052. (Shigeki Ohtsu) nodejs/node#8786 - Remove support for loading dynamic third-party engine modules. An attacker may be able to hide malicious code to be inserted into Node.js at runtime by masquerading as one of the dynamic engine modules. Originally reported by Ahmed Zaki (Skype). (Ben Noordhuis) https://github.com/nodejs/node-private/pull/73 * http: CVE-2016-5325 - Properly validate for allowable characters in the `reason` argument in `ServerResponse#writeHead()`. Fixes a possible response splitting attack vector. This introduces a new case where `throw` may occur when configuring HTTP responses, users should already be adopting try/catch here. Originally reported independently by Evan Lucas and Romain Gaucher. (Evan Lucas) https://github.com/nodejs/node-private/pull/60 Semver Patch: * buffer: Zero-fill excess bytes in new `Buffer` objects created with `Buffer.concat()` while providing a `totalLength` parameter that exceeds the total length of the original `Buffer` objects being concatenated. https://github.com/nodejs/node-private/pull/64 * src: Fix regression where passing an empty password and/or salt to crypto.pbkdf2() would cause a fatal error (Rich Trott) nodejs/node#8572 * tls: CVE-2016-7099 - Fix invalid wildcard certificate validation check whereby a TLS server may be able to serve an invalid wildcard certificate for its hostname due to improper validation of `*.` in the wildcard string. Originally reported by Alexander Minozhenko and James Bunton (Atlassian). (Ben Noordhuis) https://github.com/nodejs/node-private/pull/75 * v8: Fix regression where a regex on a frozen object was broken (Myles Borins) nodejs/node#8673 Signed-off-by: Ilkka Myller <[email protected]>
This is a security release. All Node.js users should consult the security release summary at https://nodejs.org/en/blog/vulnerability/september-2016-security-releases/ for details on patched vulnerabilities. Notable Changes Semver Minor: * openssl: - Upgrade to 1.0.2i, fixes a number of defects impacting Node.js: CVE-2016-6304 ("OCSP Status Request extension unbounded memory growth", high severity), CVE-2016-2183, CVE-2016-2178, and CVE-2016-6306. (Shigeki Ohtsu) nodejs/node#8714 - Upgrade to 1.0.2j, fixes a defect included in 1.0.2i resulting in a crash when using CRLs, CVE-2016-7052. (Shigeki Ohtsu) nodejs/node#8786 - Remove support for loading dynamic third-party engine modules. An attacker may be able to hide malicious code to be inserted into Node.js at runtime by masquerading as one of the dynamic engine modules. Originally reported by Ahmed Zaki (Skype). (Ben Noordhuis) https://github.com/nodejs/node-private/pull/73 * http: CVE-2016-5325 - Properly validate for allowable characters in the `reason` argument in `ServerResponse#writeHead()`. Fixes a possible response splitting attack vector. This introduces a new case where `throw` may occur when configuring HTTP responses, users should already be adopting try/catch here. Originally reported independently by Evan Lucas and Romain Gaucher. (Evan Lucas) https://github.com/nodejs/node-private/pull/60 Semver Patch: * buffer: Zero-fill excess bytes in new `Buffer` objects created with `Buffer.concat()` while providing a `totalLength` parameter that exceeds the total length of the original `Buffer` objects being concatenated. https://github.com/nodejs/node-private/pull/64 * src: Fix regression where passing an empty password and/or salt to crypto.pbkdf2() would cause a fatal error (Rich Trott) nodejs/node#8572 * tls: CVE-2016-7099 - Fix invalid wildcard certificate validation check whereby a TLS server may be able to serve an invalid wildcard certificate for its hostname due to improper validation of `*.` in the wildcard string. Originally reported by Alexander Minozhenko and James Bunton (Atlassian). (Ben Noordhuis) https://github.com/nodejs/node-private/pull/75 * v8: Fix regression where a regex on a frozen object was broken (Myles Borins) nodejs/node#8673 Signed-off-by: Ilkka Myller <[email protected]>
Change `Malloc()/Calloc()` so that size zero does not return a null pointer, consistent with prior behavior. Fixes: #8571 PR-URL: #8572 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Yorkie Liu <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
This is a security release. All Node.js users should consult the security release summary at https://nodejs.org/en/blog/vulnerability/september-2016-security-releases/ for details on patched vulnerabilities. Notable Changes Semver Minor: * openssl: - Upgrade to 1.0.2i, fixes a number of defects impacting Node.js: CVE-2016-6304 ("OCSP Status Request extension unbounded memory growth", high severity), CVE-2016-2183, CVE-2016-2178, and CVE-2016-6306. (Shigeki Ohtsu) #8714 - Upgrade to 1.0.2j, fixes a defect included in 1.0.2i resulting in a crash when using CRLs, CVE-2016-7052. (Shigeki Ohtsu) #8786 - Remove support for loading dynamic third-party engine modules. An attacker may be able to hide malicious code to be inserted into Node.js at runtime by masquerading as one of the dynamic engine modules. Originally reported by Ahmed Zaki (Skype). (Ben Noordhuis) nodejs-private/node-private#73 * http: CVE-2016-5325 - Properly validate for allowable characters in the `reason` argument in `ServerResponse#writeHead()`. Fixes a possible response splitting attack vector. This introduces a new case where `throw` may occur when configuring HTTP responses, users should already be adopting try/catch here. Originally reported independently by Evan Lucas and Romain Gaucher. (Evan Lucas) nodejs-private/node-private#60 Semver Patch: * buffer: Zero-fill excess bytes in new `Buffer` objects created with `Buffer.concat()` while providing a `totalLength` parameter that exceeds the total length of the original `Buffer` objects being concatenated. (Сковорода Никита Андреевич) nodejs-private/node-private#64 * src: Fix regression where passing an empty password and/or salt to crypto.pbkdf2() would cause a fatal error (Rich Trott) #8572 * tls: CVE-2016-7099 - Fix invalid wildcard certificate validation check whereby a TLS server may be able to serve an invalid wildcard certificate for its hostname due to improper validation of `*.` in the wildcard string. Originally reported by Alexander Minozhenko and James Bunton (Atlassian). (Ben Noordhuis) nodejs-private/node-private#75 * v8: Fix regression where a regex on a frozen object was broken (Myles Borins) #8673
Change `Malloc()/Calloc()` so that size zero does not return a null pointer, consistent with prior behavior. Fixes: nodejs#8571 PR-URL: nodejs#8572 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Yorkie Liu <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
Change `Malloc()/Calloc()` so that size zero does not return a null pointer, consistent with prior behavior. Fixes: #8571 PR-URL: #8572 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Yorkie Liu <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
Assuming that UncheckedMalloc(0) returns a non-nullptr is non-standard and we use other allocators as well (e.g., OPENSSL_malloc) that do not guarantee this behavior. It is the caller's responsibility to check that size != 0 implies UncheckedMalloc(size) != nullptr, and that's exactly what the checked variants (Malloc etc.) already do. The current behavior is also inconsistent with UncheckedRealloc(), which always returns a nullptr when the size is 0, and with the documentation in src/README.md. Refs: nodejs#8571 Refs: nodejs#8572
Assuming that UncheckedMalloc(0) returns a non-nullptr is non-standard and we use other allocators as well (e.g., OPENSSL_malloc) that do not guarantee this behavior. It is the caller's responsibility to check that size != 0 implies UncheckedMalloc(size) != nullptr, and that's exactly what the checked variants (Malloc etc.) already do. The current behavior is also inconsistent with UncheckedRealloc(), which always returns a nullptr when the size is 0, and with the documentation in src/README.md. Refs: nodejs#8571 Refs: nodejs#8572
Assuming that UncheckedMalloc(0) returns a non-nullptr is non-standard and we use other allocators as well (e.g., OPENSSL_malloc) that do not guarantee this behavior. It is the caller's responsibility to check that size != 0 implies UncheckedMalloc(size) != nullptr, and that's exactly what the checked variants (Malloc etc.) already do. The current behavior is also inconsistent with UncheckedRealloc(), which always returns a nullptr when the size is 0, and with the documentation in src/README.md. Refs: nodejs#8571 Refs: nodejs#8572
Assuming that UncheckedMalloc(0) returns a non-nullptr is non-standard and we use other allocators as well (e.g., OPENSSL_malloc) that do not guarantee this behavior. It is the caller's responsibility to check that size != 0 implies UncheckedMalloc(size) != nullptr, and that's exactly what the checked variants (Malloc etc.) already do. The current behavior is also inconsistent with UncheckedRealloc(), which always returns a nullptr when the size is 0, and with the documentation in src/README.md as well as with multiple comments in the source code. This changes UncheckedMalloc(), UncheckedCalloc(), and UncheckedRealloc() to always return a nullptr when the size is 0 instead of doing fake allocations in UncheckedMalloc() and UncheckedCalloc() while returning a nullptr from UncheckedRealloc(). This is consistent with existing documentation and comments. Refs: nodejs#8571 Refs: nodejs#8572
Assuming that UncheckedMalloc(0) returns a non-nullptr is non-standard and we use other allocators as well (e.g., OPENSSL_malloc) that do not guarantee this behavior. It is the caller's responsibility to check that size != 0 implies UncheckedMalloc(size) != nullptr, and that's exactly what the checked variants (Malloc etc.) already do. The current behavior is also inconsistent with UncheckedRealloc(), which always returns a nullptr when the size is 0, and with the documentation in src/README.md as well as with multiple comments in the source code. This changes UncheckedMalloc(), UncheckedCalloc(), and UncheckedRealloc() to always return a nullptr when the size is 0 instead of doing fake allocations in UncheckedMalloc() and UncheckedCalloc() while returning a nullptr from UncheckedRealloc(). This is consistent with existing documentation and comments. Refs: #8571 Refs: #8572 PR-URL: #44543 Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: James M Snell <[email protected]>
Assuming that UncheckedMalloc(0) returns a non-nullptr is non-standard and we use other allocators as well (e.g., OPENSSL_malloc) that do not guarantee this behavior. It is the caller's responsibility to check that size != 0 implies UncheckedMalloc(size) != nullptr, and that's exactly what the checked variants (Malloc etc.) already do. The current behavior is also inconsistent with UncheckedRealloc(), which always returns a nullptr when the size is 0, and with the documentation in src/README.md as well as with multiple comments in the source code. This changes UncheckedMalloc(), UncheckedCalloc(), and UncheckedRealloc() to always return a nullptr when the size is 0 instead of doing fake allocations in UncheckedMalloc() and UncheckedCalloc() while returning a nullptr from UncheckedRealloc(). This is consistent with existing documentation and comments. Refs: #8571 Refs: #8572 PR-URL: #44543 Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: James M Snell <[email protected]>
Checklist
make -j4 test
(UNIX), orvcbuild test nosign
(Windows) passesAffected core subsystem(s)
crypto
Description of change
crypto.pbkdf2() with empty password and/or salt causes a fatal error in
Node.js 6.6.0. It did not in 6.5.0. The problematic change is
a00ccb0. We still need to review other
changes in that change set, but this is a test and fix for the specific
issue reported in #8571.
The problem is that
malloc(0)
may return NULL on some platforms. Sodo not report out-of-memory error unless
malloc
was passed a numbergreater than
0
.Fixes: #8571