traefik / yaegi

Yaegi is Another Elegant Go Interpreter
https://pkg.go.dev/github.com/traefik/yaegi
Apache License 2.0
7.15k stars 356 forks source link

Segfault when using zap logging #1639

Open pvbouwel opened 6 months ago

pvbouwel commented 6 months ago

The following program uber.go triggers an unexpected result

package main

import (
    "fmt"
    "time"

    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
)

func NewLogger(logLevel string) (*zap.Logger, error) {
    config := zap.Config{
        Encoding:         "json",
        OutputPaths:      []string{"stdout"},
        ErrorOutputPaths: []string{"stdout"},
        EncoderConfig: zapcore.EncoderConfig{
            TimeKey: "time",
            EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
                enc.AppendString(t.UTC().Format("2006-01-02T15:04:05Z"))
            },
            NameKey:       "logger",
            MessageKey:    "message",
            LevelKey:      "level",
            EncodeLevel:   zapcore.LowercaseLevelEncoder,
        },
    }

    var level zapcore.Level
    err := level.UnmarshalText([]byte(logLevel))
    if err != nil {
        panic(err)
    }
    config.Level = zap.NewAtomicLevelAt(level)
    return config.Build()
}

func main() {
  l, err := NewLogger("INFO")
  if err != nil {
    fmt.Print("Creating logger failed")
  }
  l.Info("Hello log")
}

Expected result

$ go run uber.go
{"level":"info","time":"2024-05-31T14:21:44Z","message":"Hello log"}

Got

$ yaegi run uber.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x128 pc=0x7f5c3f]

