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,crypto: fix 0-length output crash in webcrypto #38913

Closed
wants to merge 1 commit into from

Conversation

XadillaX
Copy link
Contributor

@XadillaX XadillaX commented Jun 3, 2021

Refs: #38883

@github-actions github-actions bot added c++ Issues and PRs that require attention from people who are familiar with C++. crypto Issues and PRs related to the crypto subsystem. needs-ci PRs that need a full CI run. labels Jun 3, 2021
src/crypto/crypto_cipher.h Outdated Show resolved Hide resolved
@@ -249,7 +249,7 @@ class CipherJob final : public CryptoJob<CipherTraits> {
v8::Local<v8::Value>* result) override {
Environment* env = AsyncWrap::env();
CryptoErrorStore* errors = CryptoJob<CipherTraits>::errors();
if (out_.size() > 0) {
if (out_.size() > 0 || errors->Empty()) {
Copy link
Member

Choose a reason for hiding this comment

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

A few lines below, there's this:

if (errors->Empty())
  errors->Capture();      // this is unreachable now
CHECK(!errors->Empty());

So, either those lines are not needed, or there is no guarantee that errors->IsEmpty() will return false only if no error occurred.

If errors->IsEmpty() is true if and only if the operation succeeded, then out_.size() > 0 is not a required condition.

If errors->IsEmpty() is never true when the operation failed, then the lines below should be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I move if (errors->Empty()) errors->Capture(); before if (out_.size() > 0)?

Copy link
Member

Choose a reason for hiding this comment

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

I think @jasnell might be the best person to ask, but that might be enough to solve the problem. It would make the sanity check CHECK(!errors->Empty()) below useless.

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've moved it above.

/cc @jasnell

Copy link
Member

@tniessen tniessen left a comment

Choose a reason for hiding this comment

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

LGTM but I'd appreciate a review from @jasnell due to the error handling dilemma.

test/parallel/test-crypto-subtle-zero-length.js Outdated Show resolved Hide resolved
@XadillaX
Copy link
Contributor Author

/ping @jasnell

@XadillaX XadillaX requested a review from jasnell June 15, 2021 06:42
@XadillaX
Copy link
Contributor Author

/ping @jasnell

1 similar comment
@XadillaX
Copy link
Contributor Author

/ping @jasnell

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@tniessen
Copy link
Member

Related failure on ubi81_sharedlibs_openssl111fips_x64:

04:49:43 not ok 592 parallel/test-crypto-subtle-zero-length
04:49:43   ---
04:49:43   duration_ms: 0.94
04:49:43   severity: fail
04:49:43   exitcode: 1
04:49:43   stack: |-
04:49:43     node:internal/process/promises:246
04:49:43               triggerUncaughtException(err, true /* fromPromise */);
04:49:43               ^
04:49:43     
04:49:43     [Error: Cipher job failed]
04:49:43   ...

@nodejs nodejs deleted a comment from nodejs-github-bot Jun 22, 2021
@nodejs nodejs deleted a comment from nodejs-github-bot Jun 22, 2021
@nodejs-github-bot
Copy link
Collaborator

@XadillaX
Copy link
Contributor Author

Related failure on ubi81_sharedlibs_openssl111fips_x64:

04:49:43 not ok 592 parallel/test-crypto-subtle-zero-length
04:49:43   ---
04:49:43   duration_ms: 0.94
04:49:43   severity: fail
04:49:43   exitcode: 1
04:49:43   stack: |-
04:49:43     node:internal/process/promises:246
04:49:43               triggerUncaughtException(err, true /* fromPromise */);
04:49:43               ^
04:49:43     
04:49:43     [Error: Cipher job failed]
04:49:43   ...

How do I mock an environment like this case?

@richardlau
Copy link
Member

@XadillaX see nodejs/build#2176 for the instructions that the CI build was based on.

CHECK(errors->Empty());
*err = v8::Undefined(env->isolate());
*result = out_.ToArrayBuffer(env);
return v8::Just(!result->IsEmpty());
}

if (errors->Empty())
errors->Capture();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

/ping @jasnell, why here should errors->Capture() again? Can't we CHECK(!errors->Empty()) directly?

@XadillaX XadillaX force-pushed the cipher_zero_length branch from 7d4b493 to 46d0c55 Compare June 22, 2021 14:09
@XadillaX
Copy link
Contributor Author

Related failure on ubi81_sharedlibs_openssl111fips_x64:

04:49:43 not ok 592 parallel/test-crypto-subtle-zero-length
04:49:43   ---
04:49:43   duration_ms: 0.94
04:49:43   severity: fail
04:49:43   exitcode: 1
04:49:43   stack: |-
04:49:43     node:internal/process/promises:246
04:49:43               triggerUncaughtException(err, true /* fromPromise */);
04:49:43               ^
04:49:43     
04:49:43     [Error: Cipher job failed]
04:49:43   ...

After testing, I found that it's not

if (errors->Empty())
  errors->Capture();

occurs the error. It failed either with commenting those two lines.

@XadillaX
Copy link
Contributor Author

Related failure on ubi81_sharedlibs_openssl111fips_x64:

04:49:43 not ok 592 parallel/test-crypto-subtle-zero-length
04:49:43   ---
04:49:43   duration_ms: 0.94
04:49:43   severity: fail
04:49:43   exitcode: 1
04:49:43   stack: |-
04:49:43     node:internal/process/promises:246
04:49:43               triggerUncaughtException(err, true /* fromPromise */);
04:49:43               ^
04:49:43     
04:49:43     [Error: Cipher job failed]
04:49:43   ...

It seems a problem with ubi81_sharedlibs_openssl111fips_x64's sharelib.

Maybe lack of code below (I guess):

    /*
     * CCM mode needs to know about the case where inl == 0 && in == NULL - it
     * means the plaintext/ciphertext length is 0
     */
    if (inl < 0
            || (inl == 0
                && EVP_CIPHER_mode(ctx->cipher) != EVP_CIPH_CCM_MODE)) {
        *outl = 0;
        return inl == 0;
    }

Refs: https://github.com/openssl/openssl/blob/OpenSSL_1_1_1-stable/crypto/evp/evp_enc.c#L309-L318

So I will check zero-length in Node.js' source code.

@nodejs-github-bot
Copy link
Collaborator

@XadillaX
Copy link
Contributor Author

@tniessen All CI have already passed except commit message due to several f commits. It will be rebased when landing.

@XadillaX XadillaX requested a review from mscdex June 23, 2021 07:07
@richardlau
Copy link
Member

Related failure on ubi81_sharedlibs_openssl111fips_x64:

04:49:43 not ok 592 parallel/test-crypto-subtle-zero-length
04:49:43   ---
04:49:43   duration_ms: 0.94
04:49:43   severity: fail
04:49:43   exitcode: 1
04:49:43   stack: |-
04:49:43     node:internal/process/promises:246
04:49:43               triggerUncaughtException(err, true /* fromPromise */);
04:49:43               ^
04:49:43     
04:49:43     [Error: Cipher job failed]
04:49:43   ...

It seems a problem with ubi81_sharedlibs_openssl111fips_x64's sharelib.

Maybe lack of code below (I guess):

    /*
     * CCM mode needs to know about the case where inl == 0 && in == NULL - it
     * means the plaintext/ciphertext length is 0
     */
    if (inl < 0
            || (inl == 0
                && EVP_CIPHER_mode(ctx->cipher) != EVP_CIPH_CCM_MODE)) {
        *outl = 0;
        return inl == 0;
    }

Refs: https://github.com/openssl/openssl/blob/OpenSSL_1_1_1-stable/crypto/evp/evp_enc.c#L309-L318

So I will check zero-length in Node.js' source code.

I suspect it's because UBI 8.1 is outdated and has OpenSSL 1.1.1c (which is several versions behind the current OpenSSL 1.1.1k). I'll add updating the UBI container to UBI 8.4, which has OpenSSL 1.1.1g (which is at least more up to date) to my list of things to do.

@XadillaX
Copy link
Contributor Author

Related failure on ubi81_sharedlibs_openssl111fips_x64:

04:49:43 not ok 592 parallel/test-crypto-subtle-zero-length
04:49:43   ---
04:49:43   duration_ms: 0.94
04:49:43   severity: fail
04:49:43   exitcode: 1
04:49:43   stack: |-
04:49:43     node:internal/process/promises:246
04:49:43               triggerUncaughtException(err, true /* fromPromise */);
04:49:43               ^
04:49:43     
04:49:43     [Error: Cipher job failed]
04:49:43   ...

It seems a problem with ubi81_sharedlibs_openssl111fips_x64's sharelib.
Maybe lack of code below (I guess):

    /*
     * CCM mode needs to know about the case where inl == 0 && in == NULL - it
     * means the plaintext/ciphertext length is 0
     */
    if (inl < 0
            || (inl == 0
                && EVP_CIPHER_mode(ctx->cipher) != EVP_CIPH_CCM_MODE)) {
        *outl = 0;
        return inl == 0;
    }

Refs: https://github.com/openssl/openssl/blob/OpenSSL_1_1_1-stable/crypto/evp/evp_enc.c#L309-L318

So I will check zero-length in Node.js' source code.

I suspect it's because UBI 8.1 is outdated and has OpenSSL 1.1.1c (which is several versions behind the current OpenSSL 1.1.1k). I'll add updating the UBI container to UBI 8.4, which has OpenSSL 1.1.1g (which is at least more up to date) to my list of things to do.

I think it's really a problem. We can make sure that our UBI 8.4 build container use the right openssl sharedlib, but we can't make sure users use the right one.

Comment on lines 133 to 140
// Only `ubi81_sharedlibs_openssl111fips_x64` failed when `in.size()` is zero.
// So we regard 0 as special and DO NOT go into `EVP_CipherUpdate()` logic
// because it will occur failure under `ubi81_sharedlibs_openssl111fips_x64`.
//
// Refs: https://github.com/nodejs/node/pull/38913#issuecomment-866505244
Copy link
Member

Choose a reason for hiding this comment

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

I don't know if it's possible to work out which version of OpenSSL 1.1.1 changed the behavior without going through and build/testing against OpenSSL 1.1.1c, 1.1.1d, 1.1.1e etc. but the comment would be a lot better if we could refer to an OpenSSL release rather than a container specific to our build CI.

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've updated the comment and you may review again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@richardlau btw, I think we should leave at least one outdated OpenSSL CI to discover similar problems.

@XadillaX XadillaX force-pushed the cipher_zero_length branch from 96b9f19 to 7eb1d04 Compare June 24, 2021 03:37
@nodejs-github-bot
Copy link
Collaborator

@XadillaX XadillaX added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Jun 24, 2021
@XadillaX XadillaX requested a review from richardlau June 24, 2021 05:29
// it up. But we still have to regard zero as special in Node.js code to
// prevent old OpenSSL failure.
//
// Refs: https://ci.nodejs.org/job/node-test-commit-linux-containered/nodes=ubi81_sharedlibs_openssl111fips_x64/27534/console
Copy link
Member

Choose a reason for hiding this comment

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

This link will expire -- build logs are only kept for around two weeks or so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@XadillaX XadillaX force-pushed the cipher_zero_length branch from dcf45a1 to 4986fb9 Compare June 25, 2021 02:23
@nodejs-github-bot
Copy link
Collaborator

@aduh95 aduh95 added the commit-queue Add this label to land a pull request using GitHub Actions. label Jun 26, 2021
@github-actions github-actions bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Jun 26, 2021
@github-actions
Copy link
Contributor

Landed in b3d4a2c...8954e03

@github-actions github-actions bot closed this Jun 26, 2021
nodejs-github-bot pushed a commit that referenced this pull request Jun 26, 2021
Fixes: #38883

PR-URL: #38913
Refs: #38883
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
targos pushed a commit that referenced this pull request Jul 11, 2021
Fixes: #38883

PR-URL: #38913
Refs: #38883
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. c++ Issues and PRs that require attention from people who are familiar with C++. crypto Issues and PRs related to the crypto subsystem. needs-ci PRs that need a full CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Decrypting a zero-length array with SubtleCrypto triggers Assertion failures
7 participants