Skip to content

investigate flaky test-crypto-timing-safe-equal-benchmarks #38226

Description

@Trott

pummel/test-crypto-timing-safe-equal-benchmarks seems to fail frequently on LinuxONE. That's often a sign that a test (or some Node.js internal code) is not prepared to run on very fast CPUs. (In CI, our LinuxONE host is very fast and often finds race conditions of the "oh, I didn't expect it to run that quickly" variety).

Is this a bug in crypto? A bug in the test? Something else?

@nodejs/crypto @nodejs/testing

Doesn't seem to be a LinuxONE platform team so pinging a few IBM/Red Hat folks who are active in the Build team: @richardlau @AshCripps @mhdawson Probably not the exact right people, but they probably know who to loop in.

  • Test: test-crypto-timing-safe-equal-benchmarks
  • Platform: linuxone
  • Console Output:
00:09:15 not ok 2944 pummel/test-crypto-timing-safe-equal-benchmarks
00:09:18   ---
00:09:18   duration_ms: 2.136
00:09:18   severity: fail
00:09:18   exitcode: 1
00:09:18   stack: |-
00:09:18     node:assert:402
00:09:18         throw err;
00:09:18         ^
00:09:18     
00:09:18     AssertionError [ERR_ASSERTION]: timingSafeEqual should not leak information from its execution time (t=5.715867808741522)
00:09:18         at Object.<anonymous> (/home/iojs/build/workspace/node-test-commit-linuxone/nodes/rhel7-s390x/test/pummel/test-crypto-timing-safe-equal-benchmarks.js:109:1)
00:09:18         at Module._compile (node:internal/modules/cjs/loader:1108:14)
00:09:18         at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
00:09:18         at Module.load (node:internal/modules/cjs/loader:988:32)
00:09:18         at Function.Module._load (node:internal/modules/cjs/loader:828:14)
00:09:18         at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
00:09:18         at node:internal/main/run_main_module:17:47 {
00:09:18       generatedMessage: false,
00:09:18       code: 'ERR_ASSERTION',
00:09:18       actual: false,
00:09:18       expected: true,
00:09:18       operator: '=='
00:09:18     }
00:09:18   ...

