Skip to content

Commit f166976

Browse files
iamrajivdeadprogram
authored andcommitted
interp: defer out-of-bounds loads to runtime instead of crashing
When the interpreter encountered a load that was out of bounds of the object, it panicked with "interp: load out of bounds", crashing the compiler. 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: package main import "unsafe" var p = unsafe.SliceData([]int{}) var v = *p func main() {} Return nil for an out-of-bounds load, the same as for an external global, so the caller defers the load to runtime instead of crashing. This matches what regular Go does, where the load reads from the runtime zero-base at runtime. Fixes #4214
1 parent cb311ed commit f166976

4 files changed

Lines changed: 45 additions & 3 deletions

File tree

interp/interp_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func TestInterp(t *testing.T) {
2020
"revert",
2121
"store",
2222
"alloc",
23+
"slicedata",
2324
} {
2425
name := name // make local to this closure
2526
t.Run(name, func(t *testing.T) {

interp/memory.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ func (mv *memoryView) put(index uint32, obj object) {
299299
}
300300

301301
// Load the value behind the given pointer. Returns nil if the pointer points to
302-
// an external global.
302+
// an external global or if the load is out of bounds of the object (in which
303+
// case the caller defers the load to runtime).
303304
func (mv *memoryView) load(p pointerValue, size uint32) value {
304305
if checks && mv.hasExternalStore(p) {
305306
panic("interp: load from object with external store")
@@ -312,8 +313,13 @@ func (mv *memoryView) load(p pointerValue, size uint32) value {
312313
if p.offset() == 0 && size == obj.size {
313314
return obj.buffer.clone()
314315
}
315-
if checks && p.offset()+size > obj.size {
316-
panic("interp: load out of bounds")
316+
if p.offset()+size > obj.size {
317+
// The load is out of bounds of the object. This can happen for valid
318+
// Go programs, for example when dereferencing the pointer returned by
319+
// unsafe.SliceData on a zero-capacity slice (which points to a
320+
// zero-sized object). Return nil so the caller defers this load to
321+
// runtime instead of crashing the compiler.
322+
return nil
317323
}
318324
v := obj.buffer.asRawValue(mv.r)
319325
loadedBuf := v.buf[p.offset() : p.offset()+size]

interp/testdata/slicedata.ll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
2+
target triple = "x86_64--linux"
3+
4+
; Reproduction of https://github.com/tinygo-org/tinygo/issues/4214.
5+
; Dereferencing the pointer returned by unsafe.SliceData on a zero-capacity
6+
; slice produces a load that is out of bounds of a zero-sized object. The
7+
; interp must defer this load to runtime instead of crashing the compiler.
8+
9+
@main.zeroSized = global {} zeroinitializer
10+
@main.v = global i64 0
11+
12+
define void @runtime.initAll() unnamed_addr {
13+
entry:
14+
call void @main.init(ptr undef)
15+
ret void
16+
}
17+
18+
define internal void @main.init(ptr %context) unnamed_addr {
19+
entry:
20+
%val = load i64, ptr @main.zeroSized
21+
store i64 %val, ptr @main.v
22+
ret void
23+
}

interp/testdata/slicedata.out.ll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
2+
target triple = "x86_64--linux"
3+
4+
@main.zeroSized = local_unnamed_addr global {} zeroinitializer
5+
@main.v = local_unnamed_addr global i64 0
6+
7+
define void @runtime.initAll() unnamed_addr {
8+
entry:
9+
%val = load i64, ptr @main.zeroSized, align 8
10+
store i64 %val, ptr @main.v, align 8
11+
ret void
12+
}

0 commit comments

Comments
 (0)