goroutine 1 [running]:
github.com/traefik/yaegi/interp.nodeType2(0xc00018e908, 0xc00067e750, 0xc000522500, {0xc000595220, 0x3, 0x4})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/type.go:1083 +0x3aff
github.com/traefik/yaegi/interp.genType(0xc00018e908, 0xc00067e750, {0xc00003a720, 0x19}, 0xc00051f040, {0xc000540e48, 0x1, 0x1}, {0xc000595220, 0x3, ...})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/type.go:1136 +0xcb
github.com/traefik/yaegi/interp.nodeType2(0xc00018e908, 0xc00067e750, 0xc0004f0a00, {0xc000595220, 0x2, 0x4})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/type.go:847 +0x56bb
github.com/traefik/yaegi/interp.nodeType2(0xc00018e908, 0xc00067e750, 0xc0004f08c0, {0xc000693010, 0x1, 0x2})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/type.go:425 +0x2f4b
github.com/traefik/yaegi/interp.nodeType2(0xc00018e908, 0xc00067e750, 0xc0004f03c0, {0xc00080dbc8, 0x0, 0x1})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/type.go:1079 +0x3ad7
github.com/traefik/yaegi/interp.nodeType(...)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/type.go:400
github.com/traefik/yaegi/interp.(*Interpreter).gta.func1(0xc0004f0140)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:320 +0x11d5
github.com/traefik/yaegi/interp.(*node).Walk(0xc0004f0140, 0xc0005430c8, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:282 +0x2e
github.com/traefik/yaegi/interp.(*node).Walk(0xc0004f0000, 0xc0005430c8, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*node).Walk(0xc0004ef900, 0xc0005430c8, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*Interpreter).gta(0xc00018e908, 0xc0004ef900, {0xc00003a600, 0x1d}, {0xc00069e001, 0x16}, {0xc0008087f0, 0x6})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:20 +0x22b
github.com/traefik/yaegi/interp.(*Interpreter).importSrc(0xc00018e908, {0xc00003a3c0, 0x1e}, {0xc00069e001, 0x16}, 0x1)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/src.go:109 +0x925
github.com/traefik/yaegi/interp.(*Interpreter).gta.func1(0xc00080b2c0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:273 +0xcdb
github.com/traefik/yaegi/interp.(*node).Walk(0xc00080b2c0, 0xc000543e10, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:282 +0x2e
github.com/traefik/yaegi/interp.(*node).Walk(0xc00080af00, 0xc000543e10, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*node).Walk(0xc00080ac80, 0xc000543e10, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*Interpreter).gta(0xc00018e908, 0xc00080ac80, {0xc00003a3c0, 0x1e}, {0xc00003a341, 0x17}, {0xc00011d3d0, 0x7})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:20 +0x22b
github.com/traefik/yaegi/interp.(*Interpreter).importSrc(0xc00018e908, {0xc0001201e0, 0x16}, {0xc00003a341, 0x17}, 0x1)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/src.go:109 +0x925
github.com/traefik/yaegi/interp.(*Interpreter).gta.func1(0xc0004ecc80)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:273 +0xcdb
github.com/traefik/yaegi/interp.(*node).Walk(0xc0004ecc80, 0xc000544b58, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:282 +0x2e
github.com/traefik/yaegi/interp.(*node).Walk(0xc0004ec640, 0xc000544b58, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*node).Walk(0xc0004ec3c0, 0xc000544b58, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*Interpreter).gta(0xc00018e908, 0xc0004ec3c0, {0xc0001201e0, 0x16}, {0xc0001200c1, 0xf}, {0xc00025ebdd, 0x3})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:20 +0x22b
github.com/traefik/yaegi/interp.(*Interpreter).importSrc(0xc00018e908, {0xc00025e290, 0x4}, {0xc0001200c1, 0xf}, 0x1)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/src.go:109 +0x925
github.com/traefik/yaegi/interp.(*Interpreter).gta.func1(0xc00012aa00)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:273 +0xcdb
github.com/traefik/yaegi/interp.(*node).Walk(0xc00012aa00, 0xc0005458a0, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:282 +0x2e
github.com/traefik/yaegi/interp.(*node).Walk(0xc00012a3c0, 0xc0005458a0, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*node).Walk(0xc00012a140, 0xc0005458a0, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:286 +0x6b
github.com/traefik/yaegi/interp.(*Interpreter).gta(0xc00018e908, 0xc00012a140, {0xc00025e290, 0x4}, {0xc00025e290, 0x4}, {0xc00025e290, 0x4})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:20 +0x22b
github.com/traefik/yaegi/interp.(*Interpreter).gtaRetry(0xc00018e908, {0xc000115a88?, 0xc0000c4820?, 0xc0001159c8?}, {0xc00025e290, 0x4}, {0xc00025e290, 0x4})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/gta.go:395 +0x158
github.com/traefik/yaegi/interp.(*Interpreter).CompileAST(0xc00018e908, {0x10d3588?, 0xc0000c4820?})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/program.go:92 +0x11f
github.com/traefik/yaegi/interp.(*Interpreter).compileSrc(0xc00018e908, {0xc0004d8000?, 0x392?}, {0x7ffc649acef4?, 0xc00044c000?}, 0x0?)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/program.go:64 +0xaa
github.com/traefik/yaegi/interp.(*Interpreter).eval(0xc00018e908, {0xc0004d8000?, 0x392?}, {0x7ffc649acef4?, 0xc000696000?}, 0x0?)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:554 +0x25
github.com/traefik/yaegi/interp.(*Interpreter).EvalPath(0xc00018e908, {0x7ffc649acef4, 0x7})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/interp.go:512 +0xa6
main.runFile(0xc00018e908, {0x7ffc649acef4, 0x7}, 0x0)
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/cmd/yaegi/run.go:153 +0xd7
main.run({0xc0000362c0, 0x1, 0x1})
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/cmd/yaegi/run.go:116 +0xb65
main.main()
    /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/cmd/yaegi/yaegi.go:133 +0xca

Yaegi Version

0.16.1

Additional Notes

We had been using an older version of zap successfully in a custom traefik plugin but that version stops working from traefik v2.11.1 onwards. For the traefik version we are on (v2.10.1) zap v1.23.0 works but newer versions of zap do not. So in summary:

ldez commented 6 months ago

Hello,

FYI, the support of Traefik v2.10 has ended in 2024/02 https://doc.traefik.io/traefik/deprecation/releases/ The Traefik security issues are only fixed on v2.11 and v3.0.

Traefik v2.10 uses an old version of yaegi and go1.20 (currently, the Go team supports only go1.21 and go1.22)

I think that the segfault comes from a divergence between the min Go required of yaegi v0.15 (go1.19/go1.20), the version of Go used by Traefik v2.10 (go1.20), and the Go version required by your plugin (go1.22.2).

I recommend migration from zap to slog because it's the new standard way to use structured logs.

I also highly recommend migrating to, at least, Traefik v2.11.

pvbouwel commented 5 months ago

Thank you for the quick feedback. Yes, upgrade is necessary and one of the reasons of reporting this as it was unclear what would be a viable upgrade strategy. Thank you for the suggestion of slog, that could be the way forward for us.

Because I suspected the old Traefik version I did try with plain yaegi locally so the stacktrace in this ticket is really from an installation using go install github.com/traefik/yaegi/cmd/yaegi@v0.16.1

theclapp commented 5 months ago

@pvbouwel said:

Because I suspected the old Traefik version I did try with plain yaegi locally so the stacktrace in this ticket is really from an installation using go install github.com/traefik/yaegi/cmd/yaegi@v0.16.1

The stack trace ends with

github.com/traefik/yaegi/interp.nodeType2(0xc00018e908, 0xc00067e750, 0xc000522500, {0xc000595220, 0x3, 0x4}) /home/peter/go/pkg/mod/github.com/traefik/yaegi@v0.16.1/interp/type.go:1083 +0x3aff

Lines 1079-1083 of interp/type.go are:

https://github.com/traefik/yaegi/blob/v0.16.1/interp/type.go#L1079-L1083

typ, err := nodeType2(interp, sc, c.child[l-1], seen)
if err != nil {
    return nil, err
}
incomplete = incomplete || typ.incomplete

So we get typ, err from a recursive call of nodetype2 on 1079, there's no error, and the nil pointer is from dereferencing typ on 1083.

I believe this is an instance (not a dup, exactly, since this issue actually has a test case) of https://github.com/traefik/yaegi/issues/1636.

pvbouwel commented 5 months ago

We have successfully moved to slog and are now transitioning to traefik v3.0.1 so we are not having any further impact from this.

I understand from the update of theclapp it might be useful to still track it separately so I'll leave the decision to close this issue to a maintainer.