Repository navigation
interp: keep the byte layout of heap objects with uintptr pointers - #5911
Draft
davecheney wants to merge 1 commit into
Draft
davecheney wants to merge 1 commit into
davecheney wants to merge 1 commit into
Conversation
When interp writes a heap object back as a new global, it uses the
object type or the layout type of the object. If the program stores
a pointer in the object through a uintptr, this type can be
incorrect:
- A pointer-free layout gives the type [N x i8]. A pointer cannot go
in an i8 field, thus the build fails with "interp: ptrtoint integer
size does not equal pointer size".
- A pointer at an offset that is not aligned to a pointer field goes
across two fields. The build fails, or the pointer becomes 0.
Also, rawLLVMValue made a struct that is not packed. Thus a pointer
that is not aligned moved to the next aligned offset, and the size
of the value changed. A run of pointer words that is not a whole
pointer gave a pointer that is not correct.
This change adds rawValue.fitsType. It is true if each pointer in the
buffer is a whole pointer in a pointer-sized field of the type. If it
is false, interp makes a packed type from the buffer (rawLLVMType).
This type has the same size and the same bytes as the buffer, and a
pointer field at the offset of each pointer. rawLLVMValue now uses
this type. A part of a pointer gives an error and does not give an
incorrect constant. Interp makes the type before it creates the
global, thus an object that points to itself is safe. Normal programs
do not change: the code size of testdata/init.go, map.go, reflect.go
and stdlib.go for cortex-m-qemu is the same.
Tests:
- interp/testdata/rawlayout.ll: heap objects with pointer-free and
pointer layouts that hold an aligned, misaligned, self or
straddling uintptr pointer.
- interp/rawlayout_test.go: the size and the value of rawLLVMValue,
and the error for a part of a pointer.
- testdata/init_rawlayout.go: compares the init result with the
same bytes made at runtime.
Reproducer. Go prints "heap: true". TinyGo fails to build. The bug
is also in v0.43.0.
package main
import "unsafe"
var x int
var h = newH()
func newH() *[2]uintptr {
p := new([2]uintptr)
p[1] = uintptr(unsafe.Pointer(&x))
return p
}
func main() {
println("heap:", h[1] == uintptr(unsafe.Pointer(&x)))
}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When interp writes a heap object back as a new global, it uses the object type or the layout type of the object. If the program stores a pointer in the object through a uintptr, this type can be incorrect:
Also, rawLLVMValue made a struct that is not packed. Thus a pointer that is not aligned moved to the next aligned offset, and the size of the value changed. A run of pointer words that is not a whole pointer gave a pointer that is not correct.
This change adds rawValue.fitsType. It is true if each pointer in the buffer is a whole pointer in a pointer-sized field of the type. If it is false, interp makes a packed type from the buffer (rawLLVMType). This type has the same size and the same bytes as the buffer, and a pointer field at the offset of each pointer. rawLLVMValue now uses this type. A part of a pointer gives an error and does not give an incorrect constant. Interp makes the type before it creates the global, thus an object that points to itself is safe. Normal programs do not change: the code size of testdata/init.go, map.go, reflect.go and stdlib.go for cortex-m-qemu is the same.
Tests:
Reproducer. Go prints "heap: true". TinyGo fails to build. The bug is also in v0.43.0.