Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: marked free object in span on Windows #45364

Closed
zhuah opened this issue Apr 3, 2021 · 5 comments
Closed

runtime: marked free object in span on Windows #45364

zhuah opened this issue Apr 3, 2021 · 5 comments

Comments

@zhuah
Copy link

@zhuah zhuah commented Apr 3, 2021

What version of Go are you using (go version)?

$ go version
go version go1.15.11 darwin/amd64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env

What did you do?

I'm cross compile from macOS to windows with mingw,
the code is just like the sample here, passing a go memory pointer to C code and copy it,
the problem is that when malloc returns a reused memory block, it contains the original go memory pointer,
which cause the gc to crash with this invalid pointer.
if i replace malloc with calloc then the crash is gone.

Unfortunately i can only reproduce it in my own real code.

/*
#include <stdlib.h>
#include <string.h>

typedef struct {
	char *data;
	int length;
} testData;

static char data[2048];

void testFn(testData *d) {
	memcpy(data, d->data, d->length);
}
*/
import "C"
import (
	"unsafe"
)

type cTestData = C.testData

func (d *cTestData) setup(ptr *uint8, data int) {
	d.data = (*C.char)(unsafe.Pointer(ptr))
	d.length = C.int(data)
}

func main() {
	for i := 0; i < 100000; i++ {
		b := make([]uint8, 100)

		cdata := (*cTestData)(C.malloc(C.size_t(unsafe.Sizeof(C.testData{}))))
		cdata.setup(&b[0], len(b))

		C.testFn(cdata)
		C.free(unsafe.Pointer(cdata))
		b = nil
	}
}

What did you expect to see?

What did you see instead?

runtime: marked free object in span 0x1e152552208, elemsize=176 freeindex=0 (bad use of unsafe.Pointer? try -d=checkptr)
0xc000186000 free  marked   zombie
000000c000186000:  0000000000000000  0000000000000000
000000c000186010:  0000000000000000  0000000000000000
000000c000186020:  0000000000000000  0000000000000000
000000c000186030:  0000000000000000  0000000000000000
000000c000186040:  0000000000000000  0000000000000000
000000c000186050:  0000000000000000  0000000000000000
000000c000186060:  0000000000000000  0000000000000000
000000c000186070:  0000000000000000  0000000000000000
000000c000186080:  0000000000000000  0000000000000000
000000c000186090:  0000000000000000  0000000000000000
000000c0001860a0:  0000000000000000  0000000000000000
0xc0001860b0 free  unmarked
0xc000186160 alloc marked
0xc000186210 free  unmarked
0xc0001862c0 free  unmarked
0xc000186370 free  unmarked
0xc000186420 free  unmarked
0xc0001864d0 free  unmarked
0xc000186580 free  unmarked
0xc000186630 free  unmarked
0xc0001866e0 free  unmarked
0xc000186790 free  unmarked
0xc000186840 free  unmarked
0xc0001868f0 free  unmarked
0xc0001869a0 free  unmarked
0xc000186a50 free  unmarked
0xc000186b00 free  unmarked
0xc000186bb0 free  unmarked
0xc000186c60 free  unmarked
0xc000186d10 free  unmarked
0xc000186dc0 free  unmarked
0xc000186e70 free  unmarked
0xc000186f20 free  unmarked
0xc000186fd0 free  unmarked
0xc000187080 free  unmarked
0xc000187130 free  unmarked
0xc0001871e0 free  unmarked
0xc000187290 free  unmarked
0xc000187340 free  unmarked
0xc0001873f0 free  unmarked
0xc0001874a0 free  unmarked
0xc000187550 free  unmarked
0xc000187600 free  unmarked
0xc0001876b0 free  unmarked
0xc000187760 free  unmarked
0xc000187810 free  unmarked
0xc0001878c0 free  unmarked
0xc000187970 free  unmarked
0xc000187a20 free  unmarked
0xc000187ad0 free  unmarked
0xc000187b80 free  unmarked
0xc000187c30 free  unmarked
0xc000187ce0 free  unmarked
0xc000187d90 free  unmarked
0xc000187e40 free  unmarked
0xc000187ef0 free  unmarked
fatal error: found pointer to free object

goroutine 22 [running]:
runtime.throw(0x7ff6a3babdec, 0x1c)
        /usr/local/Cellar/gotip/stable/src/runtime/panic.go:1116 +0x79 fp=0xc00004db70 sp=0xc00004db40 pc=0x7ff6a368bef9
