golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.21k stars 17.57k forks source link

cmd/link: LC_UUID not generated by go linker, resulting in failure to access local network on macOS 15 #68678

Open torarnv opened 2 months ago

torarnv commented 2 months ago

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

torarnv commented 2 months ago

Thanks @seankhliao :)

torarnv commented 2 months ago

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 commented 2 months ago

CC @cherrymui

torarnv commented 2 months ago

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

vsarunas commented 1 week ago

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```