go: runtime: C.setgid hangs on Linux
What version of Go are you using (go version)?
go version devel go1.18-a8e6556445 Fri Apr 1 09:06:13 2022 +0000 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env)?
go env Output
GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/home/codespace/.cache/go-build" GOENV="/home/codespace/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="devel go1.18-a8e6556445 Fri Apr 1 09:06:13 2022 +0000" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/workspaces/go/gotest/go.mod" GOWORK="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3393830914=/tmp/go-build -gno-record-gcc-switches"
What did you do?
- Compile this C snippet into a shared library:
#include <pthread.h>
#include <stdlib.h>
pthread_mutex_t *lock_new(void)
{
pthread_mutex_t *lock = malloc(sizeof(pthread_mutex_t));
pthread_mutex_lock(lock);
return lock;
}
$ gcc -fPIC -c foo.c
$ gcc -shared -o foo.so -pthread foo.o
- From Go, load the shared library at runtime and then call
setgid:
package main
/*
#include <dlfcn.h>
#include <unistd.h>
#cgo LDFLAGS: -ldl -static
*/
import "C"
import "fmt"
func main() {
fmt.Println("Step 1")
if C.dlopen(C.CString("./foo.so"), C.RTLD_NOW) == nil {
panic("library not found")
}
fmt.Println("Step 2")
C.setgid(0)
fmt.Println("Step 3")
}
$ go run .
What did you expect to see?
# main
/usr/bin/ld: /tmp/go-link-95168809/000001.o: in function `_cgo_1403fb244f50_Cfunc_dlopen':
/tmp/go-build/cgo-gcc-prolog:54: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
Step 1
Step 2
Step 3
What did you see instead?
# main
/usr/bin/ld: /tmp/go-link-95168809/000001.o: in function `_cgo_1403fb244f50_Cfunc_dlopen':
/tmp/go-build/cgo-gcc-prolog:54: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
Step 1
Step 2
The C.setgid call hangs indefinitely.
Observations
- A pure C program doing the same calls does not hang
- It also hangs if the
dlopenandsetgidcalls are done from the C preamble - It does not hang if
pthreadsare not used - It does not hang if
-staticis not set in the LDFLAGS directive in the C preamble
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 1
- Comments: 16 (16 by maintainers)
This C program seg faults on my machine if linked with
-static:(foo.so is the same as original.)
It seems to die in the SIGSETXID handler
It doesn’t crash if it is not linked statically, nor if the
setgidcall is not made on a non-main thread (it doesn’t seem to matter wheredlopencall is made).This is a linux/amd64 machine with glibc 2.33.
I’m leaning to think that this is probably a bug in the C library.
That makes sense since the CGo mechanism for handling
syscall.Setgid()should be in effect and nothing about thesyscall.AllThreadsSyscall()mechanism (substantially changed ingo1.18vsgo1.17) should be in play.The observation that compilation with
-static, in the C preamble of the.gofile, might be important. I found this bug that seems to be saying that something about thedlopenmechanism is sensitive to the right dynamic symbols being available: https://sourceware.org/bugzilla/show_bug.cgi?id=16628 . In general, this kind of linkage and runtime behavior looks pretty subtle.