Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions interp/interp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestInterp(t *testing.T) {
"revert",
"store",
"alloc",
"slicedata",
} {
name := name // make local to this closure
t.Run(name, func(t *testing.T) {
Expand Down
12 changes: 9 additions & 3 deletions interp/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@ func (mv *memoryView) put(index uint32, obj object) {
}

// Load the value behind the given pointer. Returns nil if the pointer points to
// an external global.
// an external global or if the load is out of bounds of the object (in which
// case the caller defers the load to runtime).
func (mv *memoryView) load(p pointerValue, size uint32) value {
if checks && mv.hasExternalStore(p) {
panic("interp: load from object with external store")
Expand All @@ -312,8 +313,13 @@ func (mv *memoryView) load(p pointerValue, size uint32) value {
if p.offset() == 0 && size == obj.size {
return obj.buffer.clone()
}
if checks && p.offset()+size > obj.size {
panic("interp: load out of bounds")
if p.offset()+size > obj.size {
// The load is out of bounds of the object. This can happen for valid
// Go programs, for example when dereferencing the pointer returned by
// unsafe.SliceData on a zero-capacity slice (which points to a
// zero-sized object). Return nil so the caller defers this load to
// runtime instead of crashing the compiler.
return nil
}
v := obj.buffer.asRawValue(mv.r)
loadedBuf := v.buf[p.offset() : p.offset()+size]
Expand Down
23 changes: 23 additions & 0 deletions interp/testdata/slicedata.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"

; Reproduction of https://github.com/tinygo-org/tinygo/issues/4214.
; Dereferencing the pointer returned by unsafe.SliceData on a zero-capacity
; slice produces a load that is out of bounds of a zero-sized object. The
; interp must defer this load to runtime instead of crashing the compiler.

@main.zeroSized = global {} zeroinitializer
@main.v = global i64 0

define void @runtime.initAll() unnamed_addr {
entry:
call void @main.init(ptr undef)
ret void
}

define internal void @main.init(ptr %context) unnamed_addr {
entry:
%val = load i64, ptr @main.zeroSized
store i64 %val, ptr @main.v
ret void
}
12 changes: 12 additions & 0 deletions interp/testdata/slicedata.out.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64--linux"

@main.zeroSized = local_unnamed_addr global {} zeroinitializer
@main.v = local_unnamed_addr global i64 0

define void @runtime.initAll() unnamed_addr {
entry:
%val = load i64, ptr @main.zeroSized, align 8
store i64 %val, ptr @main.v, align 8
ret void
}