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

"cannot use struct as struct" due to reflect detection mishandling struct literals #799

Open
CaledoniaProject opened this issue Sep 28, 2023 · 7 comments
Labels
bug Something isn't working

Comments

@CaledoniaProject
Copy link

CaledoniaProject commented Sep 28, 2023

What version of Garble and Go are you using?

$ garble version
mvdan.cc/garble v0.10.1

Build settings:
      -buildmode exe
       -compiler gc
  DefaultGODEBUG panicnil=1
     CGO_ENABLED 1
          GOARCH amd64
            GOOS linux
         GOAMD64 v1

$ go version
go version go1.21.1 linux/amd64

What environment are you running Garble on?

go env Output
$ go env
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/root/.cache/go-build'
GOENV='/root/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/root/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/root/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.1'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
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 -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build853078376=/tmp/go-build'

What did you do?

Can't build a package that imports github.com/sijms/go-ora/v2

/root/go/bin/garble build -ldflags '-linkmode=external "-extldflags=-Wl,-headerpad=0x500" -w -s' -o XXX *.go
# github.com/sijms/go-ora/v2
xm5H4KwhW9m.go:5: cannot use struct{g1c12lVZnml int; d1MzwrSH int; jyaY6LhUA int; ngmDRT int; s18DOF int; bt1DjT8 int}{…} (value of type struct{g1c12lVZnml int; d1MzwrSH int; jyaY6LhUA int; ngmDRT int; s18DOF int; bt1DjT8 int}) as struct{varchar int; nvarchar int; raw int; number int; date int; timestamp int} value in struct literal
exit status 2
exit status 1

Where do you think the problem originates from?

@monoidic
Copy link

monoidic commented Oct 8, 2023

Where do you think the problem originates from?

The type Connection struct (defined here) is left "ungarbled", including the fields of maxlen, a field of Connection defined to be a struct literal.
In func NewConnection (defined here), a Connection struct is initialized. However, here, the struct literal is initialized with "garbled" fields. And due to the field name mismatches, it doesn't work.

So, tl;dr, it's improper handling of struct literals. I'll see if I can figure it out, but no promises.

@mvdan
Copy link
Member

mvdan commented Oct 9, 2023

Note that there are a number of related issues, like #785 as well.

@mvdan
Copy link
Member

mvdan commented Nov 18, 2023

Can you please try the latest master again? We've just merged a number of issues relating to structs and type obfuscation.

@lu4p
Copy link
Member

lu4p commented Nov 18, 2023

@mvdan I tested this already with my PR, many deeply nested structs, doesn't work yet.

@CaledoniaProject
Copy link
Author

Nothing changed, I get this error

# github.com/sijms/go-ora/v2
xm5H4KwhW9m.go:5: cannot use struct{g1c12lVZnml int; d1MzwrSH int; jyaY6LhUA int; ngmDRT int; s18DOF int; bt1DjT8 int}{…} (value of type struct{g1c12lVZnml int; d1MzwrSH int; jyaY6LhUA int; ngmDRT int; s18DOF int; bt1DjT8 int}) as struct{varchar int; nvarchar int; raw int; number int; date int; timestamp int} value in struct literal
exit status 2
exit status 1

@lu4p
Copy link
Member

lu4p commented Dec 6, 2023

I tried to debug this.

Here is a minimal reproducer:

package main

import (
	"reflect"
)

func main() {}

var _ = reflect.TypeOf(Connection{})

type Connection struct {
	MaxLen struct {
		Varchar int
	}
}

// NewConnection create a new connection from databaseURL string
func NewConnection() *Connection {
	return &Connection{
		MaxLen: struct {
			Varchar int
		}{
			Varchar: 0x7FFF,
		},
	}
}

Problem being that to go/ssa the inline struct definition doesn't exist, the value is just directly assigned to the Connection struct.

For go/ast the inline struct definition is an inline struct definition without any external struct it "belongs to"

I'm a bit lost here, the only "easy" solution would be to blacklist all struct fields with the same name globally (because of imports).

There is probably another solution using go/ast, but I sense it will be complicated.

@lu4p lu4p added the bug Something isn't working label Dec 6, 2023
@mvdan
Copy link
Member

mvdan commented Dec 6, 2023

Problem being that to go/ssa the inline struct definition doesn't exist, the value is just directly assigned to the Connection struct.

Could you please show what you mean by this, perhaps with the relevant bits of the SSA statements?

I'd really try to avoid going back to go/ast, or mixing it with go/ssa, because that will just bring lots of other disadvantages.

If go/ssa doesn't track enough information needed to statically analyse this case, we could always raise an issue upstream.

@mvdan mvdan changed the title cannot use struct as struct "cannot use struct as struct" due to reflect detection mishandling struct literals Feb 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Development

No branches or pull requests

4 participants