Go: 运行时:信号堆栈的 mlock 失败:12

创建于 2020-02-25  ·  128评论  ·  资料来源: golang/go

您使用的是哪个版本的 Go ( go version )?

 $去版本
转到版本 go1.14rc1 linux/amd64

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

我用golang:1.14-rc-alpine docker 图像点击了这个,错误在 1.13 中不会发生。

您使用的是什么操作系统和处理器架构( go env )?

go env输出

$去环境
GO111MODULE=""
GOARCH="amd64"
戈宾=“”
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
高尔夫球旗=“”
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUUMDB=""
GOOS =“Linux”
GOPATH="/去"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO=“gccgo”
AR=“ar”
抄送=“gcc”
CXX="g++"
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=/tmp/go-build968395959=/tmp/go-build -gno-record- gcc 开关”

你做了什么?

克隆https://github.com/ethereum/go-ethereumDockerfile的构建器版本替换为golang:1.14-rc-alpine (或使用下面的 Dockerfile) ,然后从根目录构建

$ docker build .

FROM golang:1.14-rc-alpine

RUN apk add --no-cache make gcc musl-dev linux-headers git

ADD . /go-ethereum
RUN cd /go-ethereum && make geth

你期待看到什么?

Go 应该成功运行我们的构建脚本。

你看到了什么?

Step 4/9 : RUN cd /go-ethereum && make geth
 ---> Running in 67781151653c
env GO111MODULE=on go run build/ci.go install ./cmd/geth
runtime: mlock of signal stack failed: 12
runtime: increase the mlock limit (ulimit -l) or
runtime: update your kernel to 5.3.15+, 5.4.2+, or 5.5+
fatal error: mlock failed

runtime stack:
runtime.throw(0xa3b461, 0xc)
    /usr/local/go/src/runtime/panic.go:1112 +0x72
runtime.mlockGsignal(0xc0004a8a80)
    /usr/local/go/src/runtime/os_linux_x86.go:72 +0x107
runtime.mpreinit(0xc000401880)
    /usr/local/go/src/runtime/os_linux.go:341 +0x78
runtime.mcommoninit(0xc000401880)
    /usr/local/go/src/runtime/proc.go:630 +0x108
runtime.allocm(0xc000033800, 0xa82400, 0x0)
    /usr/local/go/src/runtime/proc.go:1390 +0x14e
runtime.newm(0xa82400, 0xc000033800)
    /usr/local/go/src/runtime/proc.go:1704 +0x39
runtime.startm(0x0, 0xc000402901)
    /usr/local/go/src/runtime/proc.go:1869 +0x12a
runtime.wakep(...)
    /usr/local/go/src/runtime/proc.go:1953
runtime.resetspinning()
    /usr/local/go/src/runtime/proc.go:2415 +0x93
runtime.schedule()
    /usr/local/go/src/runtime/proc.go:2527 +0x2de
runtime.mstart1()
    /usr/local/go/src/runtime/proc.go:1104 +0x8e
runtime.mstart()
    /usr/local/go/src/runtime/proc.go:1062 +0x6e

...
make: *** [Makefile:16: geth] Error 2
NeedsInvestigation

最有用的评论

内核错误在 Go 1.13 中表现为随机内存损坏(有和没有抢占式调度)。 Go 1.14 中的新功能是我们检测到错误的存在,尝试解决它,并且如果不可能的话,更愿意尽早和大声地崩溃。 您可以在我提到的问题中查看详细信息。

既然你说我不诚实和讨厌,我再提醒你一下行为准则: https :

所有128条评论

这是试图解决严重影响 Go 程序的内核错误的结果。 请参阅https://github.com/golang/go/issues/35777。 该错误消息表明只有两个已知的可用修复程序:增加 ulimit 或升级到更新的内核。

该错误消息表明只有两个已知的可用修复程序:增加 ulimit 或升级到更新的内核。

好吧,我正在运行官方的 alpine docker 镜像,其目的是能够构建一个 Go 程序。 显然不能。 恕我直言,上游图像应该是为了实现其目的而修复的图像,而不是我们的构建基础架构来解决上游图像中的错误。

Alpine 镜像是 Go 团队维护的吗? (真正的问题。我不知道。)无论哪种方式,是的,图像应该是固定的,最好是内核升级。

我不完全确定谁以及如何维护 docker 镜像(https://hub.docker.com/_/golang),但是 docker hub 存储库是一个“官方镜像”,这是一个超级难获得的状态,所以我认为食物链有足够高的人负责。

它“由 Docker 社区维护”。 问题应提交至

https://github.com/docker-library/golang/issues

编辑:问题是主机内核,而不是 Docker 库映像,所以他们无法修复它。

那么,Go 崩溃的官方解决方案是指指点点其他人来破解您的代码? 说得通。

@karalabe我想提醒您https://golang.org/conduct。 特别是,请尊重和慈善。

请回答问题

将问题重定向到正确的问题跟踪系统是标准做法。

如果您想了解在 Go 方面考虑了哪些选项,对我之前链接的问题中可能的解决方法和修复进行了广泛的讨论。

Go 1.13 不会发生此问题。 因此,这是 Go 1.14 中引入的错误。

说你无法修复它并告诉人们使用变通方法是不诚实的,因为恢复一段代码实际上可以修复它。 另一种解决方案是检测有问题的平台/内核,并在 Go 中提供回退机制。

告诉人们使用不同的内核尤其令人讨厌,因为大多数人似乎不能四处走动并为自己构建一个新内核。 如果 alpine 不发布新内核,那么大多数开发人员无能为力。 最后,如果您的项目依赖于一个稳定的基础架构,而您不能仅仅更换内核,那么您又将陷入困境。

将问题重定向到正确的问题跟踪系统是标准做法。

Go 崩溃的事实不是 docker 的错。 将 Go 崩溃重定向到 docker repo 是一种偏差。

您还可以在运行时禁用抢占式调度

$ GODEBUG=asyncpreemptoff=1 ./your_app

@ianlancetaylor我们建议在受影响的内核上运行时执行此操作; 那可行吗?

顺便说一句,Docker 库模块没有得到及时更新是一个已知问题,这是一种安全责任。 买者自负。

内核错误在 Go 1.13 中表现为随机内存损坏(有和没有抢占式调度)。 Go 1.14 中的新功能是我们检测到错误的存在,尝试解决它,并且如果不可能的话,更愿意尽早和大声地崩溃。 您可以在我提到的问题中查看详细信息。

既然你说我不诚实和讨厌,我再提醒你一下行为准则: https :

@karalabe ,我

我使用的是最新的 Ubuntu 和最新的可用内核。 根据错误消息,显然所有可用的 Ubuntu 内核都不适合 Go 1.14 https://packages.ubuntu.com/search?keywords=linux-image-generic

您可以将$ uname -a的输出添加到主要问题文本中吗? 也许删除 goroutine 堆栈跟踪?

我已经向 golang-dev 发布了一条说明。

抄送@克莱门斯

当您说您使用的是最新的 ubuntu 和内核时,您到底是什么意思(即 dpkg -l linux-image-*、lsb_release -a、uname -a 之类的输出),因为据我所知修复程序位于 19.10(当前稳定版本)和 20.04(开发版本)的更新包中的内核中。 它不在 18.04 的 GA 内核中,而是在 HWE 内核中,但是那些不是用 gcc 9 构建的,因此无论如何都不应该受到影响。

@networkimprov禁用信号抢占使错误不太可能发生,但它仍然存在。 这是某些 Linux 内核版本中的错误。 该错误影响所有语言的所有程序。 在使用信号抢占的 Go 程序中特别有可能观察到它,但它也存在于所有其他程序中。

Go 尝试通过锁定信号堆栈来解决该错误。 除非您遇到 mlock 限制,否则效果很好。 我想这种解决方法的一个缺点是我们使问题非常明显,而不是偶尔由于随机内存损坏而失败,如果我们不做 mlock 就会发生这种情况。

在某些时候,没有办法解决内核错误。

@karalabe

我使用的是最新的 Ubuntu 和最新的可用内核

$ docker pull -q ubuntu:latest
docker.io/library/ubuntu:latest
$ docker run --rm -i -t ubuntu
root<strong i="9">@e2689d364a25</strong>:/# uname -a
Linux e2689d364a25 5.4.8-050408-generic #202001041436 SMP Sat Jan 4 19:40:55 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

这确实满足最低版本要求。

相似地:

$ docker pull -q golang:1.14-alpine
docker.io/library/golang:1.14-alpine
$ docker run --rm -i -t golang:1.14-alpine
/go # uname -a
Linux d4a35392c5b8 5.4.8-050408-generic #202001041436 SMP Sat Jan 4 19:40:55 UTC 2020 x86_64 Linux

你能澄清你所看到的吗?

@mwhudson

$ dpkg -l linux-image-*
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                                   Version      Architecture Description
+++-======================================-============-============-===============================================================
rc  linux-image-4.13.0-16-generic          4.13.0-16.19 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-19-generic          4.13.0-19.22 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-21-generic          4.13.0-21.24 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-25-generic          4.13.0-25.29 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-36-generic          4.13.0-36.40 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-37-generic          4.13.0-37.42 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-38-generic          4.13.0-38.43 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-41-generic          4.13.0-41.46 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.13.0-45-generic          4.13.0-45.50 amd64        Linux kernel image for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-4.15.0-23-generic          4.15.0-23.25 amd64        Signed kernel image generic
rc  linux-image-4.15.0-30-generic          4.15.0-30.32 amd64        Signed kernel image generic
rc  linux-image-4.15.0-32-generic          4.15.0-32.35 amd64        Signed kernel image generic
rc  linux-image-4.15.0-34-generic          4.15.0-34.37 amd64        Signed kernel image generic
rc  linux-image-4.15.0-36-generic          4.15.0-36.39 amd64        Signed kernel image generic
rc  linux-image-4.15.0-39-generic          4.15.0-39.42 amd64        Signed kernel image generic
rc  linux-image-4.15.0-42-generic          4.15.0-42.45 amd64        Signed kernel image generic
rc  linux-image-4.15.0-43-generic          4.15.0-43.46 amd64        Signed kernel image generic
rc  linux-image-4.15.0-45-generic          4.15.0-45.48 amd64        Signed kernel image generic
rc  linux-image-4.15.0-47-generic          4.15.0-47.50 amd64        Signed kernel image generic
rc  linux-image-4.18.0-17-generic          4.18.0-17.18 amd64        Signed kernel image generic
rc  linux-image-5.0.0-13-generic           5.0.0-13.14  amd64        Signed kernel image generic
rc  linux-image-5.0.0-15-generic           5.0.0-15.16  amd64        Signed kernel image generic
rc  linux-image-5.0.0-16-generic           5.0.0-16.17  amd64        Signed kernel image generic
rc  linux-image-5.0.0-17-generic           5.0.0-17.18  amd64        Signed kernel image generic
rc  linux-image-5.0.0-19-generic           5.0.0-19.20  amd64        Signed kernel image generic
rc  linux-image-5.0.0-20-generic           5.0.0-20.21  amd64        Signed kernel image generic
rc  linux-image-5.0.0-21-generic           5.0.0-21.22  amd64        Signed kernel image generic
rc  linux-image-5.0.0-25-generic           5.0.0-25.26  amd64        Signed kernel image generic
rc  linux-image-5.0.0-27-generic           5.0.0-27.28  amd64        Signed kernel image generic
rc  linux-image-5.0.0-29-generic           5.0.0-29.31  amd64        Signed kernel image generic
rc  linux-image-5.0.0-32-generic           5.0.0-32.34  amd64        Signed kernel image generic
rc  linux-image-5.3.0-19-generic           5.3.0-19.20  amd64        Signed kernel image generic
rc  linux-image-5.3.0-22-generic           5.3.0-22.24  amd64        Signed kernel image generic
rc  linux-image-5.3.0-23-generic           5.3.0-23.25  amd64        Signed kernel image generic
rc  linux-image-5.3.0-24-generic           5.3.0-24.26  amd64        Signed kernel image generic
rc  linux-image-5.3.0-26-generic           5.3.0-26.28  amd64        Signed kernel image generic
ii  linux-image-5.3.0-29-generic           5.3.0-29.31  amd64        Signed kernel image generic
ii  linux-image-5.3.0-40-generic           5.3.0-40.32  amd64        Signed kernel image generic
rc  linux-image-extra-4.13.0-16-generic    4.13.0-16.19 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-19-generic    4.13.0-19.22 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-21-generic    4.13.0-21.24 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-25-generic    4.13.0-25.29 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-36-generic    4.13.0-36.40 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-37-generic    4.13.0-37.42 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-38-generic    4.13.0-38.43 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-41-generic    4.13.0-41.46 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
rc  linux-image-extra-4.13.0-45-generic    4.13.0-45.50 amd64        Linux kernel extra modules for version 4.13.0 on 64 bit x86 SMP
ii  linux-image-generic                    5.3.0.40.34  amd64        Generic Linux kernel image

$ lsb_release -a

No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 19.10
Release:    19.10
Codename:   eoan
$ uname -a

Linux roaming-parsley 5.3.0-40-generic #32-Ubuntu SMP Fri Jan 31 20:24:34 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ sudo apt-get dist-upgrade 

Reading package lists... Done
Building dependency tree       
Reading state information... Done
Calculating upgrade... Done
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

@myitcv

FROM golang:1.14-alpine
RUN  apk add --no-cache make gcc musl-dev linux-headers git wget

RUN \
  wget -O geth.tgz "https://github.com/ethereum/go-ethereum/archive/v1.9.11.tar.gz" && \
  mkdir /go-ethereum && tar -C /go-ethereum -xzf geth.tgz --strip-components=1 && \
  cd /go-ethereum && make geth
$ docker build .

Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM golang:1.14-alpine
1.14-alpine: Pulling from library/golang
c9b1b535fdd9: Already exists 
cbb0d8da1b30: Already exists 
d909eff28200: Already exists 
8b9d9d6824f5: Pull complete 
a50ef8b76e53: Pull complete 
Digest: sha256:544b5e7984e7b2e7a2a9b967bbab6264cf91a3b3816600379f5dc6fbc09466cc
Status: Downloaded newer image for golang:1.14-alpine
 ---> 51e47ee4db58

Step 2/3 : RUN  apk add --no-cache make gcc musl-dev linux-headers git wget
 ---> Running in 879f98ddb4ff
[...]
OK: 135 MiB in 34 packages
Removing intermediate container 879f98ddb4ff
 ---> 9132e4dae4c3

Step 3/3 : RUN   wget -O geth.tgz "https://github.com/ethereum/go-ethereum/archive/v1.9.11.tar.gz" &&   mkdir /go-ethereum && tar -C /go-ethereum -xzf geth.tgz --strip-components=1 &&   cd /go-ethereum && make geth
 ---> Running in a24c806c60d3
2020-02-26 07:18:54--  https://github.com/ethereum/go-ethereum/archive/v1.9.11.tar.gz
[...]
2020-02-26 07:18:58 (2.48 MB/s) - 'geth.tgz' saved [8698235]

env GO111MODULE=on go run build/ci.go install ./cmd/geth
runtime: mlock of signal stack failed: 12
runtime: increase the mlock limit (ulimit -l) or
runtime: update your kernel to 5.3.15+, 5.4.2+, or 5.5+
fatal error: mlock failed

抱歉,我之前的评论有误导性。 因为当然,Docker 容器中uname -a返回的内核版本将是主机的内核版本。

因此根据:

$ uname -a

Linux roaming-parsley 5.3.0-40-generic #32-Ubuntu SMP Fri Jan 31 20:24:34 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

您需要升级主机操作系统内核。

FWIW,您在上面使用 Alpine 列出的步骤make geth对我有用。:

...
Done building.
Run "./build/bin/geth" to launch geth.

是的,但在我之前的帖子中,我强调了我已经在最新的 Ubuntu 上并且已经从包存储库安装了最新的可用内核。 除了从源代码重建整个内核之外,我不知道如何更新我的内核以使用 Go 1.14。 也许我错过了什么?

只是强调一下,我确实了解解决方法是什么,如果我想让它起作用,我可以。 我打开这个问题报告是因为我希望其他人最终会遇到同样的问题。 如果只是更新我的系统就可以解决这个问题,我很乐意接受它作为解决方案,但除非我遗漏了什么,固定内核对(最近的)Ubuntu 用户不可用,因此相当大的用户群可能会受到影响。

是的,但在我之前的帖子中,我强调了我已经在最新的 Ubuntu 上并且已经从包存储库安装了最新的可用内核。 除了从源代码重建整个内核之外,我不知道如何更新我的内核以使用 Go 1.14。 也许我错过了什么?

嗯,是的,我也刚刚在焦点上进行了复制。 该修复程序存在于 Ubuntu eoan 内核的 git 中: https ://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id

@karalabe - 我刚刚意识到我的错误:虽然我使用的是最新的 Ubuntu,但实际上我使用的是eoan

@mwhudson - 只需要注意一件事(尽管您可能已经意识到这一点),

https://github.com/golang/go/blob/20a838ab94178c55bc4dc23ddc332fce8545a493/src/runtime/os_linux_x86.go#L56 -L61

似乎表明 Go 方面正在检查补丁版本 15 或更高版本。 5.3.0-40.32 报告的补丁版本是什么? 我猜是0?

重新开始这个讨论,直到我们在这里解决这个问题。

一个小总结,因为我必须自己拼凑:

  • 根本原因: https :
  • 受影响的 Linux 内核:5.2.x、5.3.0-5.3.14、5.4.0-5.4.1
  • 2019 年 12 月 5 日发布的固定 Linux 内核 5.3.15: https ://lwn.net/Articles/806395/
  • 已于 2019 年 12 月 5 日发布的固定 Linux 内核 5.4.2: https ://lwn.net/Articles/806394/
  • Go 的部分解决方法: https :
  • 最新 Ubuntu 版本的可用 Linux 内核: https : generic
  • 可用于 Ubuntu 的最新 linux 内核版本为 5.3.0
  • 然而,这个补丁似乎被精心挑选到 Ubuntu 的 5.3.0 内核中: https ://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/ = 59e7e6398a9d6d91cd01bc364f9491dc1bf2a426

因此,似乎 Ubuntu 的内核已打补丁,但无论如何都启用了解决方法。

因此,似乎 Ubuntu 的内核已打补丁,但无论如何都启用了解决方法。

哦,是的,是的,我实际上应该阅读失败,不是吗? 这是变通方法失败而不是原始错误,在实际不需要变通方法的情况下,但没有好的方法让 Go 知道这一点。 我可以在 Ubuntu 中修补 Go 1.14 包的检查,但这对运行例如docker golang:1.14-alpine映像的用户没有帮助。 嗯。

我想问题是,此时有多少用户在使用“易受攻击的”内核。 现在不可能有​​那么多发行版使用 gcc 9 编译未打补丁的内核。

不可能有那么多...

著名遗言? :D

严肃地说,我认为人们不会经常更新内核,许多系统也不能。 也许一个更好的问题是默认情况下哪些系统/发行版使用易受攻击的内核,并假设会有很多人坚持使用它们。

我可以在 Ubuntu 中修补 Go 1.14 包的检查,但这对运行例如docker golang:1.14-alpine映像的用户没有帮助。 嗯。

您还会错过从源代码构建的用户。 例如,以太坊确实为我们的 PPA 构建了源代码,因为 Launchpad 上的所有发行版都没有最近的 Go 包。

Ubuntu(和其他发行版?)使用挑剔而不是遵循 Linux 内核补丁版本是否常见? 在https://packages.ubuntu.com/search?keywords=linux-image-generic 上,所有内核的补丁版本都为零。

就快速谷歌搜索而言,Ubuntu 在发行发行版后不会发布新内核,而只是挑选安全修复程序(即没有补丁版本碰撞)。 例外是 LTS 版本(支持 5 年),它可能每隔几年进行一次内核更新以支持新硬件(但这也非常罕见)。 不知道其他发行版。

以我现在有限的知识,这似乎很奇怪。 补丁发布旨在以受控方式分发补丁,以便人们(在这种情况下是 Go 运行时)可以知道包含哪些补丁,哪些不包含。 然而,事情就是这样,所以我们必须忍受它。

开放问题:

  • 有多少人受到这个问题的影响?
  • ulimit是一种可行的解决方法吗?
  • Ubuntu 是唯一“无视”Linux 内核补丁号的发行版吗?
  • 根据上述问题的答案:为 Ubuntu 添加特殊检测是否合理?

@neelance

Ubuntu(和其他发行版?)使用挑剔而不是遵循 Linux 内核补丁版本是否常见?

许多发行版都这样做,而不仅仅是 Ubuntu。 Debian 做到了,Red Hat Enterprise Linux 做到了,我希望 SUSE 也为他们的企业发行版做到这一点。 如果您不能积极关注上游稳定版本(并在上游支持消失时切换稳定版本),那么挑选樱桃是获得任何错误修复的唯一方法。 Fedora 是个例外; 一段时间后,它会重新调整到最新的稳定版本上游内核。

还有容器引擎使用的专有内核的问题。 我们甚至无法查看它们的来源,其中一些过去曾对内核版本号撒谎。 我希望他们也使用樱桃采摘。

通常,内核功能(或错误)的版本检查是一个非常糟糕的主意。 由于静态链接,Go 的情况更糟,因此不可能交换应用程序下的运行时来修复其内核期望。

Ubuntu(和其他发行版?)使用挑剔而不是遵循 Linux 内核补丁版本是否常见? 在https://packages.ubuntu.com/search?keywords=linux-image-generic 上,所有内核的补丁版本都为零。

基本内核版本字符串不会改变,这是真的。 但这并不意味着上游稳定版本不会合并,而是在代码更改时会增加 ABI 编号。

请注意,您选择了未显示正确更改日志的元包,在这里您可以看到最新版本 5.4.0-14.17 已合并 5.4.18 稳定版本:
http://changelogs.ubuntu.com/changelogs/pool/main/l/linux-5.4/linux-5.4_5.4.0-14.17/changelog

听起来几乎不可能在所有发行版中进行适当的自动检测。 我看到三个选项:

  • 没做什么。
  • 使变通方法选择加入。
  • 使变通方法选择退出。

或者在 5.3.x 和 5.4.x 上默认禁用异步抢占,并让用户在运行时启用它。

https://github.com/golang/go/issues/37436#issuecomment -591237929 指出禁用异步抢占不是正确的解决方案,是吗?

严格来说不是,但是在异步抢占登陆之前没有关于该问题的报告。

实际上,运行时是否可以在启动时分叉一个子进程(对于 5.3.x 和 5.4.x)来触发错误并启用解决方法(如果有)? IIRC有一个可靠的复制器,见https://github.com/golang/go/issues/35326#issuecomment -558690446

禁用异步抢占会分散注意力。 在有故障的内核上运行的程序仍然损坏。 只是损坏表现为奇怪的内存损坏,而不是运行到指向内核版本的mlock限制的错误。 虽然显然我们想彻底解决这个问题,但我认为,在选择明确错误或随机内存损坏的情况下,我们应该始终选择明确错误。

我同意内核版本检测很糟糕,只是我们不知道任何其他选择。 如果有人在这方面有任何建议,那将非常有帮助。

我们可以做的一件事是添加GODEBUG设置以禁用mlock信号堆栈。 这将为人们提供一种专注于实际问题的解决方法。 我们可以在错误消息中提及该设置。 恐怕不管有没有打补丁的内核都会导致人们打开设置。 但至少它会给真正有补丁内核的人一种解决这个问题的方法。 抄送@克莱门斯

实际上,运行时是否可以在启动时分叉一个子进程(对于 5.3.x 和 5.4.x)来触发错误并启用解决方法(如果有)? IIRC 有一个可靠的复制器,参见 #35326(评论)

这是一个有趣的想法,但我认为在这种情况下,对于每个 Go 程序,在启动时运行测试的成本太高了。

我可能错过了一些东西(这个线程很快!),但是仅仅提高 mlock 限制有什么缺点或困难? 没有什么理由不只是将其设置为无限制,但即使您不想这样做,每个线程也只需要 4 KiB,因此仅仅 64 MiB 确实比单个进程的运行时能够进行 mlocking 还要多. AFAIK,大多数发行版默认设置为无限制。 我知道的唯一值得注意的例外是 Docker,默认情况下将其设置为(我认为)64 KiB,但这可以通过将--ulimit memlock=67108864传递给 Docker 来引发。

看起来我们已经有了一个相当简单的解决方法。 有什么阻止人们这样做吗?

问题的关键在于,如果可能的话,您不应该应用手动解决方法。 它看起来像是 1.14 中的回归。

无法在Docker库端修复: https :

ulimit解决方法的问题在于它是一个定时炸弹。 目前没有自动化流程需要在 Go 1.13 中提高 ulimit。 Go 1.14 更新后立即失败的任何人都会注意到并修复它。

更有趣的情况是,如果有人使用未受影响的旧版本内核,并且在某个时候将内核切换到新版本,会发生什么情况。 突然之间事情会开始破裂,但是由于 Go 是应用程序层而内核是操作系统层,因此建立连接并找出修复方法需要时间。 问题是成本是多少。


但目前尚不清楚的是,是否只是 Go 编译器或所有 Go 构建的应用程序也有问题? 如果只是编译器,这是一个幸运的情况,可以包含后果。 然而,如果所有使用 1.14 构建的 Go 应用程序都有恐慌的倾向,这可能真的会损害预先构建的二进制可移植性,因为突然之间我的代码可能无法在不同的系统上运行(这实际上是完全有效的,只是使用了不同的内核版本控制方案)。

但目前尚不清楚的是,是否只是 Go 编译器或所有 Go 构建的应用程序也有问题?

Linux 内核错误甚至不是 Go 特有的。 如果我理解正确,它会影响任何程序——甚至是用 C 编写的程序! — 使用 XMM 或 YMM 寄存器并可能接收信号。 1.14 下的 Go 程序碰巧比许多其他程序受到_更多_的严重影响,因为它们在内部使用信号进行 goroutine 抢占,这就是为什么 Go 1.14 运行时包含mlock解决方法。

Linux 内核错误甚至不是 Go 特有的。

是的,但我的内核打了补丁,但 Go 仍然对此感到恐慌:)

