garble icon indicating copy to clipboard operation
garble copied to clipboard

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

Open CaledoniaProject opened this issue 2 years ago • 10 comments

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?

CaledoniaProject avatar Sep 28 '23 01:09 CaledoniaProject

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.

monoidic avatar Oct 08 '23 14:10 monoidic

Note that there are a number of related issues, like https://github.com/burrowers/garble/issues/785 as well.

mvdan avatar Oct 09 '23 19:10 mvdan

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

mvdan avatar Nov 18 '23 14:11 mvdan

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

lu4p avatar Nov 18 '23 16:11 lu4p

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

CaledoniaProject avatar Nov 19 '23 01:11 CaledoniaProject

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 avatar Dec 06 '23 05:12 lu4p

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 avatar Dec 06 '23 20:12 mvdan

same issue, any progress???

cytown avatar Jul 22 '24 04:07 cytown

tried go install mvdan.cc/garble@66b61406c12990b7a02c906388027b8ea6c26b4a , same result.

cytown avatar Jul 22 '24 04:07 cytown