Skip to content

cmd/go: go vet -trimpath with CGo pollutes cache with absolute paths under relative BuildIDs #81805

Open

Description

@jwebb

Go version

go version go1.27.1 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN='/home/jwebb/gocode/bin'
GOCACHE='/tmp/go-vet-worktrees.Jz1u78/cache'
GOCACHEPROG=''
GODEBUG=''
GOENV=''
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3008632129=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='/home/jwebb/gocode/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPACKAGESDRIVER=''
GOPATH='/home/jwebb/gocode'
GOPRIVATE=''
GOPROXY='off'
GOROOT='/home/jwebb/opt/go'
GOSUMDB='off'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/jwebb/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/home/jwebb/opt/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.27.1'
GOWORK='off'
PKG_CONFIG='pkg-config'

What did you do?

Run this bash script:

#!/usr/bin/env bash
set -eu

# Isolate the normal disk cache and ignore Go/Git user configuration.
export GOENV=off GOFLAGS= GOWORK=off GOTOOLCHAIN=local CGO_ENABLED=1
export GOCACHEPROG= GOPROXY=off GOSUMDB=off GOPRIVATE=
export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null

repro_dir=$(mktemp -d "${TMPDIR:-/tmp}/go-vet-worktrees.XXXXXX")
export GOCACHE="$repro_dir/cache"
printf 'Reproducer directory: %s\n' "$repro_dir"
go version
go env

git init -q "$repro_dir/consumer"
cat > "$repro_dir/consumer/go.mod" <<'EOF'
module example.com/cgocache

go 1.20
EOF
cat > "$repro_dir/consumer/cgo.go" <<'EOF'
package cgocache

import "C"
EOF
git -C "$repro_dir/consumer" add go.mod cgo.go
git -C "$repro_dir/consumer" -c user.name=Reproducer \
    -c user.email=reproducer@example.com commit -qm initial
git -C "$repro_dir/consumer" worktree add --detach -q "$repro_dir/producer" HEAD

# Populate compilation artifacts, but no vet results, from the other worktree.
# (Same path would be hit by go vet, but we'd need a more complex reproducer to avoid the cached final result.)
go -C "$repro_dir/producer" test -trimpath -vet=off .
git -C "$repro_dir/consumer" worktree remove "$repro_dir/producer"

# This should use consumer/cgo.go, but tries to open producer/cgo.go.
go -C "$repro_dir/consumer" vet -trimpath .

What did you see happen?

The final vet command tries to open the removed producer worktree's source file:

?   	example.com/cgocache	[no test files]
example.com/cgocache: can't parse raw cgo file: open /tmp/go-vet-worktrees.XXXXXX/producer/cgo.go: no such file or directory

What did you expect to see?

go vet -trimpath should succeed using consumer/cgo.go. The package in the consumer worktree is complete and identical to the committed package that populated the cache. The producer worktree should no longer be needed.

Specifically what has happened here is that the generated *.cgo1.go file contains an absolute //line directive naming the producer's source file, i.e. no trimming. With vet -trimpath, its contribution to the cache key is trimmed, permitting reuse in the other worktree. The cgocall analyzer follows that directive when locating the original cgo source.

I'm using Git worktrees in the reproducer as it seems the most likely reason people will hit this, particularly as LLMs have made worktrees more popular. It also applies when using a shared build cache via GOCACHEPROG (our actual use case), or indeed simply maintaining multiple independent checkouts.

Note that simply disabling trimpath allows the build to succeed, but only due to vet no longer being able to share caching between trees, which becomes a build performance issue for large codebases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions