Skip to content

fix(octet): prevent o_free from freeing Lua-allocated userdata struct - #1195

Merged
jaromil merged 5 commits into
masterfrom
fix/pcall_zkcc
Jul 6, 2026
Merged

fix(octet): prevent o_free from freeing Lua-allocated userdata struct#1195
jaromil merged 5 commits into
masterfrom
fix/pcall_zkcc

Conversation

@jaromil

@jaromil jaromil commented Jul 6, 2026

Copy link
Copy Markdown
Member

o_free() called zfree(t) on the octet struct after ref reached 0. For userdata-backed octets (from o_new via lua_newuserdata), the struct is Lua-allocated, not zmalloc'd. Under pcall, incremental GC fires between o_arg and o_free, causing ref to reach 0 in o_free which then corrupts the Lua heap via wrong-allocator free.

Changes:

  • o_alloc: explicitly initialize ref=0 (was relying on zeroed malloc)
  • o_free: only zfree(t) when ref < 0 (heap-allocated, o_alloc path); userdata-backed octets (ref==0 after GC+o_free) only free val. Set val=NULL after freeing for defence in depth.
  • o_destroy: set val=NULL after GC finalizer frees it

jaromil added 5 commits July 7, 2026 00:44
o_free() called zfree(t) on the octet struct after ref reached 0.
For userdata-backed octets (from o_new via lua_newuserdata), the
struct is Lua-allocated, not zmalloc'd.  Under pcall, incremental
GC fires between o_arg and o_free, causing ref to reach 0 in o_free
which then corrupts the Lua heap via wrong-allocator free.

Changes:
- o_alloc: explicitly initialize ref=0 (was relying on zeroed malloc)
- o_free: only zfree(t) when ref < 0 (heap-allocated, o_alloc path);
  userdata-backed octets (ref==0 after GC+o_free) only free val.
  Set val=NULL after freeing for defence in depth.
- o_destroy: set val=NULL after GC finalizer frees it
…ncrypt path

The HEDLEY_ASSUME(h != NULL) tells the compiler it can elide null checks,
but the immediately following SAFE_GOTO(h, ...) is precisely a null check.
The decrypt path (line 697) never had this assume; this aligns the encrypt
path to match.
Every expansion of these macros is an error path. HEDLEY_UNLIKELY tells
the compiler to lay out the happy path contiguously and move error handlers
out of line. Affects thousands of call sites across all native modules.
Zero behavioral change.
Every *_new function either returns a valid pointer or errors out.
HEDLEY_RETURNS_NON_NULL on the declaration propagates that knowledge
to every caller, letting the compiler eliminate redundant null checks
after SAFE guards.  Affects octet, big, ecp, ecp2, secp, hash, fp12,
float, and time constructors.
o_val and o_len are stateless field accessors with no side effects.
Annotating them as HEDLEY_PURE lets the compiler CSE repeated calls
in hot loops, particularly o_len in ZK witness generation paths.
@jaromil
jaromil merged commit 11b7131 into master Jul 6, 2026
33 checks passed
@jaromil
jaromil deleted the fix/pcall_zkcc branch July 6, 2026 23:49
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.

1 participant