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

crypto.generatePrime seems have much higher priority over than process.exit which results in stuck #56449

Closed
zyscoder opened this issue Jan 3, 2025 · 3 comments
Labels
confirmed-bug Issues with confirmed bugs. crypto Issues and PRs related to the crypto subsystem.

Comments

@zyscoder
Copy link

zyscoder commented Jan 3, 2025

Version

v22.11.0

Platform

Linux u24vm 6.8.0-51-generic #52-Ubuntu SMP PREEMPT_DYNAMIC Thu Dec  5 13:09:44 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Subsystem

crypto

What steps will reproduce the bug?

Setup a node instance,

» node

and run the following javascript code.

_crypto = require('crypto');
_crypto.generatePrime(134217728,{bigint:true},function(err,prime){});
process.exit();

The node instance cannot be closed using process.exit() or with double Ctl-C or Ctl-D.
Note that if we don't execute process.exit() but run other code, it works.

How often does it reproduce? Is there a required condition?

This issue can always be triggered following the steps above.

What is the expected behavior? Why is that the expected behavior?

I don't know if this is a bug.
The documentation of process.exit() says The process.exit() method instructs Node.js to terminate the process **synchronously**. Calling process.exit() will force the process to exit as quickly as possible even if there are still asynchronous operations pending that have not yet completed fully, including I/O operations to process.stdout and process.stderr.
However, the above code cannot close the node instance immediately, leading to the node server being stuck.

What do you see instead?

» node   
Welcome to Node.js v22.11.0.
Type ".help" for more information.
> _crypto = require('crypto');
{
  checkPrime: [Function: checkPrime],
  checkPrimeSync: [Function: checkPrimeSync],
  createCipheriv: [Function: createCipheriv],
  createDecipheriv: [Function: createDecipheriv],
  createDiffieHellman: [Function: createDiffieHellman],
  createDiffieHellmanGroup: [Function: createDiffieHellmanGroup],
  createECDH: [Function: createECDH],
  createHash: [Function: createHash],
  createHmac: [Function: createHmac],
  createPrivateKey: [Function: createPrivateKey],
  createPublicKey: [Function: createPublicKey],
  createSecretKey: [Function: createSecretKey],
  createSign: [Function: createSign],
  createVerify: [Function: createVerify],
  diffieHellman: [Function: diffieHellman],
  generatePrime: [Function: generatePrime],
  generatePrimeSync: [Function: generatePrimeSync],
  getCiphers: [Function (anonymous)],
  getCipherInfo: [Function: getCipherInfo],
  getCurves: [Function (anonymous)],
  getDiffieHellman: [Function: createDiffieHellmanGroup],
  getHashes: [Function (anonymous)],
  hkdf: [Function: hkdf],
  hkdfSync: [Function: hkdfSync],
  pbkdf2: [Function: pbkdf2],
  pbkdf2Sync: [Function: pbkdf2Sync],
  generateKeyPair: [Function: generateKeyPair],
  generateKeyPairSync: [Function: generateKeyPairSync],
  generateKey: [Function: generateKey],
  generateKeySync: [Function: generateKeySync],
  privateDecrypt: [Function (anonymous)],
  privateEncrypt: [Function (anonymous)],
  publicDecrypt: [Function (anonymous)],
  publicEncrypt: [Function (anonymous)],
  randomBytes: [Function: randomBytes],
  randomFill: [Function: randomFill],
  randomFillSync: [Function: randomFillSync],
  randomInt: [Function: randomInt],
  randomUUID: [Function: randomUUID],
  scrypt: [Function: scrypt],
  scryptSync: [Function: scryptSync],
  sign: [Function: signOneShot],
  setEngine: [Function: setEngine],
  timingSafeEqual: [Function (anonymous)],
  getFips: [Function: getFips],
  setFips: [Function: setFips],
  verify: [Function: verifyOneShot],
  hash: [Function: hash],
  Certificate: [Function: Certificate] {
    exportChallenge: [Function: exportChallenge],
    exportPublicKey: [Function: exportPublicKey],
    verifySpkac: [Function: verifySpkac]
  },
  Cipher: undefined,
  Cipheriv: [Function: Cipheriv],
  Decipher: undefined,
  Decipheriv: [Function: Decipheriv],
  DiffieHellman: [Function: DiffieHellman],
  DiffieHellmanGroup: [Function: DiffieHellmanGroup],
  ECDH: [Function: ECDH] { convertKey: [Function: convertKey] },
  Hash: [Function: deprecated],
  Hmac: [Function: deprecated],
  KeyObject: [class KeyObject],
  Sign: [Function: Sign],
  Verify: [Function: Verify],
  X509Certificate: [class X509Certificate],
  secureHeapUsed: [Function: secureHeapUsed],
  constants: [Object: null prototype] {
    OPENSSL_VERSION_NUMBER: 805306608,
    SSL_OP_ALL: 2147485776,
    SSL_OP_ALLOW_NO_DHE_KEX: 1024,
    SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: 262144,
    SSL_OP_CIPHER_SERVER_PREFERENCE: 4194304,
    SSL_OP_CISCO_ANYCONNECT: 32768,
    SSL_OP_COOKIE_EXCHANGE: 8192,
    SSL_OP_CRYPTOPRO_TLSEXT_BUG: 2147483648,
    SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: 2048,
    SSL_OP_LEGACY_SERVER_CONNECT: 4,
    SSL_OP_NO_COMPRESSION: 131072,
    SSL_OP_NO_ENCRYPT_THEN_MAC: 524288,
    SSL_OP_NO_QUERY_MTU: 4096,
    SSL_OP_NO_RENEGOTIATION: 1073741824,
    SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: 65536,
    SSL_OP_NO_SSLv2: 0,
    SSL_OP_NO_SSLv3: 33554432,
    SSL_OP_NO_TICKET: 16384,
    SSL_OP_NO_TLSv1: 67108864,
    SSL_OP_NO_TLSv1_1: 268435456,
    SSL_OP_NO_TLSv1_2: 134217728,
    SSL_OP_NO_TLSv1_3: 536870912,
    SSL_OP_PRIORITIZE_CHACHA: 2097152,
    SSL_OP_TLS_ROLLBACK_BUG: 8388608,
    ENGINE_METHOD_RSA: 1,
    ENGINE_METHOD_DSA: 2,
    ENGINE_METHOD_DH: 4,
    ENGINE_METHOD_RAND: 8,
    ENGINE_METHOD_EC: 2048,
    ENGINE_METHOD_CIPHERS: 64,
    ENGINE_METHOD_DIGESTS: 128,
    ENGINE_METHOD_PKEY_METHS: 512,
    ENGINE_METHOD_PKEY_ASN1_METHS: 1024,
    ENGINE_METHOD_ALL: 65535,
    ENGINE_METHOD_NONE: 0,
    DH_CHECK_P_NOT_SAFE_PRIME: 2,
    DH_CHECK_P_NOT_PRIME: 1,
    DH_UNABLE_TO_CHECK_GENERATOR: 4,
    DH_NOT_SUITABLE_GENERATOR: 8,
    RSA_PKCS1_PADDING: 1,
    RSA_NO_PADDING: 3,
    RSA_PKCS1_OAEP_PADDING: 4,
    RSA_X931_PADDING: 5,
    RSA_PKCS1_PSS_PADDING: 6,
    RSA_PSS_SALTLEN_DIGEST: -1,
    RSA_PSS_SALTLEN_MAX_SIGN: -2,
    RSA_PSS_SALTLEN_AUTO: -2,
    defaultCoreCipherList: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA',
    TLS1_VERSION: 769,
    TLS1_1_VERSION: 770,
    TLS1_2_VERSION: 771,
    TLS1_3_VERSION: 772,
    POINT_CONVERSION_COMPRESSED: 2,
    POINT_CONVERSION_UNCOMPRESSED: 4,
    POINT_CONVERSION_HYBRID: 6,
    defaultCipherList: [Getter/Setter]
  },
  webcrypto: [Getter],
  subtle: [Getter],
  getRandomValues: [Getter]
}
> _crypto.generatePrime(134217728,{bigint:true},function(err,prime){});
undefined
> process.exit();