runtime.(*mspan).reportZombies(0x1e152552208)
        /usr/local/Cellar/gotip/stable/src/runtime/mgcsweep.go:827 +0x385 fp=0xc00004dbf0 sp=0xc00004db70 pc=0x7ff6a36785c5
runtime.(*mspan).sweep(0x1e152552208, 0x1e152552200, 0x0)
        /usr/local/Cellar/gotip/stable/src/runtime/mgcsweep.go:451 +0x7f8 fp=0xc00004dcc0 sp=0xc00004dbf0 pc=0x7ff6a3677e78
runtime.sweepone(0x0)
        /usr/local/Cellar/gotip/stable/src/runtime/mgcsweep.go:233 +0x290 fp=0xc00004dd28 sp=0xc00004dcc0 pc=0x7ff6a3677430
runtime.deductSweepCredit(0x2000, 0x0)
        /usr/local/Cellar/gotip/stable/src/runtime/mgcsweep.go:864 +0x8e fp=0xc00004dd50 sp=0xc00004dd28 pc=0x7ff6a367866e
runtime.(*mcentral).cacheSpan(0x7ff6a3eccfd0, 0x7ff6a369ef10)
        /usr/local/Cellar/gotip/stable/src/runtime/mcentral.go:101 +0x70 fp=0xc00004ddc8 sp=0xc00004dd50 pc=0x7ff6a366a9b0
runtime.(*mcache).refill(0x1e12b9e07b0, 0x4)
        /usr/local/Cellar/gotip/stable/src/runtime/mcache.go:142 +0xb5 fp=0xc00004dde8 sp=0xc00004ddc8 pc=0x7ff6a366a695
runtime.(*mcache).nextFree(0x1e12b9e07b0, 0x7ff6a365f904, 0x1e1525789a0, 0xc000124820, 0x20)
        /usr/local/Cellar/gotip/stable/src/runtime/malloc.go:880 +0xa5 fp=0xc00004de20 sp=0xc00004dde8 pc=0x7ff6a365f1e5
runtime.mallocgc(0x10, 0x7ff6a3b36060, 0x1, 0x0)
        /usr/local/Cellar/gotip/stable/src/runtime/malloc.go:1061 +0x894 fp=0xc00004dec0 sp=0xc00004de20 pc=0x7ff6a365fc34
runtime.convT2I(0x7ff6a3c14d60, 0xc00004df48, 0x2, 0x0)
        /usr/local/Cellar/gotip/stable/src/runtime/iface.go:413 +0x48 fp=0xc00004df00 sp=0xc00004dec0 pc=0x7ff6a365d088

@randall77
Copy link
Contributor

@randall77 randall77 commented Apr 3, 2021

Unfortunately, you are falling afoul of the rules about storing Go pointers in C memory. https://golang.org/pkg/cmd/cgo/#hdr-Passing_pointers
The Go garbage collector cannot see pointers into the Go heap from C memory.

Running your program with GODEBUG=cgocheck=2 reports a violation.

write of Go pointer 0xc00001e0e0 to non-Go memory 0x2bb040a0
fatal error: Go pointer stored into non-Go memory

I'm not sure what exactly the problem is here, though, as you're only reading from a possibly stale pointer. I'm not sure how exactly the pointer comes back to life on the Go side. But it is definitely unsafe to rely on the contents of the Go memory in testFn, as the byte slice may have already been freed and filled with something else.

@zhuah
Copy link
Author

@zhuah zhuah commented Apr 3, 2021

The C code is just copy Go memory block passed in, it doesn't store go pointer and never pass it back to go.

@randall77
Copy link
Contributor

@randall77 randall77 commented Apr 3, 2021

	d.data = (*C.char)(unsafe.Pointer(ptr))

Writes a Go pointer into C memory.

@zhuah
Copy link
Author

@zhuah zhuah commented Apr 3, 2021

Sorry, i missed that line. Will it be ok if i reset d.data to nil after calling C function?

@randall77
Copy link
Contributor

@randall77 randall77 commented Apr 3, 2021

No, I don't think so. It's not the content of C memory itself (Go never looks at such memory), it's the validity of the d->data pointer on the C side - it may point to junk by the time your C code accesses it.

You probably need to arrange the Go pointer to reach the C code as an argument to a cgo function, not an element of a C-allocated structure. Or do the copy in Go (not sure what your end goal is - that may or may not be feasible).

Closing, as this is not a bug in Go. If you have more questions, probably your best resource is to use one of the resources here: https://github.com/golang/go/wiki/Questions

@golang golang locked and limited conversation to collaborators Apr 3, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants