Skip to content

Commit fde3926

Browse files
committedJul 29, 2015
runtime: ignore arguments in cgocallback_gofunc frame
Otherwise the GC may see uninitialized memory there, which might be old pointers that are retained, or it might trigger the invalid pointer check. Fixes #11907. Change-Id: I67e306384a68468eef45da1a8eb5c9df216a77c0 Reviewed-on: https://go-review.googlesource.com/12852 Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent f6dfe16 commit fde3926

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed
 

‎src/runtime/crash_cgo_test.go

+88
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@ func TestCgoTraceback(t *testing.T) {
3636
}
3737
}
3838

39+
func TestCgoCallbackGC(t *testing.T) {
40+
if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
41+
t.Skipf("no pthreads on %s", runtime.GOOS)
42+
}
43+
got := executeTest(t, cgoCallbackGCSource, nil)
44+
want := "OK\n"
45+
if got != want {
46+
t.Fatalf("expected %q, but got %q", want, got)
47+
}
48+
}
49+
3950
func TestCgoExternalThreadPanic(t *testing.T) {
4051
if runtime.GOOS == "plan9" {
4152
t.Skipf("no pthreads on %s", runtime.GOOS)
@@ -191,6 +202,83 @@ func main() {
191202
}
192203
`
193204

205+
const cgoCallbackGCSource = `
206+
package main
207+
208+
import "runtime"
209+
210+
/*
211+
#include <pthread.h>
212+
213+
void go_callback();
214+
215+
static void *thr(void *arg) {
216+
go_callback();
217+
return 0;
218+
}
219+
220+
static void foo() {
221+
pthread_t th;
222+
pthread_create(&th, 0, thr, 0);
223+
pthread_join(th, 0);
224+
}
225+
*/
226+
import "C"
227+
import "fmt"
228+
229+
//export go_callback
230+
func go_callback() {
231+
runtime.GC()
232+
grow()
233+
runtime.GC()
234+
}
235+
236+
var cnt int
237+
238+
func grow() {
239+
x := 10000
240+
sum := 0
241+
if grow1(&x, &sum) == 0 {
242+
panic("bad")
243+
}
244+
}
245+
246+
func grow1(x, sum *int) int {
247+
if *x == 0 {
248+
return *sum + 1
249+
}
250+
*x--
251+
sum1 := *sum + *x
252+
return grow1(x, &sum1)
253+
}
254+
255+
func main() {
256+
const P = 100
257+
done := make(chan bool)
258+
// allocate a bunch of stack frames and spray them with pointers
259+
for i := 0; i < P; i++ {
260+
go func() {
261+
grow()
262+
done <- true
263+
}()
264+
}
265+
for i := 0; i < P; i++ {
266+
<-done
267+
}
268+
// now give these stack frames to cgo callbacks
269+
for i := 0; i < P; i++ {
270+
go func() {
271+
C.foo()
272+
done <- true
273+
}()
274+
}
275+
for i := 0; i < P; i++ {
276+
<-done
277+
}
278+
fmt.Printf("OK\n")
279+
}
280+
`
281+
194282
const cgoExternalThreadPanicSource = `
195283
package main
196284

‎src/runtime/stubs.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,16 @@ func breakpoint()
128128
func reflectcall(argtype *_type, fn, arg unsafe.Pointer, argsize uint32, retoffset uint32)
129129

130130
func procyield(cycles uint32)
131-
func cgocallback_gofunc(fv *funcval, frame unsafe.Pointer, framesize uintptr)
132131
func goexit()
133132

133+
// Not all cgocallback_gofunc frames are actually cgocallback_gofunc,
134+
// so not all have these arguments. Mark them uintptr so that the GC
135+
// does not misinterpret memory when the arguments are not present.
136+
// cgocallback_gofunc is not called from go, only from cgocallback,
137+
// so the arguments will be found via cgocallback's pointer-declared arguments.
138+
// See the assembly implementations for more details.
139+
func cgocallback_gofunc(fv uintptr, frame uintptr, framesize uintptr)
140+
134141
//go:noescape
135142
func cas(ptr *uint32, old, new uint32) bool
136143

0 commit comments

Comments
 (0)
Please sign in to comment.