Activity

  1. added
    cryptoIssues and PRs related to the crypto subsystem.
    flaky-testIssues and PRs involving tests that fail intermittently in CI.
    on Apr 13, 2021
  2. Trott commented on Apr 29, 2021

    @Trott
    MemberAuthor

    This has now started failing somewhat consistently on LinuxONE in CI. I don't know if that means the test just isn't reliable or if it means there's a timing issue with faster processors or what. @nodejs/crypto

    not ok 2961 pummel/test-crypto-timing-safe-equal-benchmarks
      ---
      duration_ms: 2.12
      severity: fail
      exitcode: 1
      stack: |-
        node:assert:412
            throw err;
            ^
        
        AssertionError [ERR_ASSERTION]: timingSafeEqual should not leak information from its execution time (t=7.219867083599476)
            at Object.<anonymous> (/home/iojs/build/workspace/node-test-commit-linuxone/nodes/rhel7-s390x/test/pummel/test-crypto-timing-safe-equal-benchmarks.js:109:1)
            at Module._compile (node:internal/modules/cjs/loader:1109:14)
            at Object.Module._extensions..js (node:internal/modules/cjs/loader:1138:10)
            at Module.load (node:internal/modules/cjs/loader:989:32)
            at Function.Module._load (node:internal/modules/cjs/loader:829:14)
            at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:12)
            at node:internal/main/run_main_module:17:47 {
          generatedMessage: false,
          code: 'ERR_ASSERTION',
          actual: false,
          expected: true,
          operator: '=='
        }
      ...
    

    https://ci.nodejs.org/job/node-test-commit-linuxone/nodes=rhel7-s390x/26963/consoleText

  3. Trott commented on Apr 29, 2021

    @Trott
    MemberAuthor

    Stress tests for bisecting:

    Bisecting: 70 revisions left to test after this (roughly 6 steps)
    [4f11b8b] doc: fix typo in buffer.md

    Bisecting: 35 revisions left to test after this (roughly 5 steps)
    [f27b7cf] fs: aggregate errors in fsPromises to avoid error swallowing

    Bisecting: 17 revisions left to test after this (roughly 4 steps)
    [87aca07] doc: clarify that fs.Dir async iterator closes automatically

    Bisecting: 8 revisions left to test after this (roughly 3 steps)
    [896e5af] tools: remove fixer for non-ascii-character ESLint custom rule

    Bisecting: 4 revisions left to test after this (roughly 2 steps)
    [1adcae9] doc: add try/catch in http2 respondWithFile example

    Bisecting: 1 revision left to test after this (roughly 1 step)
    [7919ced] lib: harden lint checks for globals

    Bisecting: 0 revisions left to test after this (roughly 0 steps)
    [4af15df] src: fix validation of negative offset to avoid abort

    7919ced is the first bad commit
    commit 7919ced
    Author: Antoine du Hamel duhamelantoine1995@gmail.com
    Date: Mon Apr 26 18:03:54 2021 +0200

    lib: harden lint checks for globals
    
    PR-URL: https://github.com/nodejs/node/pull/38419
    Reviewed-By: James M Snell <jasnell@gmail.com>
    Reviewed-By: Darshan Sen <raisinten@gmail.com>
    

    :040000 040000 8080a76b162a40209ec5b9397db3945fa36ee143 247e5c50613229df3f9776ce9637237146682dd0 M

  4. Trott commented on Apr 29, 2021

    @Trott
    MemberAuthor

    I know they're not active in this repo anymore, but ping @not-an-aardvark: Hey, does this look like an issue with the crypto safe timing test? Or does it look like a bug? The machine that it's failing on is very fast, if that helps. Would increasing numTimes from 1e5 to a larger value help or does that defeat the purpose of the test?

  5. not-an-aardvark commented on Apr 29, 2021

    @not-an-aardvark
    Contributor

    From looking at a few of the failing cases, it seems like the error message is always "timingSafeEqual should not leak information from its execution time (t=<some positive number>)". Assuming that the test is working properly, this would be a signal that timingSafeEqual is consistently running faster for equal inputs than for unequal inputs (at least on some hardware/builds), which would be bad and might be worth investigating. That said, in the past this test has had various false positives due to artifacts of the test setup itself.

    The test fails if it determines that a timing difference is statistically significant. In general, increasing numTrials would make the test more sensitive to small timing differences (i.e. maybe even more likely to fail), since it would have a larger dataset to do statistical analysis on.

    In this case, it seems like the t-values are routinely 9 and above, which wouldn't plausibly happen by chance. So we can be pretty confident that it's either (a) a legitimate bug in crypto.timingSafeEqual, or (b) a bug in the test setup, which wouldn't be fixed by changing numTrials.


    Incidentally, when we were originally implementing crypto.timingSafeEqual in #8040, we considered using a "double HMAC" implementation rather than CRYPTO_memcmp, but decided on CRYPTO_memcmp because we didn't want to leak the boolean condition of whether the comparison was successful.*

    Since then, there have been significant advances in side-channel attacks (Meltdown/Spectre and others), which has led me to believe that double HMAC could be a good defense-in-depth measure against increasingly powerful compilers and CPU branch prediction that could optimize away CRYPTO_memcmp.

    It might be worth updating crypto.timingSafeEqual to use both double HMAC and CRYPTO_memcmp (i.e. given buffers a and b, return the result of CRYPTO_memcmp(sha256hmac(randomKey, a), sha256hmac(randomKey, b))).

    *I also have some doubts about whether it's really possible to avoid leaking the boolean result, due to branch prediction. This might also be why this test keeps failing. In any case, combining CRYPTO_memcmp and double HMAC wouldn't make anything worse in that regard.

  6. Trott commented on Apr 29, 2021

    @Trott
    MemberAuthor

    Bisect results:

    7919ced0c97e9a5b17e6042e0b57bc911d23583d is the first bad commit
    commit 7919ced0c97e9a5b17e6042e0b57bc911d23583d
    Author: Antoine du Hamel <duhamelantoine1995@gmail.com>
    Date:   Mon Apr 26 18:03:54 2021 +0200
    
        lib: harden lint checks for globals
        
        PR-URL: https://github.com/nodejs/node/pull/38419
        Reviewed-By: James M Snell <jasnell@gmail.com>
        Reviewed-By: Darshan Sen <raisinten@gmail.com>
    
    :040000 040000 8080a76b162a40209ec5b9397db3945fa36ee143 247e5c50613229df3f9776ce9637237146682dd0 M
    
  7. Trott commented on Apr 29, 2021

    @Trott
    MemberAuthor

    The bisect results are certainly surprising. Going to run a bunch more tests to confirm them.

    Stress tests against 7919ced:

    Stress tests on 4af15df (the last change before the one above that is being tentatively blamed for the problem):

  8. Trott commented on Apr 30, 2021

    @Trott
    MemberAuthor

    In this case, it seems like the t-values are routinely 9 and above, which wouldn't plausibly happen by chance.

    When I increase numTimes to 1e6, I got a whopping t value of 37.871320438939634 which I imagine is not moving in the right direction.....

  9. Trott commented on Apr 30, 2021

    @Trott
    MemberAuthor

    The bisect results are certainly surprising. Going to run a bunch more tests to confirm them.

    Stress tests against 7919ced:

    Stress tests on 4af15df (the last change before the one above that is being tentatively blamed for the problem):

    Those sure seem consistent with 7919ced making the problem much worse, presumably as some indirect side effect. I wonder if the next step is to sorta bisect the individual line changes in that commit (since outside of the lint tool changes, they are mostly self-contained) to get to the bottom of which one (if it's indeed only one) is causing the issue.

  10. Trott commented on Apr 30, 2021

    @Trott
    MemberAuthor

    So, I bisected on individual file changes and the one that is causing the issue is Trott@845adb7 which is totally puzzling to me, but maybe @not-an-aardvark or @aduh95 would have a reasonable explanation (assuming my results hold up to further scrutiny, which I will definitely be applying to confirm).

  11. reopened this on Apr 30, 2021
  12. 26 remaining items

  13. added a commit that references this issue on Nov 27, 2023
  14. tniessen commented on Apr 4, 2024

    @tniessen
    Member

    Refs: #52341

    Not sure if this change might make it better or worse.

  15. mhdawson commented on Apr 25, 2024

    @mhdawson
    Member

    Seems to be failing a lot on MacOS now. An example:

    https://github.com/nodejs/node/actions/runs/8822600805/job/24221172295?pr=52671

    === release test-crypto-timing-safe-equal-benchmarks ===
    Path: pummel/test-crypto-timing-safe-equal-benchmarks
    Error: --- stderr ---
    node:assert:408
        throw err;
        ^
    
    AssertionError [ERR_ASSERTION]: timingSafeEqual should not leak information from its execution time (t=8.54045763079109)
        at Object.<anonymous> (/Users/runner/work/node/node/test/pummel/test-crypto-timing-safe-equal-benchmarks.js:109:1)
        at Module._compile (node:internal/modules/cjs/loader:1476:14)
        at Module._extensions..js (node:internal/modules/cjs/loader:1555:10)
        at Module.load (node:internal/modules/cjs/loader:1288:32)
        at Module._load (node:internal/modules/cjs/loader:1104:12)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:191:14)
        at node:internal/main/run_main_module:30:49 {
      generatedMessage: false,
      code: 'ERR_ASSERTION',
      actual: false,
      expected: true,
      operator: '=='
    }
    
  16. lpinca commented on May 1, 2024

    @lpinca
    Member

    b876e00 landed, I'm closing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cryptoIssues and PRs related to the crypto subsystem.flaky-testIssues and PRs involving tests that fail intermittently in CI.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions