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

cmd/cgo: Go-owned value zeroed after C call returns #27656

Closed
nilsocket opened this issue Sep 13, 2018 · 12 comments

Comments

@nilsocket
Copy link

commented Sep 13, 2018

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

go version go1.11 linux/amd64

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

GOARCH="amd64"
GOOS="linux"
Linux nil 4.18.6-arch1-1-ARCH #1 SMP PREEMPT GNU/Linux

What did you do?

main.go

  • go build -buildmode=c-shared -o libtemp.so; cp *.h *.so ../
package main

import (
	"C"
	"unsafe"
)
import "reflect"

func main() {
}

//export phew
func phew() uintptr {
	res := make([]uint8, 2)
	res[0] = uint8(1)
	res[1] = uint8(2)
	// time.Sleep(time.Millisecond)
	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&res))
	return hdr.Data
}

main.c

  • gcc main.c -ltemp -L. -Wl,-R. ; ./a.out
#include <stdio.h>
#include <inttypes.h>

#include "libtemp.h"

int main(){
	uintptr_t resPtr = phew();
	uint8_t *res = (uint8_t*)resPtr;

	for (int i = 0; i < 2; i++){
		printf("%d\n", res[i]);
	}

	printf("Exiting gracefully");
}

What did you expect to see?

1
2
Exiting gracefully

What did you see instead?

0
0
Exiting gracefully

un-commenting time.Sleep(time.Millisecond), gives correct result.

@nilsocket nilsocket changed the title CGO: returning from function before doing work CGO: returning from function before work is done Sep 13, 2018

@nilsocket

This comment has been minimized.

Copy link
Author

commented Sep 13, 2018

gcc version if helpful,
gcc (GCC) 8.2.1 20180831

@bcmills

This comment has been minimized.

Copy link
Member

commented Sep 13, 2018

cgo-exported functions are not allowed to return pointers to Go memory. (The Go garbage collector does not see the C stack or heap.)

See https://golang.org/cmd/cgo/#hdr-Passing_pointers.

@bcmills bcmills closed this Sep 13, 2018

@bcmills bcmills changed the title CGO: returning from function before work is done cmd/cgo: returning from function before work is done Sep 13, 2018

@bcmills bcmills changed the title cmd/cgo: returning from function before work is done cmd/cgo: Go-owned value zeroed after C call returns Sep 13, 2018

@nilsocket

This comment has been minimized.

Copy link
Author

commented Sep 13, 2018

Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers.

@bcmills
The pointer which I'm passing doesn't contain any Go Pointer.
C code also doesn't pass around Go pointer anywhere else.
Go-owned value is not being zeroed, as stated in the title.

I don't think the problem is related to GC, as I was able to see result when sleeping by one millisecond.
I assume(not sure), a seperate goroutine is being started to assign values, and by the time it assigns it is returning, or some optimization bug, or cgo bug.

@bcmills

This comment has been minimized.

Copy link
Member

commented Sep 13, 2018

	res := make([]uint8, 2)

allocates a slice in Go memory.

	return hdr.Data

returns a Go-owned pointer as a uintptr. The prohibition on passing Go pointers applies even if those pointers are represented as uintptr.

@bcmills

This comment has been minimized.

Copy link
Member

commented Sep 13, 2018

The relevant part of the cgo rule is this:

A Go function called by C code may not return a Go pointer (which implies that it may not return a string, slice, channel, and so forth). A Go function called by C code may take C pointers as arguments, and it may store non-pointer or C pointer data through those pointers, but it may not store a Go pointer in memory pointed to by a C pointer. A Go function called by C code may take a Go pointer as an argument, but it must preserve the property that the Go memory to which it points does not contain any Go pointers.

@nilsocket

This comment has been minimized.

Copy link
Author

commented Sep 13, 2018

Note that values of some Go types, other than the type's zero value, always include Go pointers. This is true of string, slice, interface, channel, map, and function types.

Source which you have pointed out means these types contain Go pointers.

Go pointer (which implies that it may not return a string, slice, channel, and so forth)

@nilsocket

This comment has been minimized.

Copy link
Author

commented Sep 13, 2018

@bcmills consider re-opening the issue.

@nilsocket

This comment has been minimized.

Copy link
Author

commented Sep 13, 2018

Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers.

@bcmills
The pointer which I'm passing doesn't contain any Go Pointer.
C code also doesn't pass around Go pointer anywhere else.
Go-owned value is not being zeroed, as stated in the title.

I don't think the problem is related to GC, as I was able to see result when sleeping by one millisecond.
I assume(not sure), a seperate goroutine is being started to assign values, and by the time it assigns it is returning, or some optimization bug, or cgo bug.

  • The pointer which I'm passing doesn't contain any other Go Pointer.
	res := make([]uint8, 2)

allocates a slice in Go memory.

	return hdr.Data

returns a Go-owned pointer as a uintptr. The prohibition on passing Go pointers applies even if those pointers are represented as uintptr.

But the pointer which I'm passing doesn't contain any other Go pointer, which means I can pass it to C.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Sep 13, 2018

The rule, which @bcmills quoted above is: "A Go function called by C code may not return a Go pointer." You are looking at the rule for pointers that may be passed from Go code when calling C code. That is not what your program is doing. Your program is C code calling a Go function. That rule for that case is that the Go function may not return a Go pointer. It's not OK for an exported Go function to return a Go pointer that does not point to other Go pointers; an exported Go function may not return a Go pointer at all.

@nilsocket

This comment has been minimized.

Copy link
Author

commented Sep 13, 2018

@ianlancetaylor You cleared the point, but

A Go function called by C code may take a Go pointer as an argument, but it must preserve the property that the Go memory to which it points does not contain any Go pointers.

If a Go function which is called by C code can take a Go pointer as argument, then how did the C program get the Go pointer in first place.

@bcmills

This comment has been minimized.

Copy link
Member

commented Sep 13, 2018

If a Go function which is called by C code can take a Go pointer as argument, then how did the C program get the Go pointer in first place.

The C function gets the Go pointer by itself being called by a Go function. (If you have a Go→C→Go call chain, the values passed in the Go→C call are pinned for the duration and therefore available for the C→Go call, since the C→Go call must return before the Go→C call can return.)

@nilsocket

This comment has been minimized.

Copy link
Author

commented Sep 13, 2018

@bcmills thanks a lot for clearing up.

@golang golang locked and limited conversation to collaborators Sep 13, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
4 participants
You can’t perform that action at this time.