onsi / gomega

Ginkgo's Preferred Matcher Library
http://onsi.github.io/gomega/
MIT License
2.19k stars 284 forks source link

panic while calling format.formatType() by cgo struct #469

Open Cylkal opened 3 years ago

Cylkal commented 3 years ago

go version go1.16.7 linux/amd64

when use matcher gomega.Equal it will use format.Message() to format actual and expected. When format a struct type, the call chain is as follows: Message -> Object -> formatValue ->formatStruct when format struct contains a field which is a cgo struct, it may be panic:

Test Panicked
    runtime error: invalid memory address or nil pointer dereference
    /usr/local/go/src/runtime/panic.go:212

    Full Stack Trace
    reflect.Value.Uint(...)
        /usr/local/go/src/reflect/value.go:1945
    github.com/onsi/gomega/format.formatValue(0x8e1840, 0x12d, 0x1a8, 0x5, 0x12d, 0x1a8)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:292 +0x2ae
    github.com/onsi/gomega/format.formatValue(0x8d5de0, 0x7f0cf80020d0, 0x1b6, 0x5, 0x8d5de0, 0x7f0cf80020d0)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:304 +0xad8
    github.com/onsi/gomega/format.formatStruct(0x955ac0, 0x7f0cf80020b0, 0x1b9, 0x4, 0x955ac0, 0x9)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:402 +0x1de
    github.com/onsi/gomega/format.formatValue(0x955ac0, 0x7f0cf80020b0, 0x1b9, 0x4, 0x7f0cf80020b0, 0x1b9)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:318 +0xc1d
    github.com/onsi/gomega/format.formatValue(0x8fa1a0, 0xc0000980d0, 0x1b6, 0x4, 0x8fa1a0, 0xc0000980d0)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:304 +0xad8
    github.com/onsi/gomega/format.formatStruct(0x934480, 0xc0000980c0, 0x199, 0x3, 0x934480, 0xc000098000)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:402 +0x1de
    github.com/onsi/gomega/format.formatValue(0x934480, 0xc0000980c0, 0x199, 0x3, 0xc0000980c0, 0x199)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:318 +0xc1d
    github.com/onsi/gomega/format.formatValue(0x93dd60, 0xc0000ac410, 0x196, 0x3, 0x93dd60, 0xc0000ac410)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:304 +0xad8
    github.com/onsi/gomega/format.formatStruct(0x955be0, 0xc0000ac3f0, 0x199, 0x2, 0x955be0, 0xc0000ac500)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:402 +0x1de
    github.com/onsi/gomega/format.formatValue(0x955be0, 0xc0000ac3f0, 0x199, 0x2, 0xc0000ac3f0, 0x199)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:318 +0xc1d
    github.com/onsi/gomega/format.formatValue(0x8fa200, 0xc000096390, 0x196, 0x2, 0x8fa200, 0xc000096390)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:304 +0xad8
    github.com/onsi/gomega/format.formatStruct(0x960b80, 0xc000096360, 0x199, 0x1, 0x960b80, 0xc000096400)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:402 +0x1de
    github.com/onsi/gomega/format.formatValue(0x960b80, 0xc000096360, 0x199, 0x1, 0xc000096360, 0x199)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:318 +0xc1d
    github.com/onsi/gomega/format.formatValue(0x928260, 0xc000096360, 0x16, 0x1, 0x22, 0xa24ee0)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:304 +0xad8
    github.com/onsi/gomega/format.Object(0x928260, 0xc000096360, 0x1, 0x0, 0x0)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:215 +0x157
    github.com/onsi/gomega/format.Message(0x928260, 0xc000096360, 0x9855a8, 0x8, 0xc0001d30a0, 0x1, 0x1, 0xc0000963c0, 0xc00009e100)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/format/format.go:86 +0x165
    github.com/onsi/gomega/matchers.(*EqualMatcher).FailureMessage(0xc0000a6530, 0x928260, 0xc000096360, 0x0, 0x0)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/matchers/equal_matcher.go:37 +0xd0
    github.com/onsi/gomega/internal.(*Assertion).match(0xc00009e1c0, 0xa1d8b0, 0xc0000a6530, 0x1, 0x0, 0x0, 0x0, 0xc0000a6530)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/internal/assertion.go:74 +0xfb
    github.com/onsi/gomega/internal.(*Assertion).To(0xc00009e1c0, 0xa1d8b0, 0xc0000a6530, 0x0, 0x0, 0x0, 0xc00009e1c0)
        /root/go/pkg/mod/github.com/onsi/gomega@v1.16.0/internal/assertion.go:38 +0xbe
    tscan/flow/servicescan.glob..func2.4.2()
        /root/code/tscan-open/flow/servicescan/match_test.go:180 +0x1bc
    github.com/onsi/ginkgo/internal/leafnodes.(*runner).runSync(0xc00044a960, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/leafnodes/runner.go:113 +0xa3
    github.com/onsi/ginkgo/internal/leafnodes.(*runner).run(0xc00044a960, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/leafnodes/runner.go:64 +0x15c
    github.com/onsi/ginkgo/internal/leafnodes.(*ItNode).Run(0xc000464640, 0xa166e0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/leafnodes/it_node.go:26 +0x87
    github.com/onsi/ginkgo/internal/spec.(*Spec).runSample(0xc0002f31d0, 0x0, 0xa166e0, 0xc00039d280)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/spec/spec.go:215 +0x72f
    github.com/onsi/ginkgo/internal/spec.(*Spec).Run(0xc0002f31d0, 0xa166e0, 0xc00039d280)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/spec/spec.go:138 +0xf2
    github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).runSpec(0xc000323080, 0xc0002f31d0, 0x1)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/specrunner/spec_runner.go:200 +0x111
    github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).runSpecs(0xc000323080, 0x1)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/specrunner/spec_runner.go:170 +0x147
    github.com/onsi/ginkgo/internal/specrunner.(*SpecRunner).Run(0xc000323080, 0xc0003bd4f8)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/specrunner/spec_runner.go:66 +0x117
    github.com/onsi/ginkgo/internal/suite.(*Suite).Run(0xc0003dad20, 0x7f0d0ee6b9c0, 0xc0001b1080, 0x98b39b, 0x11, 0xc0003fa1c0, 0x1, 0x1, 0xa1fc18, 0xc00039d280, ...)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/internal/suite/suite.go:79 +0x546
    github.com/onsi/ginkgo.runSpecsWithCustomReporters(0xa16e00, 0xc0001b1080, 0x98b39b, 0x11, 0xc000052f28, 0x1, 0x1, 0x0)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/ginkgo_dsl.go:238 +0x218
    github.com/onsi/ginkgo.RunSpecs(0xa16e00, 0xc0001b1080, 0x98b39b, 0x11, 0x8cc9715da2939f)
        /root/go/pkg/mod/github.com/onsi/ginkgo@v1.16.4/ginkgo_dsl.go:213 +0xa7
    tscan/flow/servicescan_test.TestServicescan(0xc0001b1080)
        /root/code/tscan-open/flow/servicescan/servicescan_suite_test.go:12 +0x7f
    testing.tRunner(0xc0001b1080, 0x9ac008)
        /usr/local/go/src/testing/testing.go:1193 +0xef
    created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:1238 +0x2b3
------------------------------

the cgo struct like:

/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -L. -lpcre
#include <pcre.h>
#include <string.h>
*/
import "C"

type pcreExtra C.struct_pcre_extra

where pcre_extra define:

typedef struct pcre_extra {
  unsigned long int flags;        /* Bits for which fields are set */
  void *study_data;               /* Opaque data from pcre_study() */
  unsigned long int match_limit;  /* Maximum number of calls to match() */
  void *callout_data;             /* Data passed back in callouts */
  const unsigned char *tables;    /* Pointer to character tables */
  unsigned long int match_limit_recursion; /* Max recursive calls to match() */
  unsigned char **mark;           /* For passing back a mark pointer */
  void *executable_jit;           /* Contains a pointer to a compiled jit code */
} pcre_extra;

use fmt, it print like:

{flags:65 study_data:0x7fd858002340 match_limit:140567166067728 callout_data:0x7fd89569a13d tables:0x12d match_limit_recursion:140567166067544 mark:0x7fd89569a146 executable_jit:0x7fd8580026a0}

and panic occurs when visit field match_limit_recursion by call reflect.Value.Uint().

Could you avoid format cgo struct, or other method like add a new format methd not format unexported field?

onsi commented 3 years ago

hey @Cylkal - sorry I haven't been able to get to this sooner. I'll have to carve out some time to take a deeper look at it. For now, if it helps you get unstuck, you could implement the GomegaStringer interface to control how your object's output is formatted and possibly avoid the panic?

Cylkal commented 3 years ago

hey @Cylkal - sorry I haven't been able to get to this sooner. I'll have to carve out some time to take a deeper look at it. For now, if it helps you get unstuck, you could implement the GomegaStringer interface to control how your object's output is formatted and possibly avoid the panic?

yeah, this may be one way.