Skip to content

Commit b39e0f4

Browse files
committed
runtime: don't crash on nil pointers in checkptrAlignment
Ironically, checkptrAlignment had a latent case of bad pointer arithmetic: if ptr is nil, then `add(ptr, size-1)` might produce an illegal pointer value. The fix is to simply check for nil at the top of checkptrAlignment, and short-circuit if so. This CL also adds a more explicit bounds check in checkptrStraddles, rather than relying on `add(ptr, size-1)` to wrap around. I don't think this is necessary today, but it seems prudent to be careful. Fixes #47430. Change-Id: I5c50b2f7f41415dbebbd803e1b8e7766ca95e1fd Reviewed-on: https://go-review.googlesource.com/c/go/+/338029 Trust: Matthew Dempsky <[email protected]> Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 7cd10c1 commit b39e0f4

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

src/runtime/checkptr.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ package runtime
77
import "unsafe"
88

99
func checkptrAlignment(p unsafe.Pointer, elem *_type, n uintptr) {
10+
// nil pointer is always suitably aligned (#47430).
11+
if p == nil {
12+
return
13+
}
14+
1015
// Check that (*[n]elem)(p) is appropriately aligned.
1116
// Note that we allow unaligned pointers if the types they point to contain
1217
// no pointers themselves. See issue 37298.
@@ -29,10 +34,12 @@ func checkptrStraddles(ptr unsafe.Pointer, size uintptr) bool {
2934
return false
3035
}
3136

32-
end := add(ptr, size-1)
33-
if uintptr(end) < uintptr(ptr) {
37+
// Check that add(ptr, size-1) won't overflow. This avoids the risk
38+
// of producing an illegal pointer value (assuming ptr is legal).
39+
if uintptr(ptr) >= -(size - 1) {
3440
return true
3541
}
42+
end := add(ptr, size-1)
3643

3744
// TODO(mdempsky): Detect when [ptr, end] contains Go allocations,
3845
// but neither ptr nor end point into one themselves.

src/runtime/checkptr_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func TestCheckPtr(t *testing.T) {
2626
}{
2727
{"CheckPtrAlignmentPtr", "fatal error: checkptr: misaligned pointer conversion\n"},
2828
{"CheckPtrAlignmentNoPtr", ""},
29+
{"CheckPtrAlignmentNilPtr", ""},
2930
{"CheckPtrArithmetic", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"},
3031
{"CheckPtrArithmetic2", "fatal error: checkptr: pointer arithmetic result points to invalid allocation\n"},
3132
{"CheckPtrSize", "fatal error: checkptr: converted pointer straddles multiple allocations\n"},

src/runtime/testdata/testprog/checkptr.go

+35-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@
44

55
package main
66

7-
import "unsafe"
7+
import (
8+
"runtime"
9+
"time"
10+
"unsafe"
11+
)
812

913
func init() {
1014
register("CheckPtrAlignmentNoPtr", CheckPtrAlignmentNoPtr)
1115
register("CheckPtrAlignmentPtr", CheckPtrAlignmentPtr)
16+
register("CheckPtrAlignmentNilPtr", CheckPtrAlignmentNilPtr)
1217
register("CheckPtrArithmetic", CheckPtrArithmetic)
1318
register("CheckPtrArithmetic2", CheckPtrArithmetic2)
1419
register("CheckPtrSize", CheckPtrSize)
@@ -29,6 +34,35 @@ func CheckPtrAlignmentPtr() {
2934
sink2 = (**int64)(unsafe.Pointer(uintptr(p) + 1))
3035
}
3136

37+
// CheckPtrAlignmentNilPtr tests that checkptrAlignment doesn't crash
38+
// on nil pointers (#47430).
39+
func CheckPtrAlignmentNilPtr() {
40+
var do func(int)
41+
do = func(n int) {
42+
// Inflate the stack so runtime.shrinkstack gets called during GC
43+
if n > 0 {
44+
do(n - 1)
45+
}
46+
47+
var p unsafe.Pointer
48+
_ = (*int)(p)
49+
}
50+
51+
go func() {
52+
for {
53+
runtime.GC()
54+
}
55+
}()
56+
57+
go func() {
58+
for i := 0; ; i++ {
59+
do(i % 1024)
60+
}
61+
}()
62+
63+
time.Sleep(time.Second)
64+
}
65+
3266
func CheckPtrArithmetic() {
3367
var x int
3468
i := uintptr(unsafe.Pointer(&x))

0 commit comments

Comments
 (0)