Skip to content

Conversation

@Tango992
Copy link
Contributor

@Tango992 Tango992 commented Oct 3, 2025

Closes #30636

@Tango992 Tango992 changed the title fix(ext/node): omit smi from zlib.crc32 op function fix(ext/node): omit smi from zlib.crc32 op function Oct 3, 2025
@Tango992
Copy link
Contributor Author

Tango992 commented Oct 3, 2025

Maybe I'm missing something, but sometimes value that's being passed to the op as smi can be interpreted differently.

Try modify this function by adding a print statement: println!("op_zlib_crc32 called with checksum: {:#x}", value);

#[op2(fast)]
pub fn op_zlib_crc32_string(#[string] data: &str, #[smi] value: u32) -> u32 {
// SAFETY: `data` is a valid buffer.
unsafe {
zlib::crc32(value as c_ulong, data.as_ptr(), data.len() as u32) as u32
}
}

import { crc32 } from "node:zlib";

let checkSum = 0xffffffff;
for (let i = 0; i < 2**16; i++) {
  const currentChecksum = crc32("", checkSum);
  if (currentChecksum !== checkSum) {
    console.error("iteration", i, `failed with checksum: 0x${currentChecksum.toString(16)}`);
    process.exit(1);
  }
}

console.log(`Final CRC32: 0x${checkSum.toString(16)}`);

Here's the output:

...
op_zlib_crc32 called with checksum: 0xffffffff
op_zlib_crc32 called with checksum: 0xffffffff
op_zlib_crc32 called with checksum: 0xffffffff
op_zlib_crc32 called with checksum: 0xffffffff
op_zlib_crc32 called with checksum: 0xffffffff
op_zlib_crc32 called with checksum: 0xffffffff
op_zlib_crc32 called with checksum: 0xffffffff
op_zlib_crc32 called with checksum: 0x7fffffff
iteration 6216 failed with checksum: 0x7fffffff

Somehow the op function sees the value as 2**31 - 1. It happens randomly, and what causes the original issue at the first place.

@dsherret dsherret requested a review from littledivy October 3, 2025 15:29
@nathanwhit
Copy link
Member

nathanwhit commented Oct 3, 2025

The reason for that behavior is that it depends on whether or not V8 chooses to make a fast call or not. The smi optimization, as far as i know, only applies for fast calls. So when V8 doesn't take the fast call, you get the correct integer value, but the fast calls get the maximum value for a smi which is 2**31 - 1

Typically V8 will only choose fast calls once you've called the function a large number of times, especially in a hot loop. So it makes sense that this would occur only after a few thousand calls

Copy link
Member

@nathanwhit nathanwhit left a comment

Choose a reason for hiding this comment

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

LGTM

@nathanwhit nathanwhit merged commit 8059733 into denoland:main Oct 3, 2025
20 checks passed
@Tango992 Tango992 deleted the fix-node-crc32-mismatch branch October 3, 2025 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zlib CRC32 is broken (again)

2 participants