Skip to content

Treat the private store as a gc root - #227

Open
Taure wants to merge 1 commit into
rvirding:developfrom
Taure:gc-roots-private-store
Open

Taure wants to merge 1 commit into
rvirding:developfrom
Taure:gc-roots-private-store

Conversation

@Taure

@Taure Taure commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

luerl:put_private/3 is documented as putting "a private Value under Key
that is not exposed to the runtime". luerl_heap:gc/1's root set is
[Meta..., G | Stk] plus the call stack (luerl_heap.erl:601-602) and does
not include the private store.

So a reference kept there is unreachable from the collector's point of view.
The collector frees the table underneath it, the reference survives as an
opaque term, and the embedder's next decode/2 fails on a reclaimed index:

St0 = luerl:init(),
{Ref, St1} = luerl:encode(#{<<"keep">> => <<"me">>}, St0),
St2 = luerl:put_private(mine, Ref, St1),
St3 = luerl:gc(St2),
luerl:decode(luerl:get_private(mine, St3), St3).
%% ** exception error: {badkey,14}

The failure is delayed and does not point at the collection that caused it,
and the API is exactly the one that invites keeping a reference there.

Adding the store to the root set is one line. Included is a regression test,
which fails on develop with {badkey,14}.

Verification

rebar3 eunit 31/0 (30 existing plus the new test) and rebar3 ct 11/11.

Found while investigating unbounded Luerl memory growth in a game backend.
The workaround without this is to anchor the value in a global for the
duration of the collection and remove it straight after, which is visible to
script code in between.

luerl:put_private/3 holds values that are deliberately unreachable from
Lua. gc/1's root set did not include them, so the collector freed the
tables underneath any reference kept there and the embedder's next
decode/2 failed with a badkey on a reclaimed index - a use-after-free
with a delayed and confusing failure.

Adds a regression test, which fails on develop with {badkey,14}.
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