^C^C^C^C^C

Additional information

No response

@aduh95 aduh95 added confirmed-bug Issues with confirmed bugs. crypto Issues and PRs related to the crypto subsystem. labels Jan 3, 2025
@aduh95
Copy link
Contributor

aduh95 commented Jan 3, 2025

@nodejs/crypto

@jasnell
Copy link
Member

jasnell commented Jan 3, 2025

The key challenge is that openssl does not implement a standard interrupt for this operation. It does allow passing a callback that can be used to interrupt it but we are not currently using it. A fix would be to implement that callback, have it check for pending exit on each call and have it interrupt the operation to generate/check the primes

jasnell added a commit to jasnell/node that referenced this issue Jan 3, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: nodejs#56449
@jasnell
Copy link
Member

jasnell commented Jan 3, 2025

Ok, I've got a fix PR here: #56460 ...

Note that this only partially addresses the issue as openssl will still become unresponsive when generating overly large primes. The key issue is that the generation method performs a certain amount of work before it invokes the callback we can use to check to see if we should continue. But, for reasonably sized primes, this allows the operation to be more readily interupted.

jasnell added a commit to jasnell/node that referenced this issue Jan 3, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: nodejs#56449
jasnell added a commit to jasnell/node that referenced this issue Jan 4, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: nodejs#56449
jasnell added a commit to jasnell/node that referenced this issue Jan 4, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: nodejs#56449
jasnell added a commit to jasnell/node that referenced this issue Jan 4, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: nodejs#56449
jasnell added a commit to jasnell/node that referenced this issue Jan 5, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: nodejs#56449
@jasnell jasnell closed this as completed in ea493c1 Jan 7, 2025
targos pushed a commit that referenced this issue Jan 13, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: #56449
PR-URL: #56460
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
Ceres6 pushed a commit to Ceres6/node that referenced this issue Jan 13, 2025
The `generatePrime` and `checkPrime` functions in the `crypto`
module are only somewhat interruptible. This change makes it
possible to interrupt these more reliably. Note that generating
overly large primes can still take a long time and may not be
interruptible as this mechanism relies on a callback to check
for stopping conditions but OpenSSL may perform a long running
operation without calling the callback right away.

Fixes: nodejs#56449
PR-URL: nodejs#56460
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
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.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants