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/link: LC_UUID not generated by go linker, resulting in failure to access local network on macOS 15 #68678

Closed
torarnv opened this issue Jul 31, 2024 · 14 comments
Assignees
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Milestone

Comments

@torarnv
Copy link

torarnv commented Jul 31, 2024

Go version

go version go1.22.5 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/torarne/Library/Caches/go-build'
GOENV='/Users/torarne/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/var/folders/7w/09x_jw654938vd439254v6r80000gn/T//gomodcache'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/torarne/.go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.22.5/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.22.5/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.5'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/7w/09x_jw654938vd439254v6r80000gn/T/go-build1376446515=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

Go binaries built with the default linker for macOS do not have a LC_UUID load command. This UUID seems important to the network privacy machinery on macOS, that in macOS 15 now includes local network access.

Running a small go binary that does a HTTP request to a local network HTTP server results in a permission request from the OS, but accepting this does not result in the go process being able to access the server.

The logs show:
nehelper +[NEProcessInfo copyUUIDsForExecutable:]_block_invoke: failed to get UUIDs for /Users/foo/FetchGo

So it seems the nehelper relies on the binary having a UUID to allow network requests through.

I can work around this by passing -ldflags="-linkmode=external" when building the go binary, which does produce a UUID:

❯ dwarfdump -u FetchGo
UUID: C7B47B23-54A7-3F52-837D-E455B57855A1 (arm64) FetchGo

And in this case the HTTP request works after accepting the privacy dialog. But I don't think this will work when cross-compiling darwin binaries on Linux e.g.

Note that for testing this, the binary must be run as an app bundle, or launch agent -- not via SSH/Terminal, as in the latter case the permission to access the network is inherited by those processes, and they have an UUID.

What did you see happen?

Network request fails to route

What did you expect to see?

Network request succeeds

@seankhliao seankhliao changed the title LC_UUID not generated by go linker, resulting in failure to access local network on macOS 15 cmd/link: LC_UUID not generated by go linker, resulting in failure to access local network on macOS 15 Jul 31, 2024
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 31, 2024
@torarnv
Copy link
Author

torarnv commented Jul 31, 2024

Thanks @seankhliao :)

@torarnv
Copy link
Author

torarnv commented Jul 31, 2024

Sample reproducer. Change plist files to point to web server running in local network. Loading the launch agents should bring up permission dialogs.

missing-lc-uuid.zip

@ianlancetaylor
Copy link
Contributor

CC @cherrymui

@mknyszek mknyszek added this to the Backlog milestone Jul 31, 2024
@mknyszek mknyszek added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jul 31, 2024
@torarnv
Copy link
Author

torarnv commented Jul 31, 2024

See also https://forums.developer.apple.com/forums/thread/737416 for a description of the UUID mechanism.

@cherrymui cherrymui self-assigned this Jul 31, 2024
@vsarunas
Copy link

Also ran into this; this now affects all existing Go applications that are launched via launchctl; even when the macOS Sequoia firewall is off.

Go application on the new OS simply has an error: connect: no route to host

Minimal example:

package main
import (
	"fmt"
	"net"
	"os"
	"time"
)
func main() {
	if len(os.Args) != 2 {
		fmt.Println("Usage: <address:port>")
		return
	}
	for {
		if conn, err := net.Dial("tcp", os.Args[1]); err == nil {
			buf := make([]byte, 1024)
			n, _ := conn.Read(buf)
			fmt.Println("Connected, response:", string(buf[:n]))
			conn.Close()
		} else {
			fmt.Println("Error:", err, "Retrying...")
		}
		time.Sleep(1 * time.Second)
	}
}

Build:

go build -o /opt/homebrew/bin/go-connect error-test.go

Config for auto start and restart:

cat <<EOF > ~/Library/LaunchAgents/go-connect.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>go-connect</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/go-connect</string>
        <string>192.168.1.217:22</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/opt/homebrew/var/log/go-connect</string>
</dict>
</plist>
EOF

Start:

launchctl load -w ~/Library/LaunchAgents/go-connect.plist

Monitor failures:

tail -f /opt/homebrew/var/log/go-connect 
Error: dial tcp 192.168.1.217:22: connect: no route to host Retrying...
Error: dial tcp 192.168.1.217:22: connect: no route to host Retrying...
Error: dial tcp 192.168.1.217:22: connect: no route to host Retrying...

Network is fine:

ping  192.168.1.217    
PING 192.168.1.217 (192.168.1.217): 56 data bytes
64 bytes from 192.168.1.217: icmp_seq=0 ttl=64 time=6.015 ms
64 bytes from 192.168.1.217: icmp_seq=1 ttl=64 time=4.680 ms

arp -an | grep 192.168.1.217
? (192.168.1.217) at bc:d0:74[...] on en0 ifscope [ethernet]

Reported this to Apple as:

Go binaries do not have LC_UUID and result in network failures [NEProcessInfo copyUUIDsForExecutable:]_block_invoke: failed to get UUID #FB15168752```

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/618595 mentions this issue: cmd/link: generate Mach-O UUID when -B flag is specified

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/618597 mentions this issue: cmd/internal/buildid: skip over Mach-O UUID from buildid computation

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/618598 mentions this issue: cmd/link: on Mach-O, generate LC_UUID by default

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/618596 mentions this issue: cmd/internal/macho: new package for handling mach-o files in toolchain

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/618599 mentions this issue: cmd/link: apply -B UUID to external linking on Mach-O

gopherbot pushed a commit that referenced this issue Oct 21, 2024
Currently, on Mach-O, the Go linker doesn't generate LC_UUID in
internal linking mode. This causes some macOS system tools unable
to track the binary, as well as in some cases the binary unable
to access local network on macOS 15.

This CL makes the linker start generate LC_UUID. Currently, the
UUID is generated if the -B flag is specified. And we'll make it
generate UUID by default in a later CL. The -B flag is currently
for generating GNU build ID on ELF, which is a similar concept to
Mach-O's UUID. Instead of introducing another flag, we just use
the same flag and the same setting. Specifically, "-B gobuildid"
will generate a UUID based on the Go build ID.

For #68678.

Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64_14,gotip-darwin-arm64_13
Change-Id: I90089a78ba144110bf06c1c6836daf2d737ff10a
Reviewed-on: https://go-review.googlesource.com/c/go/+/618595
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Ingo Oeser <nightlyone@googlemail.com>
Reviewed-by: Than McIntosh <thanm@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
gopherbot pushed a commit that referenced this issue Oct 21, 2024
Currently the linker has some code handling and manipulating
Mach-O files. Specifically, it augments the debug/macho package
with file offset and length, so the content can be handled or
updated easily with the file.

Move this code to an internal package, so it can be used by other
part of the toolchain, e.g. buildid computation.

For #68678.

Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64_14,gotip-darwin-arm64_13
Change-Id: I2311af0a06441b7fd887ca5c6ed9e6fc44670a16
Reviewed-on: https://go-review.googlesource.com/c/go/+/618596
Reviewed-by: Than McIntosh <thanm@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
gopherbot pushed a commit that referenced this issue Oct 21, 2024
With the "-B gobuildid" linker option (which will be the default
on some platforms), the host build ID (GNU build ID, Mach-O UUID)
depends on the Go buildid. If the host build ID is included in the
Go buildid computation, it will lead to convergence problem for
the toolchain binaries. So ignore the host build ID in the buildid
computation.

This CL only handles Mach-O UUID. ELF GNU build ID will be handled
later.

For #68678.
For #63934.

Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64_14,gotip-darwin-arm64_13
Change-Id: Ie8ff20402a1c6083246d25dea391140c75be40d0
Reviewed-on: https://go-review.googlesource.com/c/go/+/618597
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Than McIntosh <thanm@golang.org>
gopherbot pushed a commit that referenced this issue Oct 21, 2024
Currently, on Mach-O, the -B UUID setting is only applied in
internal linking mode, whereas in external linking mode the UUID
is always rewritten to a hash of Go build ID. This CL makes it
apply to external linking as well. This makes the behavior
consistent on both linkmodes, and also consistent with the -B
flag's behavior for GNU build ID on ELF.

Add tests.

Updates #68678.

Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64_14,gotip-darwin-arm64_13
Change-Id: I276a5930e231141440cdba16e8812df28ac4237b
Reviewed-on: https://go-review.googlesource.com/c/go/+/618599
Reviewed-by: Than McIntosh <thanm@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
@dmitshur dmitshur added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Oct 21, 2024
@dmitshur dmitshur modified the milestones: Backlog, Go1.24 Oct 21, 2024
@cherrymui
Copy link
Member

cherrymui commented Oct 22, 2024

@gopherbot please backport this. This causes programs fail to access local network on macOS 15.

The full fix would be backporting CL 618595, CL 618596, CL 618597, and CL 618598, which may be too complex for backport. A simpler alternative is backporting just CL 618595, which enables generating LC_UUID with a flag. It still needs a flag, but not the C toolchain, therefore helpful for cross compilation. We might be able to do a hacky way to enable it by default except for the toolchain binaries. I'll look into that.

@gopherbot
Copy link
Contributor

Backport issue(s) opened: #69991 (for 1.22), #69992 (for 1.23).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/622595 mentions this issue: [release-branch.go1.23] cmd/link: generate Mach-O UUID when -B flag is specified

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/622596 mentions this issue: [release-branch.go1.22] cmd/link: generate Mach-O UUID when -B flag is specified

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsFix The path to resolution is known, but the work has not been done. OS-Darwin
Projects
Development

No branches or pull requests

8 participants