@克莱门斯

我知道的唯一值得注意的例外是 Docker,默认情况下将其设置为(我认为)64 KiB,但这可以通过将 --ulimit memlock=67108864 传递给 Docker 来引发。

看起来我们已经有了一个相当简单的解决方法。 有什么阻止人们这样做吗?

不幸的是,是的。 在我们这里的例子中,我们不能告诉我们的客户重新配置他们的 docker 容器。 太多了,他们对设置中的环境变化很敏感; 事实上,这就是我们选择在 docker 容器中交付应用程序的原因,因此隔离工具的外观将减轻更改配置选项的担忧。 更改容器内容很好,但我们调用容器的方式可能不太好。

@克莱门斯

没有什么理由不只是将其设置为无限制,但即使您不想这样做,每个线程也只需要 4 KiB,因此仅仅 64 MiB 确实比单个进程的运行时能够进行 mlocking 还要多. AFAIK,大多数发行版默认设置为无限制。

情况似乎并非如此——这是一个 Ubuntu LTS,锁定空间只有 6KiB:

~$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 3795
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 3795
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.6 LTS
Release:    16.04
Codename:   xenial
~$ uname -a
Linux bastion0 4.4.0-174-generic #204-Ubuntu SMP Wed Jan 29 06:41:01 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

一个示例内核的/proc/version的内容是什么,该内核打了补丁可以正常工作,但 Go 当前正在产生mlock错误? 谢谢。

@ucirello您是否发现在您描述的系统上运行 Go 程序有任何问题?

$ cat /proc/version
Linux version 5.3.0-40-generic (buildd@lcy01-amd64-026) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #32-Ubuntu SMP Fri Jan 31 20:24:34 UTC 2020

也许我们可以使用/proc/version记录的日期作为附加信号。 它可能应该是特定于发布的,这很痛苦。 但整个过程是痛苦的。

讨论似乎是关于接受更多的误报或漏报。 这是一个总结:

误报:在打补丁的内核上启用了变通方法。

  • 可重现。 可以显示说明。
  • 看起来像是回归。
  • 在某些环境中很难修复。
  • Go 二进制文件可能会在某些环境中运行,但无法在其他环境中运行。

假阴性:未在未打补丁的内核上启用变通方法。

  • 失败很少发生,尤其是在禁用异步抢占的情况下。
  • 由于内存损坏可能会导致严重后果。
  • 很难调试。

@ianlancetaylor我在描述的系统中没有看到任何问题,但据我所知,这可能是巧合,即错误消息说我应该升级到 5.3.15、5.4.2 或 5.5 以上,这是 4.4.x盒子。

无论如何,我想强调的是,大多数发行版以 64M 提供ulimit -l的假设似乎是不正确的。 这是一个 Linux 机器,其ulimit -l是 64 K - 它是标准安装。

如果无法应用变通方法,有没有办法检测ulimit设置并避免崩溃? 这样我们仍然可以改善情况但不会导致新的崩溃? 然后我们可以打印一个关于潜在内存损坏的警告。

如果无法应用变通方法,有没有办法检测 ulimit 设置并避免崩溃?

实际上,当前代码已经在检测 ulimit 设置。 它不直接查询它,但它会检测何时遇到它,这就是为什么它会打印要求用户提高 ulimit 的特定错误消息。 如果 mlocking 出现其他问题,它实际上会打印不同的消息。 崩溃是故意的,因为我们认为这比随机内存损坏更可取。

无论如何,我想强调的是,大多数发行版提供 ulimit -lwith 64M 的假设似乎是不正确的。 这是一个 Linux 机器,其 ulimit -l 是 64K - 它是标准安装。

@ucirello ,感谢您提供该数据点。 我手头没有 Ubuntu 盒子,但是我在 Ubuntu 安装上进行了原始调试,并且可以发誓它没有设置 ulimit。 这绝对不是在容器中?

@ianlancetaylor我创建了快速而肮脏的脚本来检查 uname 报告的内容: https : //gist.github.com/Tasssadar/7424860a2764e3ef42c7dcce7ecfd341

这是最新(好吧,-ish)Debian 测试的结果:

tassadar<strong i="9">@dorea</strong>:~/tmp$ go run gouname.go 
real uname
Linux dorea 5.4.0-3-amd64 #1 SMP Debian 5.4.13-1 (2020-01-19) x86_64 GNU/Linux

our uname
sysname Linux
nodename dorea
release 5.4.0-3-amd64  <-- used by go
version #1 SMP Debian 5.4.13-1 (2020-01-19)
machine x86_64
domainname (none)

由于 Go 只使用发布字符串,补丁版本检查基本上在普通内核上不起作用 - Debian 和 RHEL/CentOS(幸运的是内核太旧)都是这样做的,他们保留 .0 并指定真正的补丁版本稍后。 不幸的是,他们没有为version使用相同的格式。

编辑:为了让它更尴尬,Ubuntu 根本没有将补丁号放入 uname 中,即使它们可能包含所有修复程序。 也许最好的做法是让它成为警告而不是崩溃? 在这一点上,大多数内核可能已经更新了。

崩溃是故意的,因为我们认为这比随机内存损坏更可取。

@aclements这个论点有一个小缺陷:问题不在于遇到 ulimit 的真阳性,而是在没有解决方法的情况下不会导致内存损坏的假阳性。

@克莱门斯

这绝对不是在容器中?

我 100% 确定它不是容器。 虽然它是一个虚拟机。

$ sudo virt-what
xen
xen-hvm

@ucirello ,感谢您提供该数据点。 我手头没有 Ubuntu 盒子,但是我在 Ubuntu 安装上进行了原始调试,并且可以发誓它没有设置 ulimit。 这绝对不是在容器中?

作为一个不同的数据点,我的 Ubuntu Eoan 主系统默认设置为 64MB 为ulimit -l

@ucirello

@ianlancetaylor - 感谢您提供信息。 我想我当时误解了错误消息。 我不知道这个事实,错误消息让人相信新的二进制文件将与旧内核不兼容。

Go 程序只会报告带有版本号的内核的错误消息,表明它们可能存在问题。

这是我们可以做的事情:

  1. 就像我们今天所做的那样,使用uname检查内核版本中是否存在易受攻击的内核。
  2. 如果内核根据版本存在漏洞,请阅读/proc/version
  3. 如果/proc/version包含字符串"2020" ,则假定内核已打补丁。
  4. 如果/proc/version包含字符串"gcc version 8"假设内核即使打了补丁也能工作(因为只有在使用 GCC 9 或更高版本编译内核时才会出现错误)。
  5. 否则,就像我们今天在易受攻击的内核上所做的那样,在信号栈上调用mlock

这样做的目的是减少 Go 程序用完mlock空间的次数。

有人知道任何未打补丁的内核可能在/proc/version有字符串"2020" /proc/version吗?

为了安全起见,我们可能应该尝试确定内核为主要发行版打补丁的时间。 有没有人可以识别任何特定发行版? 谢谢。

我不确定这是否有帮助,但 Ubuntu 显然确实为那些正在寻找的人提供了标准内核版本:

$ cat /proc/version_signature
Ubuntu 5.3.0-1013.14-azure 5.3.18

@jrockway谢谢,问题不在于我们没有内核版本,而是 Ubuntu 使用的内核版本存在错误,但 Ubuntu 已为该错误应用了补丁,因此内核实际上可以工作,但我们没有不知道如何发现这个事实。

添加到@ianlancetaylor的字符串匹配启发式,您还可以检查/proc/version
5.3.x && x >= 15 || 5.4.x && x >= 2 (不是真正的代码,但你懂的)

谢谢,我们已经在uname结果中检查过了。

我指的是https://github.com/golang/go/issues/37436#issuecomment -591503305:

release 5.4.0-3-amd64  <-- used by go
version #1 SMP Debian 5.4.13-1 (2020-01-19) <-- has actual version

编辑:在 Ubuntu 上,您可以按照我的建议检查/proc/version_signature

啊,对不起,错过了。 因此,根据在某些系统上的那个评论release域和version由归国场uname的内核版本,他们报告不同。 我们目前检查release字段而不是version字段。

/proc/version文件再次包含一组不同的信息。

我的 Ubuntu 系统有/proc/version但没有/proc/version_signature

@bbarenblat 帮助收集了几个发行版的几个版本的 uname 和 /proc/version 以查看它们在野外的样子(我添加了一些我自己的):

Debian 不稳定:

$ uname -v
#1 SMP Debian 5.3.9-3 (2019-11-19)
$ uname -r
5.3.0-2-amd64
$ cat /proc/version
Linux version 5.3.0-2-amd64 ([email protected]) (gcc version 9.2.1 20191109 (Debian 9.2.1-19)) #1 SMP Debian 5.3.9-3 (2019-11-19)

Debian 10.3(当前稳定版):

$ uname -v
#1 SMP Debian 4.19.98-1 (2020-01-26)
$ uname -r
4.19.0-8-amd64
# cat /proc/version
Linux version 4.19.0-8-amd64 ([email protected]) (gcc version 8.3.0 (Debian 8.3.0-6)) #1 SMP Debian 4.19.98-1 (2020-01-26)

Debian 7(很旧):

$ uname -v
#1 SMP Debian 3.2.78-1
$ uname -r
3.2.0-4-amd64
$ cat /proc/version
Linux version 3.2.0-4-amd64 ([email protected]) (gcc version 4.6.3 (Debian 4.6.3-14) ) #1 SMP Debian 3.2.78-1

Debian“在某个地方过去稳定,偶尔有不稳定的包”:

$ uname -v
#1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20)
$ uname -r
4.19.0-6-amd64
$ cat /proc/version
Linux version 4.19.0-6-amd64 ([email protected]) (gcc version 8.3.0 (Debian 8.3.0-6)) #1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20)

Ubuntu 19.10:

$ uname -v
#32-Ubuntu SMP Fri Jan 31 20:24:34 UTC 2020
$ uname -r
5.3.0-40-generic
$ cat /proc/version  
Linux version 5.3.0-40-generic (buildd@lcy01-amd64-026) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #32-Ubuntu SMP Fri Jan 31 20:24:34 UTC 2020

带有 GCP 内核的 Ubuntu 19.10(实际上是 5.3.0):

$ uname -v
#9-Ubuntu SMP Mon Nov 11 09:52:23 UTC 2019
$ uname -r
5.3.0-1008-gcp
$ cat /proc/version
Linux version 5.3.0-1008-gcp (buildd@lgw01-amd64-038) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #9-Ubuntu SMP Mon Nov 11 09:52:23 UTC 2019

带有手工构建内核的 Ubuntu 19.10:

$ uname -v
#36 SMP Wed Feb 26 20:55:52 UTC 2020
$ uname -r
5.3.10
$ cat /proc/version
Linux version 5.3.10 (austin@austin-dev-ubuntu) (gcc version 9.2.1 20191008 (Ubuntu 9.2.1-9ubuntu2)) #36 SMP Wed Feb 26 20:55:52 UTC 2020

CentOS 7.7:

$ uname -v
#1 SMP Fri Dec 6 15:49:49 UTC 2019
$ uname -r
3.10.0-1062.9.1.el7.x86_64
$ cat /proc/version
Linux version 3.10.0-1062.9.1.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) ) #1 SMP Fri Dec 6 15:49:49 UTC 2019

@aclements我对您发布的哪些内核需要解决方法以及哪些已经修补感到有些困惑。 也许你可以注释你的帖子。

问题是,如果您在潜在的错误版本范围内,您_可能会受到影响_,如果您在版本范围之外,则您是安全的。
当发现最初的损坏时,我们立即拉出补丁,对其进行测试并推出。 所以我们的内核 100% 在错误范围内,但打了补丁。 在范围之外意味着您是安全的,但在范围内您无法在不进行测试的情况下证明该错误存在。 (换句话说:误报是有意为之)

我现在正在构建另一个带有上游版本号的修补过的 ubuntu 内核

  • make kernelversion
  • 使用此版本修补debian{,.master}/{changelog,control}

依赖于内核版本的软件是发行版喜欢保持稳定的原因:如果版本号的更新可能会破坏之前运行的软件,那么最好不要更改版本(但仍然应用补丁!)。

我不高兴仅仅因为 golang 1.14 就推出新内核,但我认为它会起作用。
我不鼓励每个人在受控环境之外发布 golang 1.14 二进制文件。

@rtreffer的帖子再次听起来像误报是不可避免的。 目前我仍然投票支持限制误报的负面影响的解决方案。

我认为应该选择加入以避免所有误报。 例如,添加 GOWORKAROUNDS env var boolean 或使用变通方法列表或启用启发式方法来尝试找到它们。

这将是 IMO 干扰最少的解决方案。

@fcuello-fudo 问题在于,如果未在坏内核上启用变通方法,则症状非常模糊。

重新使用 Linux 内核中的“污染”概念怎么样? Go 运行时会不断检测坏内核并应用 mlock 解决方法,但如果 mlock 失败(而不是崩溃),则会将自身标记为受污染。 然后,如果设置了污点标志,请确保为任何恐慌添加注释并抛出消息。

好处是避免了误报崩溃,同时在坏内核导致崩溃的情况下仍然提供明确的指示。

缺点是坏内核可能会悄悄地破坏内存,而不会导致明显的崩溃。

@fcuello-fudo 问题在于,如果未在坏内核上启用变通方法,则症状非常模糊。

并默认启用它,但如果用户愿意,禁用变通方法仍然有用吗?

@howardjohn (谷歌https://github.com/istio/istio/issues/21672 上

我个人机器上的内核没有打补丁。 但是我的机器有什么并不重要,我们将 Istio 运送给 1000 多个用户。 我无法控制他们在什么机器上运行它,我希望不必将其限制为内核版本的某些子集

Prometheus 项目也在阻止其 go1.14 升级: https :

我在自托管的 Kubernetes 集群上运行 go 应用程序时遇到了这个问题。 我能够通过增加相关的 ulimit 来允许 _mlock_ 解决方法生效。 但是,由于在 Kubernetes 中运行的 Docker 容器更改 ulimit 的过程并不容易找到,因此可能有助于其他人将详细信息放在这里。

  1. 更新/etc/security/limits.conf以包含类似的内容
* - memlock unlimited
  1. 更新/etc/docker/daemon.json以包含
"default-ulimits": { "memlock": { "name": "memlock", "hard": -1, "soft": -1 } }
  1. 重新启动 docker/kubernetes,让你的 pod 重新启动。

  2. 进入一个正在运行的容器并验证 ulimit 是否已增加:

$ kubectl exec -it pod-name -- /bin/sh
/ # ulimit -l
unlimited

你可以使用比 _unlimited_ 锤子更微妙的东西来逃脱; 对我来说,128KiB (131072) 的限制似乎有效。

也在尝试为https://github.com/RTradeLtd/Temporal on go 1.14 构建 docker 映像时遇到此问题

并且桩还在继续堆积。 看,这就是我在这个帖子开头生气的原因(这对我来说是一个严重的错误,我同意)。 尽管我做了很多努力来解释和提供这是一个阻止程序的重现,但我被关闭以不干扰发布。 即使在很明显这不是 docker 问题之后。

现在我们处于一个更糟糕的领域,因为各种项目都将 Go 1.14 列入黑名单。 此错误目前仅在 Go 1.15 中修复。 基于上述相关问题,我们是否相信将其推迟 8 个月是个好主意? 我认为承认这个问题并尝试在补丁版本中修复它会很好,而不是等待更多的项目被咬。

是的,我知道我只是在这里唠叨别人而不是自己解决问题。 很抱歉我不能做出更有意义的贡献,我只是不想破坏生态系统。 Go 模块已经对许多项目造成了打击,让我们不要再强调工具需要意识到的另一个怪癖。

Ian Lance-Taylor 表示一旦修复将被反向移植: https :

@lmb哦,很高兴听到,谢谢你的链接。

仅供参考:Ubuntu 20.04 可能会在下个月底发布 5.4.0,这意味着
最新的 Ubuntu LTS 和 golang 不能开箱即用。

Lorenz Bauer通知@github.com schrieb am Fr., 6. März 2020, 12:42:

Ian Lance-Taylor 曾表示,一旦有此修复程序,将向后移植
一: https :


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/golang/go/issues/37436?email_source=notifications&email_token=AAAO66R6S5VOWQBKETLI5UDRGDORJA5CNFSM4K3GBRRKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXWZissues700000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C
或取消订阅
https://github.com/notifications/unsubscribe-auth/AAAO66SH6J4M75WDJ5GO7HTRGDORJANCNFSM4K3GBRRA
.

@karalabe以我所能召集的最

此外,我是第一个注意到问题源于您的主机内核,而不是 Docker 模块的人。 在我指出这一点之前,您没有提到您在使用 Ubuntu。

在那张纸条之后,我想你还欠我们一个道歉。

编辑:我还要求您从报告中删除 goroutine 堆栈跟踪(从goroutine x [runnable] ),因为它们使页面难以阅读/导航。 [更新:Russ 已经编辑了堆栈。]

请大家记住,成功的沟通是很难的,而且是一项需要练习的技能。 情绪会对我们不利,阻碍我们成功沟通的目标,我自己也经历过。 是的,存在违反行为准则的行为,指出它是好的。 主动道歉也有帮助。 现在,让我们尝试确保每个帖子都对协作和解决此问题产生积极的净影响。

@rtreffer你知道为 Ubuntu 20.04 计划的内核版本是否会包含补丁? 看起来它是被 Ubuntu 的 5.3.0 内核挑选出来的(https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426)。 我猜它也在他们的 5.4.0 内核中,但最好能确定一下。 谢谢。

@rtreffer你知道为 Ubuntu 20.04 计划的内核版本是否会包含补丁? 看起来它是被 Ubuntu 的 5.3.0 内核挑选出来的(https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426)。 我猜它也在他们的 5.4.0 内核中,但最好能确定一下。 谢谢。

它将包含在 20.04 的发布内核中,是的:

(master)mwhudson<strong i="9">@anduril</strong>:~/src/upstream/linux-2.6$ git log -1 --oneline ad9325e9870914b18b14857c154a0fee0bd77287
ad9325e98709 x86/fpu: Don't cache access to fpu_fpregs_owner_ctx
(master)mwhudson<strong i="10">@anduril</strong>:~/src/upstream/linux-2.6$ git tag --contains ad9325e9870914b18b14857c154a0fee0bd77287
Ubuntu-5.4-5.4.0-10.13
Ubuntu-5.4-5.4.0-11.14
Ubuntu-5.4-5.4.0-12.15
Ubuntu-5.4-5.4.0-13.16
Ubuntu-5.4-5.4.0-14.17
Ubuntu-5.4.0-15.18
Ubuntu-5.4.0-16.19
Ubuntu-5.4.0-17.20
Ubuntu-5.4.0-17.21
Ubuntu-5.4.0-8.11
Ubuntu-5.4.0-9.12
Ubuntu-raspi2-5.4-5.4.0-1002.2
Ubuntu-raspi2-5.4.0-1003.3
Ubuntu-raspi2-5.4.0-1004.4

根据与@ aclements@dr2chase@randall77和其他人的讨论,我们对 1.14.1 版本的计划是:

  • 写一个 wiki 页面来描述问题
  • 继续在可能有问题的内核版本上使用mlock
  • 如果mlock失败,请默默注意该事实并继续执行
  • 如果我们看到意外的SIGSEGVSIGBUS ,并且mlock失败,那么在崩溃堆栈跟踪点人们在维基页面

希望这将提供在正常情况下正确执行的良好组合,同时引导人们了解潜在错误内核的信息,以帮助他们确定问题是他们的内核还是他们的程序还是 Go 本身的错误。

这也可以与基于uname version字段(我们目前只检查release字段)更好地识别特定内核是否已被修补的尝试相结合。

我们讨论的另一件事是:如果 mlock 失败,并且我们将要向自己发送信号,请先接触信号堆栈。

@ianlancetaylor听起来是个好主意!

如果您还没有考虑过:从错误消息链接到该 wiki 页面(可能还有一个间接)

例如 iPXE 通过像http://ipxe.org/1d0c6539这样的链接来做到这一点(他们的输出针对引导 ROM、有限的空间等进行了优化)

  • 写一个 wiki 页面来描述问题

  • 继续在可能有问题的内核版本上使用mlock

  • 如果mlock失败,请默默注意该事实并继续执行

禁用异步抢占不是更好吗? 确实,即使 asyncpreemptoff=1 也可能发生该问题,但该错误非常罕见,没有它几个月没有人注意到。

有没有办法(在 Go 运行时之外)测试您的内核是否已打补丁? 我们在发行版之外维护我们自己的内核,这使得它甚至可以工作。

@gopherbot请向后移植到 go1.14。 这是一个严重的问题,没有解决方法。

向后移植问题已打开:#37807(适用于 1.14)。

根据https://golang.org/wiki/MinorReleases ,请记住在将补丁提交给 master 后立即创建挑选 CL(s)

@nemit这里有一个 C 复制器: https :

@randall77 @aarzilli经过考虑,我实际上认为添加额外的部分缓解措施(例如触摸信号堆栈页面或禁用异步抢占)并不是一个好主意。 这是一个内核级错误,可以影响任何接收信号的程序。 使用有问题的内核运行的人应该以一种或另一种方式升级内核。 使用mlock是一种可靠的缓解措施,应该始终有效,因此尝试它是合理的。 在发送抢占信号之前接触信号堆栈,或完全禁用信号抢占,并不是一种可靠的缓解措施。 我认为我们不应该退回到不可靠的缓解措施; 我们应该告诉用户升级内核。

无法升级内核的人可以选择使用GODEBUG=asyncpreemptoff=1 ,这将与其他两个一样有效地部分缓解。

我认为我们不应该退回到不可靠的缓解措施; 我们应该告诉用户升级内核。

如果错误总是在崩溃中表现出来,我会同意你的看法。 但它没有 - 它只是随机破坏内存。 也许它崩溃了,但也许它只是随机破坏了程序的数据并继续运行。 我认为我们有责任采取一切可能的措施来防止数据损坏,即使这意味着我们不会经常发送有关所需内核升级的消息。

这是一个现在与以后的决定。 我们现在可以通过在使用前触摸信号页来尝试防止数据损坏。 或者我们可以通过在发生崩溃时向用户发送消息来尝试防止损坏。 我们不能两者都选择。

我希望有一种方法可以在我们检测到 mlock 故障时发送消息,而不会崩溃,然后通过触摸页面来缓解。 不幸的是,我认为 Go 程序不能在 stdout/stderr 上做到这一点。 也许我们可以在系统日志中抛出一些东西? 它只会帮助那些浏览系统日志的人,这可能并不多。

有道理。

在安装时发布消息怎么样? 这需要更改二进制版本和发行版的安装过程,但这会让您运行复制器来最终测试错误。

@networkimprov这是个好主意,但是由于人们在可能具有不同内核版本的机器之间发送编译的程序,我认为我们还需要上述方法。

我已经在https://golang.org/wiki/LinuxKernelSignalVectorBug编写了 wiki 页面

更改https://golang.org/cl/223121提到这个问题: runtime: don't crash on mlock failure

我已经发送了https://golang.org/cl/223121。 如果在 1.14 中遇到问题的人可以查看该更改是否解决了他们的问题,那将会很有帮助。 谢谢。,

好吧, @randall77 / @ianlancetaylor ,我倾向于不同意这是一个 golang 问题。 Golang 发现了内存损坏问题,但这是一个非常严重的内核错误。

因此,它应该通过您的内核路径升级。
发行版拿起了补丁并发送了它。 它被反向移植了。 每个新安装都将获得一个不受影响的内核。
如果您推出自己的内核,则必须自己完成这项工作。 照常。

对点击它的用户有帮助,并尽可能提供帮助。
但我认为修复内核错误甚至强制用户应用补丁不是 golang 的责任。

@rtreffer这就是我们想要做的:尽可能提供帮助。

在有缺陷的内核上,使用 Go 1.14 构建的 Go 程序表现出不可预测和糟糕的表现。 即使在有缺陷的内核上,我们也不想这样做。 如果一个程序会快速而干净地失败,那将是一回事。 但是我们看到的是导致模糊错误的内存损坏。 参见 #35326 等。

你认为我们应该采取一些不同于我们现在正在做的行动吗?

@rtreffer好吧。 我们有一些不受影响的生产 5.2 内核,因为它们不是用 gcc9 编译的,我们还可以轻松地将修复程序修补到我们的内核行中,而不会影响其他任何东西并且很好。 内核错误在我们的环境中不存在,升级主要版本需要进行更多的测试并在整个机群中小心翼翼地推出,所以仅仅“升级你的内核”并不是一个好情况。

另一方面,基于内核版本号的解决方法导致我们转向由于 ulimit 问题而导致 DID 失败的 mlock。 那不是内核错误。

话虽如此,我不确定这里有更好的解决方案,围棋团队可能做出了正确的决定。

@ianlancetaylor也许你可以在源代码和二进制版本中发布 C 复制器,并在 wiki 页面上引用它作为审查任何内核的一种方式。

在安装时发布消息怎么样? 这需要更改二进制版本和发行版的安装过程,但这会让您运行复制器来最终测试错误。

任何对此给予足够重视的发行版肯定只会修补他们的内核。

@ianlancetaylor我完全同意前进的方向,补丁和维基页面看起来很棒。

我想强调的是,损坏并不是 golang 的错误或错误,而且发行版正在运送固定内核。 它应该已经消失了。

因此,我认为除了建议的提示(wiki+panic)外,不需要任何其他内容。

@rtreffer太好了,谢谢。

更改https://golang.org/cl/223417提到这个问题: [release-branch.go1.14]runtime: don't crash on mlock failure

只是为了澄清,根据维基所说的,如果 mlock 在 1.14.1 上失败,这是否意味着该程序容易受到内存损坏的影响?

谢谢

@smasher164不一定。 当 mlock 调用失败时,我们不再打印“mlock failed”消息。 相反,我们只是保存它失败的事实。 如果您的程序崩溃,我们会在错误文本中打印 mlock 失败的事实。 这意味着“我们认为您的内核可能有问题。我们尝试了 mlock 解决方法,但它失败了。我们还是运行了您的程序,但它最终崩溃了。” 也许是由于内核错误,也许只是您程序中的错误。

@randall77感谢您的回复。 那么是否可以肯定地说,如果 mlock 失败并且程序在发送抢占信号之前触摸堆栈时没有崩溃,那么程序中不存在与异步抢占相关的内存损坏?

不幸的是没有。 如果 mlock 失败并且您的内核有问题,则可能会发生内存损坏。 仅仅因为程序没有崩溃并不意味着某处没有损坏。 崩溃是内存损坏的副作用——只是 mlock 失败不会导致崩溃。 (我们曾经在 1.14 中这样做。这是我们为 1.14.1 更改的内容之一。)
即使您关闭异步抢占,内存损坏可能仍会发生。 只是以较低的速率,因为您的程序可能仍在获取其他信号(计时器等)。

@smasher164如果您发现 wiki 页面https://golang.org/wiki/LinuxKernelSignalVectorBug不清楚,

我该如何选择退出?

我在一个由 lxd 创建非特权lxc 容器中,因此它具有与主机相同的内核,但无法设置系统范围的限制:

# container
$ uname -a
Linux runner-33-project-447-concurrent-0-job-25150 5.4.0-31-generic #35-Ubuntu SMP Thu May 7 20:20:34 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ /proc/version_signature
/proc/version_signature: Permission denied
$ ulimit -l 123456
ulimit: max locked memory: cannot modify limit: Operation not permitted
# host
$ uname -a
Linux gitlab-ci-runner-lxd-2 5.4.0-31-generic #35-Ubuntu SMP Thu May 7 20:20:34 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ cat /proc/version_signature 
Ubuntu 5.4.0-31.35-generic 5.4.34

根据https://github.com/golang/go/issues/37436#issuecomment -595836976 主机(焦点)包含内核补丁。

Go 是几天前使用go get golang.org/dl/go1.14go1.14 download构建的

$ go version
go version go1.14 linux/amd64

再次:我如何选择退出?

我可能不能/不想更改系统范围的限制,因为其他程序/管道受到影响,并且在更改系统范围的选项之前必须对所有这些可能性达成集体共识。

令人高兴的是,我们有这样的检测,但它被打破,我们不能选择退出容易。 当我们知道时,内核具有固定的补丁,并且检测容易出现误报。

@dionysius这已在 1.14.1 中修复,如https://github.com/golang/go/issues/37436#issuecomment -597360484 中所述。 我相信您需要升级到更新版本的 go: https :

要尝试然后显式加载 go 版本,我希望go get golang.org/dl/go1.14加载最新的 1.14。 会回来汇报的。

编辑,截至今天,似乎 1.14.3 是最新的 1.14

更新: go get golang.org/dl/go1.14.3看起来不错,出乎意料的是没有补丁不会加载最新的,很高兴知道(否则我永远不会遇到这个问题)

提醒一下 - Go1.15 即将发布,并且已经发布了一个测试版,但临时补丁尚未删除(在 Go1.15 有待办事项评论要删除)。

我认为删除解决方法很重要,因为 Ubuntu 20.04 LTS 使用修补的 5.4.0 内核。 这意味着 Ubuntu 20.04 上的任何用户仍然会不必要地 mlock 页面,并且如果他在 docker 容器中运行,则每次崩溃都会显示该警告,而不管他的内核并不是真正有问题的事实。 因此,这些用户可能会疯狂地试图理解和阅读所有这些信息,这与他们的错误无关,可能与整个 Ubuntu 20.04 生命周期有关。

@DanielShaulov谢谢。 你能为此开一个新问题吗? 这与 1.14 中的问题有关。

@networkimprov确定:#40184

更改https://golang.org/cl/243658提到这个问题: runtime: let GODEBUG=mlock=0 disable mlock calls

更改https://golang.org/cl/244059提到这个问题: runtime: don't mlock on Ubuntu 5.4 systems

go1.14.7 什么时候发布这个修改?

自 1.14.1(几个月前发布)以来的每个版本中都包含此修复程序。

更改https://golang.org/cl/246200提到这个问题: runtime: revert signal stack mlocking

此页面是否有帮助?
0 / 5 - 0 等级