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

Piping streams into SHA3 without end: false crashes #28245

Closed
tniessen opened this issue Jun 15, 2019 · 2 comments
Closed

Piping streams into SHA3 without end: false crashes #28245

tniessen opened this issue Jun 15, 2019 · 2 comments
Labels
confirmed-bug Issues with confirmed bugs. crypto Issues and PRs related to the crypto subsystem. openssl Issues and PRs related to the OpenSSL dependency. stream Issues and PRs related to the stream subsystem.

Comments

@tniessen
Copy link
Member

tniessen commented Jun 15, 2019

  • Version: 12.4.0
  • Platform: Windows 10 x64 and Linux 4.4.0-042stab138.1
  • Subsystem: crypto or deps

This code causes a segmentation fault in 12.4.0:

const crypto = require('crypto');
const fs = require('fs');

const myOwnCode = fs.createReadStream(__filename);
const copy = fs.createWriteStream(`${__filename}.copy`);
const hash = crypto.createHash('sha3-512');
myOwnCode.pipe(hash);
myOwnCode.pipe(copy).on('finish', () => {
  hash.digest();
});

This seems to be caused by pipe calling hash._flush when end is not set to false. This code also causes a segmentation fault:

const crypto = require('crypto');

const hash = crypto.createHash('sha3-512');
hash._flush(() => console.log('Flushed'));
hash.digest();

This seems to be at least partially caused by the implementation of _flush:

Hash.prototype._flush = function _flush(callback) {
this.push(this[kHandle].digest());
callback();
};

It bypasses the this[kState][kFinalized] safeguard:

Hash.prototype.digest = function digest(outputEncoding) {
const state = this[kState];
if (state[kFinalized])
throw new ERR_CRYPTO_HASH_FINALIZED();
outputEncoding = outputEncoding || getDefaultEncoding();
if (normalizeEncoding(outputEncoding) === 'utf16le')
throw new ERR_CRYPTO_HASH_DIGEST_NO_UTF16();
// Explicit conversion for backward compatibility.
const ret = this[kHandle].digest(`${outputEncoding}`);
state[kFinalized] = true;
return ret;
};

Note that this bug only happens when using SHA3, sha256 seems to be working just fine, so there might also be some weirdness in OpenSSL.

cc @mcollina @nodejs/crypto @nodejs/streams

@tniessen tniessen added crypto Issues and PRs related to the crypto subsystem. openssl Issues and PRs related to the OpenSSL dependency. stream Issues and PRs related to the stream subsystem. confirmed-bug Issues with confirmed bugs. labels Jun 15, 2019
@tniessen
Copy link
Member Author

tniessen commented Jun 15, 2019

Stack trace:

Thread 1 "node" received signal SIGSEGV, Segmentation fault.
0x00000000016a11ad in SHA3_absorb ()
(gdb) bt
#0  0x00000000016a11ad in SHA3_absorb ()
#1  0x00000000015ae107 in sha3_final ()
#2  0x0000000001593d3f in EVP_DigestFinal_ex ()
#3  0x0000000000a87230 in node::crypto::Hash::HashDigest(v8::FunctionCallbackInfo<v8::Value> const&) ()
#4  0x0000000000b9cae6 in v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) ()
#5  0x0000000000b9e9f9 in v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) ()
#6  0x0000000001a93942 in Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit () at ../../deps/v8/src/builtins/base.tq:1866
#7  0x00000000019fd584 in Builtins_InterpreterEntryTrampoline () at ../../deps/v8/src/builtins/base.tq:107

@addaleax
Copy link
Member

Valgrind info for the first example:

