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

x/tools/go/analysis/passes/printf: catches dual use variable strings passed to fmt.Sprintf #70088

Closed
odeke-em opened this issue Oct 29, 2024 · 5 comments
Labels
Tools This label describes issues relating to any tools in the x/tools repository.
Milestone

Comments

@odeke-em
Copy link
Member

odeke-em commented Oct 29, 2024

Go version

go version devel go1.24-889abb17e1 Sat Oct 26 02:44:00 2024 +0000 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOAUTH='netrc'
GOBIN='/home/emmanuel/go/src/go.googlesource.com/go/bin'
GOCACHE='/home/emmanuel/.cache/go-build'
GOENV='/home/emmanuel/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/emmanuel/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/emmanuel/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/emmanuel/go/src/go.googlesource.com/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/emmanuel/go/src/go.googlesource.com/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='devel go1.24-889abb17e1 Sat Oct 26 02:44:00 2024 +0000'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/emmanuel/.config/go/telemetry'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/emmanuel/go/src/github.com/googleapis/google-cloud-go/spanner/go.mod'
GOWORK='/home/emmanuel/go/src/github.com/googleapis/google-cloud-go/go.work'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3859058783=/tmp/go-build -gno-record-gcc-switches'

What did you do?

  1. Git clone https://github.com/googleapis/google-cloud-go
  2. Change directories into spanner, then try to run the tests
$ cd spanner && go test
# cloud.google.com/go/spanner
# [cloud.google.com/go/spanner]
./transaction.go:1342:70: non-constant format string in call to cloud.google.com/go/spanner.spannerErrorf
FAIL	cloud.google.com/go/spanner [build failed]

but let's examine that code https://github.com/googleapis/google-cloud-go/blob/255c6bfcdd3e844dcf602a829bfa2ce495bcd72e/spanner/transaction.go#L1351

        if resp.Status != nil && resp.Status.Code != 0 {
		return counts, spannerErrorf(codes.Code(uint32(resp.Status.Code)), resp.Status.Message)
	}

which calls

https://github.com/googleapis/google-cloud-go/blob/255c6bfcdd3e844dcf602a829bfa2ce495bcd72e/spanner/errors.go#L124-L132

func spannerErrorf(code codes.Code, format string, args ...interface{}) error {
	msg := fmt.Sprintf(format, args...)
	wrapped, _ := apierror.FromError(status.Error(code, msg))
	return &Error{
		Code: code,
		err:  wrapped,
		Desc: msg,
	}
}

The docs for (fmt.Sprintf)[https://pkg.go.dev/fmt#Sprintf] don't specify some strict requirement that the first argument has to be constant and never has over the past 23 Go releases
fmtSprintfGoDoc

That code is valid because despite the dual usage, here they just passed in format and not args. It is a very common pattern to pass in errors generated by RPC systems as errors to observability spans.
But even if we passed in arguments to the format specifier, that isn't a fatal problem; instead it is going to break a whole lot of libraries widely used in the ecosystem for example:

What did you see happen?

Failed testing and compilation!

What did you expect to see?

No problems at all.

Kindly cc-ing @alandonovan

@gabyhelp
Copy link

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

@ianlancetaylor
Copy link
Member

One can of course use go test -vet=off. This does not break the Go compatibility guarantee.

While I haven't looked at the full source code, the code you show actually looks problematic.

spannerErrorf(codes.Code(uint32(resp.Status.Code)), resp.Status.Message)

That is going to pass some non-constant value as the first argument to fmt.Sprintf. You are of course correct that this is valid code. But if resp.Status.Message contains % character, this will almost certainly do the wrong thing. It would be safer to write that line as

    spannerErrorf(codes.Code(uint32(resp.Status.Code)), "%s", resp.Status.Message)

That is what the new vet check is encouraging.

The new vet check was added in #60529. In the discussion there the check reliably found potential bugs. And that is how I would describe this code as well.

@seankhliao seankhliao changed the title cmd/go, x/tools/: overzealous static analyzer for printf prevents compilation of correct code when no arguments are passed in for Go1.24; breaks Go compatibility promise for fmt.Sprint* x/tools/go/analysis/passes/printf: catches dual use variable strings passed to fmt.Sprintf Oct 29, 2024
@gopherbot gopherbot added the Tools This label describes issues relating to any tools in the x/tools repository. label Oct 29, 2024
@gopherbot gopherbot added this to the Unreleased milestone Oct 29, 2024
@adonovan
Copy link
Member

adonovan commented Oct 29, 2024

In this case the analyzer has found a genuine problem in the code: the call spannerErrorf(code, resp.Status.Message) behaves incorrectly if the value of Message contains a percent symbol, and as far as I can tell here, the value of Message can be any arbitrary string.

Perhaps the error message could be clarified, but this is not a false positive.

spannerErrorf(codes.Code(uint32(resp.Status.Code)), "%s", resp.Status.Message)
That is what the new vet check is encouraging.

Indeed, the use of "%s" is appropriate and correct, and I routinely make suggestions of this form in code reviews.

[Sorry, I failed to notice @ianlancetaylor already said this.]

@adonovan
Copy link
Member

I'm going to close this as "working as intended". Feel free to reopen if you disagree.

@cagedmantis
Copy link
Contributor

Closed as "working as intended."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Tools This label describes issues relating to any tools in the x/tools repository.
Projects
None yet
Development

No branches or pull requests

6 participants