Skip to content

fix(digest): guard cloned state from GC in squeeze - #27

Merged
johanns merged 1 commit into
johanns:mainfrom
sorah:fix-squeeze-gc-guard-2
May 17, 2026
Merged

fix(digest): guard cloned state from GC in squeeze#27
johanns merged 1 commit into
johanns:mainfrom
sorah:fix-squeeze-gc-guard-2

Conversation

@sorah

@sorah sorah commented May 16, 2026

Copy link
Copy Markdown
Contributor

Ensure copy reachable across the allocation and finalization with RB_GC_GUARD.

rb_sha3_digest_squeeze clones self via rb_obj_clone to preserve the original state, but the cloned VALUE (copy) is not referenced again after get_sha3_digest_context extracts the raw context pointer. An optimizing compiler is then free to stop preserving copy on the stack, so the subsequent rb_str_new allocation can become a GC point that reclaims the clone and frees context_copy->state. The following Keccak_HashFinal then dereferences freed memory and segfaults inside the function prologue.

We observed the following crash calling SHA3::Digest::SHAKE_128#hex_squeeze in our system:

	[BUG] Segmentation fault
	c:0043 CFUNC :hex_squeeze
	/gems/.../sha3_ext.so(Keccak_HashFinal+0x10)
	/gems/.../sha3_ext.so(rb_sha3_digest_squeeze+0xca)

Also add coverage for #squeeze / #hex_squeeze, which previously had no direct tests on SHA3::Digest.

Disclosure: Claude Code is in use for investigation and patch suggestion. I've verified the fix fixes the crash, and have reviewed the patch before submission.

rb_sha3_digest_squeeze clones self via rb_obj_clone to preserve the
original state, but the cloned VALUE (`copy`) is not referenced again
after get_sha3_digest_context extracts the raw context pointer. An
optimizing compiler is then free to stop preserving `copy` on the
stack, so the subsequent rb_str_new allocation can become a GC point
that reclaims the clone and frees context_copy->state. The following
Keccak_HashFinal then dereferences freed memory and segfaults inside
the function prologue.

We observed the following crash calling
SHA3::Digest::SHAKE_128#hex_squeeze in a tight loop:

  [BUG] Segmentation fault
  c:0043 CFUNC :hex_squeeze
  /gems/.../sha3_ext.so(Keccak_HashFinal+0x10)
  /gems/.../sha3_ext.so(rb_sha3_digest_squeeze+0xca)

Keep `copy` reachable across the allocation and finalization with
RB_GC_GUARD. Also add coverage for #squeeze / #hex_squeeze, which
previously had no direct tests on SHA3::Digest.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a Ruby C-extension GC/liveness bug in SHA3::Digest#squeeze (and therefore #hex_squeeze) by explicitly guarding the cloned digest object from being optimized out, preventing a use-after-free crash during allocation. It also adds direct spec coverage for #squeeze / #hex_squeeze behavior on SHAKE digests.

Changes:

  • Add RB_GC_GUARD(copy) in rb_sha3_digest_squeeze to keep the cloned VALUE reachable across allocation/GC.
  • Add RSpec coverage for #squeeze/#hex_squeeze output sizing, state preservation across calls, and rejection on non-SHAKE algorithms.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
ext/sha3/digest.c Guards the cloned digest object to prevent GC from freeing the cloned internal state during squeeze.
spec/sha3_digest_core_spec.rb Adds specs covering #squeeze/#hex_squeeze correctness and expected constraints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@johanns
johanns merged commit fcef37e into johanns:main May 17, 2026
16 checks passed
@johanns

johanns commented May 17, 2026

Copy link
Copy Markdown
Owner

@sorah Thank you for the fix! I'll push a new release shortly. Cheers!

@sorah
sorah deleted the fix-squeeze-gc-guard-2 branch May 17, 2026 22:54
@sorah

sorah commented May 17, 2026

Copy link
Copy Markdown
Contributor Author

thanks for the prompt gem release, friendly reminding that you might forget to create a GitHub release 🚀

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.

3 participants