==4491== Invalid read of size 8
==4491==    at 0x21311C7: SHA3_absorb (keccak1600-x86_64.s:387)
==4491==    by 0x203230D: sha3_final (m_sha3.c:120)
==4491==    by 0x201772E: EVP_DigestFinal_ex (digest.c:171)
==4491==    by 0xE4305D: node::crypto::Hash::HashDigest(v8::FunctionCallbackInfo<v8::Value> const&) (node_crypto.cc:4640)
==4491==    by 0xFB4290: v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) (api-arguments-inl.h:157)
==4491==    by 0xFB7213: v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) (builtins-api.cc:109)
==4491==    by 0xFB8A38: v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) (builtins-api.cc:139)
==4491==    by 0xFB956C: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (builtins-api.cc:127)
==4491==    by 0x1C81D1F: ??? (builtins-async-iterator-gen.cc:256)
==4491==    by 0x1A05334: ??? (builtins-string-gen.cc:1971)
==4491==    by 0x19EF05B: ??? (builtins-internal-gen.cc:700)
==4491==    by 0x1A05334: ??? (builtins-string-gen.cc:1971)
==4491==  Address 0x9ed8dd0 is 0 bytes after a block of size 400 alloc'd
==4491==    at 0x4C2FDFB: malloc (vg_replace_malloc.c:309)
==4491==    by 0x204422F: CRYPTO_malloc (mem.c:222)
==4491==    by 0x2044262: CRYPTO_zalloc (mem.c:230)
==4491==    by 0x20175AB: EVP_DigestInit_ex (digest.c:129)
==4491==    by 0xE42BEF: node::crypto::Hash::HashInit(char const*) (node_crypto.cc:4585)
==4491==    by 0xE42B02: node::crypto::Hash::New(v8::FunctionCallbackInfo<v8::Value> const&) (node_crypto.cc:4573)
==4491==    by 0xFB4290: v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) (api-arguments-inl.h:157)
==4491==    by 0xFB4E6F: v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<true>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) (builtins-api.cc:109)
==4491==    by 0xFB8983: v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) (builtins-api.cc:135)
==4491==    by 0xFB956C: v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) (builtins-api.cc:127)
==4491==    by 0x1C81D1F: ??? (builtins-async-iterator-gen.cc:256)
==4491==    by 0x19F5799: ??? (base.tq:175)

targos pushed a commit that referenced this issue Jul 2, 2019
When piping data into an SHA3 hash, EVP_DigestFinal_ex is called in
hash._flush, bypassing safeguards in the JavaScript layer. Calling
hash.digest causes EVP_DigestFinal_ex to be called again, resulting
in a segmentation fault in the SHA3 implementation of OpenSSL.

A relatively easy solution is to cache the result of calling
EVP_DigestFinal_ex until the Hash object is garbage collected.

PR-URL: #28251
Fixes: #28245
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
evanlucas pushed a commit to evanlucas/node that referenced this issue Feb 4, 2021
When piping data into an SHA3 hash, EVP_DigestFinal_ex is called in
hash._flush, bypassing safeguards in the JavaScript layer. Calling
hash.digest causes EVP_DigestFinal_ex to be called again, resulting
in a segmentation fault in the SHA3 implementation of OpenSSL.

A relatively easy solution is to cache the result of calling
EVP_DigestFinal_ex until the Hash object is garbage collected.

PR-URL: nodejs#28251
Fixes: nodejs#28245
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
richardlau pushed a commit that referenced this issue Feb 5, 2021
When piping data into an SHA3 hash, EVP_DigestFinal_ex is called in
hash._flush, bypassing safeguards in the JavaScript layer. Calling
hash.digest causes EVP_DigestFinal_ex to be called again, resulting
in a segmentation fault in the SHA3 implementation of OpenSSL.

A relatively easy solution is to cache the result of calling
EVP_DigestFinal_ex until the Hash object is garbage collected.

PR-URL: #28251
Fixes: #28245
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>

PR-URL: #37009
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Shelley Vohr <codebytere@gmail.com>
richardlau pushed a commit that referenced this issue Feb 5, 2021
When piping data into an SHA3 hash, EVP_DigestFinal_ex is called in
hash._flush, bypassing safeguards in the JavaScript layer. Calling
hash.digest causes EVP_DigestFinal_ex to be called again, resulting
in a segmentation fault in the SHA3 implementation of OpenSSL.

A relatively easy solution is to cache the result of calling
EVP_DigestFinal_ex until the Hash object is garbage collected.

PR-URL: #28251
Fixes: #28245
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>

Backport-PR-URL: #37009
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Shelley Vohr <codebytere@gmail.com>
richardlau pushed a commit that referenced this issue Feb 8, 2021
When piping data into an SHA3 hash, EVP_DigestFinal_ex is called in
hash._flush, bypassing safeguards in the JavaScript layer. Calling
hash.digest causes EVP_DigestFinal_ex to be called again, resulting
in a segmentation fault in the SHA3 implementation of OpenSSL.

A relatively easy solution is to cache the result of calling
EVP_DigestFinal_ex until the Hash object is garbage collected.

PR-URL: #28251
Fixes: #28245
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>

Backport-PR-URL: #37009
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Shelley Vohr <codebytere@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug Issues with confirmed bugs. crypto Issues and PRs related to the crypto subsystem. openssl Issues and PRs related to the OpenSSL dependency. stream Issues and PRs related to the stream subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants