Skip to content

interp: mark objects reached through the current value of a global - #5909

Draft
davecheney wants to merge 1 commit into
tinygo-org:devfrom
davecheney:dfc/interp-mark-current-buffer
Draft

davecheney wants to merge 1 commit into
tinygo-org:devfrom
davecheney:dfc/interp-mark-current-buffer

Conversation

@davecheney

Copy link
Copy Markdown
Contributor

When interp leaves a call for run time, the objects that the call can reach must be marked so that later loads and stores of them are not done at compile time. For a global variable, markExternal followed the pointers in the LLVM initializer of the global. But interp writes its results to those initializers only after it has finished, so a pointer that was stored to a global at compile time was not followed. For example, an init function that sets a global to the address of another global, starts a goroutine with the address of the first global, waits for the goroutine on a channel, and then reads the second global gives the old value. The goroutine changed the value through the pointer before the read. The program has no data race, and the 0.43.0 release has the same problem at all optimization levels.

markExternal now follows the pointers in the current value of the object. This also reaches heap objects that interp allocated and that do not have an LLVM global yet. A global with no known value at compile time still uses its LLVM initializer.

The problem was found during review of #5903. This change does not depend on #5903 or on the other interp fixes on dfc/interp-getwritable-rollback and dfc/interp-load-alias.

When interp leaves a call for run time, the objects that the call can reach must be marked so that later loads and stores of them are not done at compile time. For a global variable, markExternal followed the pointers in the LLVM initializer of the global. But interp writes its results to those initializers only after it has finished, so a pointer that was stored to a global at compile time was not followed. For example, an init function that sets a global to the address of another global, starts a goroutine with the address of the first global, waits for the goroutine on a channel, and then reads the second global gives the old value. The goroutine changed the value through the pointer before the read. The program has no data race, and the 0.43.0 release has the same problem at all optimization levels.

markExternal now follows the pointers in the current value of the object. This also reaches heap objects that interp allocated and that do not have an LLVM global yet. A global with no known value at compile time still uses its LLVM initializer.

The problem was found during review of tinygo-org#5903. This change does not depend on tinygo-org#5903 or on the other interp fixes on dfc/interp-getwritable-rollback and dfc/interp-load-alias.
@davecheney
davecheney force-pushed the dfc/interp-mark-current-buffer branch from 4c38f19 to e5033db Compare October 17, 2026 22:43
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