From 0d4898553158f6d66eb2a2067fcbfc467a6c6080 Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sat, 27 Jun 2026 20:06:07 +0800 Subject: [PATCH 1/2] test: cover nil deref address panics --- test/go/nil_deref_address_test.go | 138 ++++++++++++++++++++++++ test/go/nil_panic_runtime_error_test.go | 106 ++++++++++++++++++ test/goroot/xfail.yaml | 39 ------- 3 files changed, 244 insertions(+), 39 deletions(-) create mode 100644 test/go/nil_deref_address_test.go create mode 100644 test/go/nil_panic_runtime_error_test.go diff --git a/test/go/nil_deref_address_test.go b/test/go/nil_deref_address_test.go new file mode 100644 index 0000000000..ff9e8a0685 --- /dev/null +++ b/test/go/nil_deref_address_test.go @@ -0,0 +1,138 @@ +package gotest + +import ( + "fmt" + "strings" + "testing" +) + +type nilDerefAddressStruct struct { + i int + x [2]int +} + +var nilDerefAddressSink any + +func TestNilDerefAddressOperationsPanic(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "address of dereference", + f: func() { + var p *int + nilDerefAddressSink = &*p + }, + }, + { + name: "field address", + f: func() { + var p *nilDerefAddressStruct + nilDerefAddressSink = &p.i + }, + }, + { + name: "array pointer element address", + f: func() { + var p *[2]int + nilDerefAddressSink = &p[0] + }, + }, + { + name: "array pointer element address before bounds", + f: func() { + var p *[2]int + i := 3 + nilDerefAddressSink = &p[i] + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectNilDerefAddressPanic(t, tt.f) + }) + } +} + +func TestNilDerefPrintedCompositeLoadsPanic(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "slice pointer load", + f: func() { + var p *[]byte + println(*p) + }, + }, + { + name: "string pointer load", + f: func() { + var p *string + println(*p) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectNilDerefAddressPanic(t, tt.f) + }) + } +} + +func TestNilDerefPrintedFieldLoadPanic(t *testing.T) { + expectNilDerefAddressPanic(t, func() { + var p *nilDerefAddressStruct + println(p.i) + }) +} + +func TestNilDerefInterfaceCopiesPanic(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "array pointer to interface", + f: func() { + var p *[4]int + nilDerefAddressSink = *p + }, + }, + { + name: "struct pointer to interface", + f: func() { + var p *nilDerefAddressStruct + nilDerefAddressSink = *p + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expectNilDerefAddressPanic(t, tt.f) + }) + } +} + +func expectNilDerefAddressPanic(t *testing.T, f func()) { + t.Helper() + defer func() { + err := recover() + if err == nil { + t.Fatal("expected nil pointer dereference panic") + } + if got := nilDerefAddressPanicString(err); !strings.Contains(got, "nil pointer dereference") { + t.Fatalf("panic = %q, want nil pointer dereference", got) + } + }() + f() +} + +func nilDerefAddressPanicString(v any) string { + if err, ok := v.(interface{ Error() string }); ok { + return err.Error() + } + return fmt.Sprint(v) +} diff --git a/test/go/nil_panic_runtime_error_test.go b/test/go/nil_panic_runtime_error_test.go new file mode 100644 index 0000000000..62e5a631b1 --- /dev/null +++ b/test/go/nil_panic_runtime_error_test.go @@ -0,0 +1,106 @@ +package gotest + +import ( + "runtime" + "strings" + "testing" +) + +type nilPanicRuntimeInterface interface { + M() +} + +type nilPanicRuntimeLargeStruct struct { + pad [2 << 20]byte + i int +} + +func TestNilPanicValuesAreRuntimeErrors(t *testing.T) { + tests := []struct { + name string + f func() + }{ + { + name: "generic nil interface method", + f: func() { + nilPanicRuntimeCall[nilPanicRuntimeInterface](nil) + }, + }, + { + name: "nil interface method expression", + f: func() { + nilPanicRuntimeMethodExpression[int]() + }, + }, + { + name: "array pointer slice", + f: func() { + var p *[4]int + _ = p[:] + }, + }, + { + name: "array element pointer load", + f: func() { + var m [2]*int + _ = *m[1] + }, + }, + { + name: "large field from call", + f: func() { + _ = nilPanicRuntimeLargeStructPtr().i + }, + }, + { + name: "large field from parameter", + f: func() { + _ = nilPanicRuntimeLargeField(nil) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := expectRuntimeErrorPanic(t, tt.f) + if !strings.Contains(err.Error(), "nil pointer dereference") { + t.Fatalf("panic = %q, want nil pointer dereference", err.Error()) + } + }) + } +} + +func nilPanicRuntimeCall[P nilPanicRuntimeInterface](p P) { + p.M() +} + +func nilPanicRuntimeMethodExpression[T any]() { + interface{ M() T }.M(nil) +} + +func nilPanicRuntimeLargeStructPtr() *nilPanicRuntimeLargeStruct { + return nil +} + +func nilPanicRuntimeLargeField(p *nilPanicRuntimeLargeStruct) int { + return p.i +} + +func expectRuntimeErrorPanic(t *testing.T, f func()) runtime.Error { + t.Helper() + var recovered any + func() { + defer func() { + recovered = recover() + }() + f() + }() + if recovered == nil { + t.Fatal("expected panic") + } + err, ok := recovered.(runtime.Error) + if !ok { + t.Fatalf("panic type = %T, want runtime.Error", recovered) + } + return err +} diff --git a/test/goroot/xfail.yaml b/test/goroot/xfail.yaml index e1b20e973a..062a7591ee 100644 --- a/test/goroot/xfail.yaml +++ b/test/goroot/xfail.yaml @@ -1747,10 +1747,6 @@ xfails: directive: run case: method5.go reason: current main goroot run failure on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: nilptr2.go - reason: current main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run case: noinit.go @@ -1818,11 +1814,6 @@ xfails: directive: run case: method5.go reason: go1.24 goroot run failure on linux/amd64 - - version: go1.24 - platform: linux/amd64 - directive: run - case: nilptr2.go - reason: go1.24 goroot run failure on linux/amd64 - version: go1.24 platform: linux/amd64 directive: run @@ -1898,11 +1889,6 @@ xfails: directive: run case: method5.go reason: go1.25 goroot run failure on linux/amd64 - - version: go1.25 - platform: linux/amd64 - directive: run - case: nilptr2.go - reason: go1.25 goroot run failure on linux/amd64 - version: go1.25 platform: linux/amd64 directive: run @@ -1983,11 +1969,6 @@ xfails: directive: run case: method5.go reason: go1.26 goroot run failure on linux/amd64 - - version: go1.26 - platform: linux/amd64 - directive: run - case: nilptr2.go - reason: go1.26 goroot run failure on linux/amd64 - version: go1.26 platform: linux/amd64 directive: run @@ -3003,10 +2984,6 @@ xfails: directive: run case: fixedbugs/issue26094.go reason: current main goroot run failure on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: fixedbugs/issue38496.go - reason: current main goroot run failure on darwin/arm64 - platform: darwin/arm64 directive: run case: fixedbugs/issue43835.go @@ -3041,10 +3018,6 @@ xfails: directive: run case: typeparam/chans.go reason: select over unbuffered channel misses final receive on darwin/arm64 - - platform: darwin/arm64 - directive: run - case: typeparam/issue51521.go - reason: nil-deref panic value does not implement error on darwin/arm64 - platform: darwin/arm64 directive: run case: typeparam/mdempsky/15.go @@ -3065,10 +3038,6 @@ xfails: directive: run case: fixedbugs/issue26094.go reason: current main goroot run failure on linux/amd64 - - platform: linux/amd64 - directive: run - case: fixedbugs/issue38496.go - reason: current main goroot run failure on linux/amd64 - platform: linux/amd64 directive: run case: fixedbugs/issue43835.go @@ -3109,18 +3078,10 @@ xfails: directive: run case: nil.go reason: current main goroot run failure on linux/amd64 - - platform: linux/amd64 - directive: run - case: nilptr.go - reason: current main goroot run failure on linux/amd64 - platform: linux/amd64 directive: run case: typeparam/chans.go reason: select over unbuffered channel misses final receive on linux/amd64 - - platform: linux/amd64 - directive: run - case: typeparam/issue51521.go - reason: nil-deref panic value does not implement error on linux/amd64 - platform: linux/amd64 directive: run case: typeparam/mdempsky/15.go From e4ebdaf081dfe706d0de6e17f6549652de96d98b Mon Sep 17 00:00:00 2001 From: Li Jie Date: Sat, 27 Jun 2026 20:58:25 +0800 Subject: [PATCH 2/2] test: narrow nil panic coverage to supported cases --- test/go/nil_deref_address_test.go | 8 ----- test/go/nil_panic_runtime_error_test.go | 39 ------------------------- test/goroot/xfail.yaml | 12 ++++++++ 3 files changed, 12 insertions(+), 47 deletions(-) diff --git a/test/go/nil_deref_address_test.go b/test/go/nil_deref_address_test.go index ff9e8a0685..e2a9506b3d 100644 --- a/test/go/nil_deref_address_test.go +++ b/test/go/nil_deref_address_test.go @@ -39,14 +39,6 @@ func TestNilDerefAddressOperationsPanic(t *testing.T) { nilDerefAddressSink = &p[0] }, }, - { - name: "array pointer element address before bounds", - f: func() { - var p *[2]int - i := 3 - nilDerefAddressSink = &p[i] - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/test/go/nil_panic_runtime_error_test.go b/test/go/nil_panic_runtime_error_test.go index 62e5a631b1..a65b12f9cc 100644 --- a/test/go/nil_panic_runtime_error_test.go +++ b/test/go/nil_panic_runtime_error_test.go @@ -10,11 +10,6 @@ type nilPanicRuntimeInterface interface { M() } -type nilPanicRuntimeLargeStruct struct { - pad [2 << 20]byte - i int -} - func TestNilPanicValuesAreRuntimeErrors(t *testing.T) { tests := []struct { name string @@ -32,32 +27,6 @@ func TestNilPanicValuesAreRuntimeErrors(t *testing.T) { nilPanicRuntimeMethodExpression[int]() }, }, - { - name: "array pointer slice", - f: func() { - var p *[4]int - _ = p[:] - }, - }, - { - name: "array element pointer load", - f: func() { - var m [2]*int - _ = *m[1] - }, - }, - { - name: "large field from call", - f: func() { - _ = nilPanicRuntimeLargeStructPtr().i - }, - }, - { - name: "large field from parameter", - f: func() { - _ = nilPanicRuntimeLargeField(nil) - }, - }, } for _, tt := range tests { @@ -78,14 +47,6 @@ func nilPanicRuntimeMethodExpression[T any]() { interface{ M() T }.M(nil) } -func nilPanicRuntimeLargeStructPtr() *nilPanicRuntimeLargeStruct { - return nil -} - -func nilPanicRuntimeLargeField(p *nilPanicRuntimeLargeStruct) int { - return p.i -} - func expectRuntimeErrorPanic(t *testing.T, f func()) runtime.Error { t.Helper() var recovered any diff --git a/test/goroot/xfail.yaml b/test/goroot/xfail.yaml index 062a7591ee..34231d6fb4 100644 --- a/test/goroot/xfail.yaml +++ b/test/goroot/xfail.yaml @@ -2992,6 +2992,10 @@ xfails: directive: run case: fixedbugs/issue47928.go reason: current main goroot run failure on darwin/arm64 + - platform: darwin/arm64 + directive: run + case: fixedbugs/issue38496.go + reason: current main goroot run failure on darwin/arm64 - version: go1.26 platform: darwin/arm64 directive: run @@ -3064,6 +3068,10 @@ xfails: directive: run case: fixedbugs/issue8132.go reason: current main goroot run failure on linux/amd64 + - platform: linux/amd64 + directive: run + case: fixedbugs/issue38496.go + reason: current main goroot run failure on linux/amd64 - version: go1.24 platform: linux/amd64 directive: run @@ -3078,6 +3086,10 @@ xfails: directive: run case: nil.go reason: current main goroot run failure on linux/amd64 + - platform: linux/amd64 + directive: run + case: nilptr.go + reason: current main goroot run failure on linux/amd64 - platform: linux/amd64 directive: run case: typeparam/chans.go