go/printer, cmd/gofmt: 在长度 >= 1< < 30 的 Go 文件中发生 panic

35g0bw71  于 6个月前  发布在  Go
关注(0)|答案(9)|浏览(65)

你正在使用哪个版本的Go( go version )?

go version go1.11.5 darwin/amd64

这个问题在最新版本中是否会重现?

是的。

你正在使用什么操作系统和处理器架构( go env )?

go env 输出

GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/tomas/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/tomas/Code/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/Cellar/go/1.11.5/libexec"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.11.5/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/sp/9jflt4d96wn7bgbq312dswx40000gn/T/go-build135989269=/tmp/go-build -gno-record-gcc-switches -fno-common"

你做了什么?

cd $(mktemp -d)
curl -LO https://storage.googleapis.com/sourcegraph-public/assets_vfsdata.go.tar.xz
xz -d -T0 -k assets_vfsdata.go.tar.xz
tar -xf assets_vfsdata.go.tar
gofmt -s -w assets_vfsdata.go

你期望看到什么?

不是恐慌。

你看到了什么?

一个恐慌。

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x10e964c]

goroutine 1 [running]:go/printer.(*printer).intersperseComments(0xc141bdf258, 0x7ffeefbff854, 0x11, 0x401750d0, 0x26, 0x401749ec, 0x36, 0x401749ec)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:746 +0x1ccgo/printer.(*printer).flush(0xc141bdf258, 0x7ffeefbff854, 0x11, 0x401750d0, 0x26, 0x401749ec, 0x36, 0x1)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1022 +0xb7go/printer.(*printer).print(0xc141bdf258, 0xc141bde888, 0x2, 0x2)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:982 +0x274go/printer.(*printer).expr1(0xc141bdf258, 0x11738e0, 0xc00000e740, 0x0, 0x1)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:895 +0x1f87go/printer.(*printer).expr(0xc141bdf258, 0x11738e0, 0xc00000e740)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1001 +0x51go/printer.(*printer).expr1(0xc141bdf258, 0x1173e60, 0xc000070840, 0x0, 0x1)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:760 +0x1a20go/printer.(*printer).expr(0xc141bdf258, 0x1173e60, 0xc000070840)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1001 +0x51go/printer.(*printer).printNode(0xc141bdf258, 0x112cf40, 0xc000070840, 0xc0004a43c0, 0x0)        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1128 +0x36b
go/printer.(*Config).fprint(0xc141bdf418, 0x1172200, 0xc00049caf0, 0xc00000e300, 0x112cf40, 0xc000070840, 0xc00048dc20, 0x1253860, 0x112b7e0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1293 +0x170
go/printer.(*printer).nodeSize(0xc141be0a48, 0x1173020, 0xc000070840, 0xf4240, 0xc000070840)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1599 +0x197
go/printer.(*printer).exprList(0xc141be0a48, 0x64c, 0xc00000e700, 0x4, 0x4, 0x1, 0x1, 0x401750d6, 0xc141bdf800)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:212 +0x4d9
go/printer.(*printer).expr1(0xc141be0a48, 0x11739e0, 0xc00000e780, 0x6, 0x1)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:907 +0x1418
go/printer.(*printer).expr1(0xc141be0a48, 0x1174260, 0xc00000ad20, 0x0, 0x1)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:790 +0x1880
go/printer.(*printer).expr(0xc141be0a48, 0x1174260, 0xc00000ad20)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1001 +0x51
go/printer.(*printer).expr1(0xc141be0a48, 0x1173e60, 0xc000070870, 0x0, 0x1)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:760 +0x1a20
go/printer.(*printer).expr(0xc141be0a48, 0x1173e60, 0xc000070870)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1001 +0x51
go/printer.(*printer).printNode(0xc141be0a48, 0x112cf40, 0xc000070870, 0xc0004a4090, 0x0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1128 +0x36b
go/printer.(*Config).fprint(0xc141be0c08, 0x1172200, 0xc00049c7e0, 0xc00000e300, 0x112cf40, 0xc000070870, 0xc00048dc20, 0x1253860, 0x112b7e0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1293 +0x170
go/printer.(*printer).nodeSize(0xc141be1690, 0x1173020, 0xc000070870, 0xf4240, 0xc000070870)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1599 +0x197
go/printer.(*printer).exprList(0xc141be1690, 0x117, 0xc00009e200, 0x19, 0x20, 0x1, 0x1, 0x41ea0c87, 0xc000499000)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:212 +0x4d9
go/printer.(*printer).expr1(0xc141be1690, 0x11739e0, 0xc00007e200, 0x0, 0x1)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:907 +0x1418
go/printer.(*printer).expr(0xc000499690, 0x11739e0, 0xc00007e200)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1001 +0x51
go/printer.(*printer).printNode(0xc000499690, 0x112c640, 0xc00007e200, 0xc0004b1620, 0x0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1128 +0x36b
go/printer.(*Config).fprint(0xc141be1850, 0x1172200, 0xc00049c150, 0xc00000e300, 0x112c640, 0xc00007e200, 0xc00048dc20, 0x1253860, 0x112b7e0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1293 +0x170
go/printer.(*printer).nodeSize(0xc141be30c0, 0x1172ae0, 0xc00007e200, 0xf4240, 0xc00007e200)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1599 +0x197
go/printer.(*printer).exprList(0xc141be30c0, 0x10a, 0xc0000507d0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:212 +0x4d9
go/printer.(*printer).stmt(0xc00049b0c0, 0x11736e0, 0xc00007e240, 0x100)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1219 +0x17c0
go/printer.(*printer).stmtList(0xc141be30c0, 0xc00007e340, 0x3, 0x4, 0x1, 0xc00049a301)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1027 +0x170
go/printer.(*printer).block(0xc141be30c0, 0xc000080b10, 0x1)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1050 +0xed
go/printer.(*printer).funcBody(0xc00049b0c0, 0x16, 0x20, 0xc000080b10)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1687 +0x4b4
go/printer.(*printer).expr1(0xc00049b0c0, 0x1173be0, 0xc000050830, 0x7, 0x1)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:798 +0x11cf
go/printer.(*printer).possibleSelectorExpr(0xc00049b0c0, 0x1173be0, 0xc000050830, 0x7, 0x1, 0xc0000202c8)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:975 +0x9f
go/printer.(*printer).expr1(0xc141be30c0, 0x11738e0, 0xc00007e380, 0x0, 0x1)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:883 +0x2180
go/printer.(*printer).expr(0xc00049b0c0, 0x11738e0, 0xc00007e380)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1001 +0x51
go/printer.(*printer).printNode(0xc00049b0c0, 0x112c3c0, 0xc00007e380, 0xc0004b1590, 0x0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1128 +0x36b
go/printer.(*Config).fprint(0xc141be3280, 0x1172200, 0xc00049c0e0, 0xc00000e300, 0x112c3c0, 0xc00007e380, 0xc00048dc20, 0x1253860, 0x112b7e0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1293 +0x170
go/printer.(*printer).nodeSize(0xc141be3ac0, 0x1172960, 0xc00007e380, 0xf4240, 0xc00007e380)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1599 +0x197
go/printer.(*printer).exprList(0xc141be3ac0, 0x0, 0xc000050840, 0x1, 0x1, 0x1, 0x0, 0x0, 0x1151300)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:212 +0x4d9
go/printer.(*printer).spec(0xc141be3ac0, 0x11742a0, 0xc000086140, 0x1, 0x10b2701)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1513 +0x23f
go/printer.(*printer).genDecl(0xc00049bac0, 0xc00007e3c0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1573 +0x79d
go/printer.(*printer).decl(0xc00049bac0, 0x1173c60, 0xc00007e3c0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1719 +0x141
go/printer.(*printer).declList(0xc141be3ac0, 0xc00009e600, 0x1f, 0x20)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1764 +0x12c
go/printer.(*printer).file(0xc141be3ac0, 0xc0004b3100)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/nodes.go:1772 +0x13a
go/printer.(*printer).printNode(0xc00049bac0, 0x11297c0, 0xc0004b3100, 0xc0004b13a0, 0x0)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1152 +0x59c
go/printer.(*Config).fprint(0xc141be3d80, 0x1172200, 0xc00049c000, 0xc00000e300, 0x11297c0, 0xc0004b3100, 0xc00048dc20, 0xc00000e301, 0xc00049c000)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1293 +0x170
go/printer.(*Config).Fprint(0xc00049bd80, 0x1172200, 0xc00049c000, 0xc00000e300, 0x11297c0, 0xc0004b3100, 0xc0000c5d08, 0x1105e62)
        /usr/local/Cellar/go/1.11.5/libexec/src/go/printer/printer.go:1351 +0x72
main.format(0xc00000e300, 0xc0004b3100, 0x0, 0x0, 0xc0800ec000, 0x41ea2269, 0x7ffffe00, 0x6, 0x8, 0x0, ...)
        /usr/local/Cellar/go/1.11.5/libexec/src/cmd/gofmt/internal.go:105 +0x6ca
main.processFile(0x7ffeefbff854, 0x11, 0x0, 0x0, 0x1172300, 0xc00000c018, 0x0, 0x0, 0x0)
        /usr/local/Cellar/go/1.11.5/libexec/src/cmd/gofmt/gofmt.go:115 +0x22c
main.gofmtMain()
        /usr/local/Cellar/go/1.11.5/libexec/src/cmd/gofmt/gofmt.go:221 +0x185
main.main()
        /usr/local/Cellar/go/1.11.5/libexec/src/cmd/gofmt/gofmt.go:178 +0x22
nr9pn0ug

nr9pn0ug1#

我可以在go version devel +3fc276ccf8 Mon Feb 4 06:53:49 2019 +0000 linux/amd64上重现这个问题。go build成功了,这证实了只有打印机有问题。
以下是在所有行截断到约120字节后得到的文件,这样我们就可以在不尝试加载1.1GB的情况下看到它是什么类型的文件:https://play.golang.org/p/i6HApVeq41v
我找不到明显的错误或原因,所以我猜这只是在给定非常大的输入时go/printer出现问题。特别是,似乎原始输入有非常长的行:

$ awk '{print length}' assets_vfsdata.go | sort -rn
1075268077
5437233
5430597
4272369
3518345
3517653
[...]

第一个长度接近int32,因此这里的可能原因是某个地方发生了整数溢出。虽然我在64位上测试了这个,但也许不是。
@tsenart抛开这个恐慌,我通常建议避免使用巨大的Go源文件,尤其是那些大部分字节都在单行中的文件。即使gofmt被修复了,我相信其他工具也可能对这些非常大的行长度有问题。当然,go build需要一段时间,实际上没有任何编辑器能够显示这个文件。
如果你确实想将文件作为二进制的一部分包含进来,也许你可以使用一个设置行长度限制的资产代码生成器,例如每行8MiB。

t1rydlwq

t1rydlwq3#

@tsenart This panic aside, I'd generally suggest to refrain from humongous Go source files, and particularly those that have most of the bytes in a single line. Even if gofmt was fixed, I'm sure other tools could also have issues with these extremely large line lengths. And of course, go build will take a while, and practically no editor will be able to display the file.
Thanks for investigating so quickly and for the feedback. It makes total sense.
If you really do want to include the file as part of the binary, perhaps you could use an assets code generator which set a limit on line lengths, such as 8MiB per line.
Indeed this is an assets file. Do you know of any asset generator project that limits line lengths to 8MiB?

o4tp2gmn

o4tp2gmn4#

找到了原因,确实是一种溢出;最长的一行是 1075268077 ,而 go/printer 使用了 infinity = 1 << 30 。我们不能将其制作成64位,因为 go/token.Position 使用了 Offset int ,所以为了32位的可移植性,我们限制为32位整数。
我可以看到一些可能的短期解决方案:

  1. infinity 加倍到 math.MaxInt32 ,这将修复大约1GB的文件,留下2GB及以上文件的破坏
  2. 如果遇到大于 infinity 的位置,使 go/printer 报错
  3. 使 go/printer 在处理大文件时避免恐慌,即使这意味着丢弃注解或格式化代码
    目前,我会发送邮件给1。我个人认为2也应该这样做,因为 go/printer 应该在无法正确打印格式良好的节点时报错,但我会等待Robert的意见。
    实际上这是一个资产文件。你知道任何限制行长度为8MiB的资产生成器项目吗?
    事实证明,导致这种恐慌的是文件的总长度,而不是行长度。只要你有位置偏移量(从文件的第一个字节开始),它们接近于 1 << 30 ,你就有风险触发恐慌。我不知道Robert是否有长期计划来更好地支持非常大的文件,但如果不在路线图上,我也不会感到惊讶。
4uqofj5v

4uqofj5v5#

  1. infinity 改为 math.MaxInt32
    实际上,cmd/compile/internal/syntax 也有 const PosMax = 1 << 30,所以这个更改可能并不像我想象的那么简单。
pgccezyw

pgccezyw6#

(这里是@tsenart的同事)顺便说一下,我绝对不希望大多数Go工具(甚至可能不是编译器)支持1GB以上的Go源文件,这肯定是我们这边的一个错误/bug。

9nvpjoqh

9nvpjoqh7#

感谢大家关于这个bug的报告、反馈和根本原因!
实际上,cmd/compile/internal/syntax 也有 const PosMax = 1 << 30,所以这个更改可能并不像我想象的那么简单。
@mvdan,这确实可能是个棘手的问题!也许我们可以与编译器团队协商 const PosMax = 1<<30,但除此之外,@tsenart@slimsag 和团队通过修复他们端上的问题 sourcegraph/sourcegraph#2224 解决了这个问题。
我认为鉴于我们已经接近 Go1.13 周期的末尾,而且我相信我曾经看到过大的源文件导致问题,也许我们应该记录一下解析源文件的限制。也许以后我们可以重新考虑使用 math.MaxInt32 ?@griesemer@mvdan 你们觉得呢?

hrysbysz

hrysbysz8#

我认为最简单的行动方案是,对于go/printer或其他可能导致在非常大的行/文件上出现恐慌的软件包,只需用一个有用的错误信息来报错。如果将来再次出现这个问题,我们总是可以考虑更好地处理大文件的方法,比如将限制翻倍或使用64位整数。

ha5z0ras

ha5z0ras9#

我并不惊讶于看到1GB文件的问题 - 这表明无论限制多么宽松,总有一天有人会遇到限制。
我认为目前最好的行动方案可能是根据@mvdan的建议,对超过一定大小的文件报错。这可能需要在为文件创建token.File时发生(待调查)。但这似乎不紧急,所以可以等待Go 1.14。

相关问题