Go: runtime : mlock de la pile de signaux a échoué : 12

Créé le 25 févr. 2020  ·  128Commentaires  ·  Source: golang/go

Quelle version de Go utilisez-vous ( go version ) ?

 $ go version
 aller à la version go1.14rc1 linux/amd64

Ce problème se reproduit-il avec la dernière version ?

J'ai touché ceci avec l'image docker golang:1.14-rc-alpine , l'erreur ne se produit pas en 1.13.

Quel système d'exploitation et architecture de processeur utilisez-vous ( go env ) ?

go env Sortie

$ aller env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/aller"
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"
CC="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- commutateurs gcc"

Qu'est-ce que tu as fait?

Clonez https://github.com/ethereum/go-ethereum , remplacez la version du constructeur dans Dockerfile à golang:1.14-rc-alpine (ou utilisez le Dockerfile ci-dessous) , puis à partir de la racine construisez l'image docker :

$ 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

Que vous attendiez-vous à voir ?

Go devrait exécuter nos scripts de construction avec succès.

Qu'avez-vous vu à la place ?

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

Commentaire le plus utile

Le bogue du noyau s'est manifesté par une corruption aléatoire de la mémoire dans Go 1.13 (avec et sans planification préemptive). Ce qui est nouveau dans Go 1.14, c'est que nous détectons la présence du bogue, essayons de le contourner et préférons planter tôt et fort si ce n'est pas possible. Vous pouvez voir les détails dans le problème que je vous ai référé.

Puisque vous m'avez traité de malhonnête et de méchant, je vous rappelle encore une fois le code de conduite : https://golang.org/conduct. J'ai aussi fini de participer à cette conversation.

Tous les 128 commentaires

C'est la conséquence d'essayer de contourner un bogue du noyau qui affecte considérablement les programmes Go. Voir https://github.com/golang/go/issues/35777. Le message d'erreur suggère les deux seuls correctifs disponibles connus : augmenter l'ulimit ou mettre à niveau vers un noyau plus récent.

Le message d'erreur suggère les deux seuls correctifs disponibles connus : augmenter l'ulimit ou mettre à niveau vers un noyau plus récent.

Eh bien, j'utilise l'image officielle de docker alpin, dont le but est de pouvoir créer un programme de Go. Apparemment, il ne peut pas. À mon humble avis, l'image en amont devrait être celle fixée pour remplir son objectif, pas notre infra de construction pour contourner un bogue dans l'image en amont.

L'image Alpine est-elle maintenue par l'équipe Go ? (Véritable question. Je ne sais pas.) Quoi qu'il en soit, oui, l'image devrait être corrigée, idéalement avec une mise à niveau du noyau.

Je ne sais pas exactement qui et comment gère les images docker (https://hub.docker.com/_/golang), mais le référentiel docker hub est une "image officielle", ce qui est un statut très difficile à obtenir, donc je suppose que quelqu'un d'assez haut placé dans la chaîne alimentaire est responsable.

Il est "maintenu par la communauté Docker". Les problèmes doivent être déposés à

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

EDIT : le problème est le noyau hôte, pas l'image de la bibliothèque Docker, ils ne peuvent donc pas le résoudre.

Donc, la solution officielle au plantage de Go est de pointer du doigt tout le monde pour pirater votre code ? Logique.

@karalabe Je voudrais vous rappeler https://golang.org/conduct. En particulier, soyez respectueux et charitable.

Veuillez répondre à la question

Il est de pratique courante de rediriger les problèmes vers le bon système de suivi des problèmes.

Il y a une discussion approfondie sur les solutions de contournement et les correctifs possibles dans le problème auquel j'ai lié plus tôt, si vous souhaitez voir quelles options ont été envisagées du côté Go.

Ce problème ne se produit pas avec Go 1.13. Ergo, c'est un bug introduit dans Go 1.14.

Dire que vous ne pouvez pas le réparer et dire aux gens d'utiliser des solutions de contournement est malhonnête, car restaurer un morceau de code le résoudrait en fait. Une solution alternative serait de détecter les plates-formes/noyaux problématiques et de fournir un mécanisme de secours intégré à Go.

Dire aux gens d'utiliser un noyau différent est particulièrement désagréable, car ce n'est pas comme si la plupart des gens pouvaient construire eux-mêmes un nouveau noyau. Si alpine ne publie pas de nouveau noyau, la plupart des développeurs ne peuvent pas faire grand-chose. Et enfin, si votre projet repose sur une infrastructure stable où vous ne pouvez pas simplement échanger des noyaux, vous êtes à nouveau dans le pétrin.

Il est de pratique courante de rediriger les problèmes vers le bon système de suivi des problèmes.

Le fait que Go plante n'est pas la faute de Docker. La redirection d'un crash Go vers un référentiel docker est une déviation.

Vous pouvez également désactiver la planification préemptive au moment de l'exécution

$ GODEBUG=asyncpreemptoff=1 ./your_app

@ianlancetaylor, nous avons une suggestion à faire lors de l'exécution sur un noyau affecté ; est-ce viable ?

BTW, c'est un problème connu que les modules de la bibliothèque Docker ne reçoivent pas de mises à jour en temps opportun, ce qui est un problème de sécurité. Caveat videur.

Le bogue du noyau s'est manifesté par une corruption aléatoire de la mémoire dans Go 1.13 (avec et sans planification préemptive). Ce qui est nouveau dans Go 1.14, c'est que nous détectons la présence du bogue, essayons de le contourner et préférons planter tôt et fort si ce n'est pas possible. Vous pouvez voir les détails dans le problème que je vous ai référé.

Puisque vous m'avez traité de malhonnête et de méchant, je vous rappelle encore une fois le code de conduite : https://golang.org/conduct. J'ai aussi fini de participer à cette conversation.

@karalabe , je me suis mal exprimé, le problème est votre noyau hôte, pas l'image Docker. Vous n'arrivez pas à le mettre à jour ?

Je suis sur le dernier Ubuntu et le dernier noyau disponible. Apparemment, tous les noyaux Ubuntu disponibles ne conviennent pas à Go 1.14 https://packages.ubuntu.com/search?keywords=linux-image-generic en fonction du message d'erreur.

Pouvez-vous ajouter la sortie de $ uname -a au texte principal du problème ? Et peut-être supprimer les traces de la pile de goroutine ?

J'ai posté une note à golang-dev.

cc @aclements

Quand vous dites que vous êtes sur la dernière version d'ubuntu et du noyau, que voulez-vous dire exactement (c'est-à-dire la sortie de dpkg -l linux-image-*, lsb_release -a, uname -a, ce genre de chose) car pour autant que je puisse voir le le correctif est dans le noyau dans la poche des mises à jour pour la 19.10 (version stable actuelle) et la 20.04 (version de développement). Ce n'est pas dans le noyau GA pour 18.04 mais dans le noyau HWE, mais d'autres ne sont pas construits avec gcc 9 et ne devraient donc pas être affectés de toute façon.

@networkimprov La désactivation de la préemption du signal rend le bogue moins susceptible de se produire, mais il est toujours présent. C'est un bug dans certaines versions du noyau Linux. Le bogue affecte tous les programmes dans toutes les langues. Il est particulièrement susceptible d'être observable avec les programmes Go qui utilisent la préemption de signal, mais il est également présent pour tous les autres programmes.

Go essaie de contourner le bogue en verrouillant la pile de signaux. Cela fonctionne bien à moins que vous ne rencontriez la limite mlock. Je suppose que l'un des inconvénients de cette solution de contournement est que nous rendons le problème très visible, plutôt que d'échouer occasionnellement en raison d'une corruption aléatoire de la mémoire, comme cela se produirait si nous ne faisions pas le mlock.

À un moment donné, il n'y a aucun moyen de contourner un bogue du noyau.

@karalabe

Je suis sur le dernier Ubuntu et le dernier noyau disponible

$ 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

qui satisfait aux exigences de version minimale.

De la même manière:

$ 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

Pouvez-vous clarifier ce que vous voyez ?

@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

Désolé, mon commentaire précédent était trompeur. Car bien sûr, la version du noyau renvoyée par uname -a dans le conteneur Docker sera celle de l'hôte.

D'où par :

$ 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

vous devez mettre à niveau le noyau du système d'exploitation hôte.

FWIW, les étapes que vous avez décrites ci-dessus en utilisant Alpine to make geth fonctionnent pour moi. :

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

Oui, mais dans mes articles précédents, j'ai souligné que je suis déjà sur le dernier Ubuntu et que j'ai installé le dernier noyau disponible à partir du référentiel de packages. Je ne vois pas comment je pourrais mettre à jour mon noyau pour qu'il fonctionne avec Go 1.14 à part reconstruire l'intégralité du noyau à partir des sources. Peut-être que j'ai raté quelque chose ?

Juste pour souligner, je comprends quelle est la solution de contournement et si je veux la faire fonctionner, je peux. J'ai ouvert ce rapport de problème parce que je m'attendrais à ce que d'autres personnes finissent par rencontrer le même problème. Si la simple mise à jour de mon système résolvait le problème, j'accepterais volontiers cela comme solution, mais à moins que quelque chose ne me manque, le noyau corrigé n'est pas disponible pour les utilisateurs (récents) d'Ubuntu, donc une base d'utilisateurs assez importante pourrait être affectée.

Oui, mais dans mes articles précédents, j'ai souligné que je suis déjà sur le dernier Ubuntu et que j'ai installé le dernier noyau disponible à partir du référentiel de packages. Je ne vois pas comment je pourrais mettre à jour mon noyau pour qu'il fonctionne avec Go 1.14 à part reconstruire l'intégralité du noyau à partir des sources. Peut-être que j'ai raté quelque chose ?

Hm oui, je viens de reproduire sur focale aussi. Le correctif est présent dans le git pour le noyau Ubuntu eoan : https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426 et ce commit est dans l'ascendance de la 5.3. 0-40.32 donc le correctif devrait être dans le noyau que vous utilisez. En d'autres termes, je pense que nous devons impliquer l'équipe du noyau -- je vais essayer de le faire.

@karalabe - Je viens de réaliser mon erreur : je pensais que j'utilisais la dernière version d'Ubuntu, j'utilise en fait eoan .

@mwhudson - juste une chose à noter (bien que vous le sachiez probablement déjà), un coup d'œil superficiel sur le code responsable de ce commutateur :

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

semble suggérer que le côté Go recherche la version 15 ou supérieure du correctif. Que rapporte la 5.3.0-40.32 en tant que version de correctif ? Je suppose 0 ?

Rouvrir cette discussion jusqu'à ce que nous ayons terminé le problème ici.

Un petit résumé car j'ai dû le reconstituer moi-même :

Il semble donc que le noyau d'Ubuntu soit corrigé, mais la solution de contournement est activée de toute façon.

Il semble donc que le noyau d'Ubuntu soit corrigé, mais la solution de contournement est activée de toute façon.

Ah d'accord, oui, je devrais lire l'échec, n'est-ce pas ? C'est la solution de contournement qui échoue plutôt que le bogue d'origine, dans un cas où la solution de contournement n'est pas réellement nécessaire mais il n'y a aucun bon moyen pour Go de le savoir. Je peux corriger l'extraction du package Go 1.14 dans Ubuntu, mais cela n'aide pas les utilisateurs exécutant, par exemple, l'image docker golang:1.14-alpine . Hum.

Je suppose que la question est de savoir combien d'utilisateurs utilisent des noyaux "vulnérables" à ce stade. Il ne peut pas y avoir autant de distributions qui compilent un noyau non corrigé avec gcc 9 maintenant.

Il ne peut pas y en avoir autant...

Derniers mots célèbres? :RÉ

Plus sérieusement, je ne pense pas que les gens mettent à jour les noyaux fréquemment, et de nombreux systèmes ne le peuvent pas. Peut-être qu'une meilleure question à poser serait de savoir quels systèmes/distributions utilisent des noyaux vulnérables par défaut et supposez simplement qu'il y aura beaucoup de gens bloqués dessus.

Je peux corriger l'extraction du package Go 1.14 dans Ubuntu, mais cela n'aide pas les utilisateurs exécutant, par exemple, l'image docker golang:1.14-alpine . Hum.

Vous manqueriez également les utilisateurs qui construisent à partir de la source. Par exemple, Ethereum fait des builds source pour nos PPA car il n'y a pas de bundle Go récent pour toutes les distributions sur Launchpad.

Est-il courant pour Ubuntu (et d'autres distributions ?) d'utiliser la sélection au lieu de suivre les versions de correctifs du noyau Linux ? Sur https://packages.ubuntu.com/search?keywords=linux-image-generic, tous les noyaux ont une version de correctif de zéro.

En ce qui concerne une recherche rapide sur Google, Ubuntu ne publie pas de nouveaux noyaux après la livraison de la distribution, mais uniquement des correctifs de sécurité choisis (c'est-à-dire pas de modification de version de correctif). Les versions LTS (prises en charge pendant 5 ans) font exception à cette règle, qui peuvent obtenir des mises à jour du noyau toutes les quelques années pour prendre en charge le nouveau matériel (mais c'est aussi très rare). Je ne sais pas pour les autres distributions.

Avec mes connaissances limitées en ce moment, cela semble étrange. Les versions de correctifs sont destinées à distribuer des correctifs de manière contrôlée afin que les utilisateurs (et dans ce cas l'environnement d'exécution Go) puissent savoir quels correctifs sont inclus et lesquels ne le sont pas. Cependant, c'est comme ça, nous devons donc vivre avec.

Questions ouvertes:

  • Combien de personnes sont concernées par ce problème ?
  • La ulimit une solution de contournement viable ?
  • Ubuntu est-elle la seule distribution qui "ne tient pas compte" des numéros de patch du noyau Linux ?
  • En fonction des réponses aux questions ci-dessus : Serait-il raisonnable d'ajouter une détection spéciale pour Ubuntu ?

@neelance

Est-il courant pour Ubuntu (et d'autres distributions ?) d'utiliser la sélection au lieu de suivre les versions de correctifs du noyau Linux ?

Beaucoup de distributions le font, pas seulement Ubuntu. Debian le fait, Red Hat Enterprise Linux le fait, et je m'attends à ce que SUSE le fasse également pour leurs distributions d'entreprise. La sélection sélective est le seul moyen d'obtenir des corrections de bogues si vous ne pouvez pas suivre de manière agressive les versions stables en amont (et changer de version stable lorsque le support en amont disparaît). Fedora est une exception ; il se rebase sur la dernière version stable du noyau amont après un certain temps.

Il y a aussi la question des noyaux propriétaires utilisés par les moteurs de conteneurs. Nous ne pouvons même pas regarder les sources pour eux, et certains d'entre eux ont menti sur les numéros de version du noyau dans le passé. Je suppose qu'ils utilisent également la cueillette.

Généralement, les vérifications de version pour les fonctionnalités du noyau (ou les bogues) sont une très mauvaise idée. C'est pire pour Go en raison de la liaison statique, il est donc impossible d'échanger le temps d'exécution sous une application pour corriger les attentes de son noyau.

Est-il courant pour Ubuntu (et d'autres distributions ?) d'utiliser la sélection au lieu de suivre les versions de correctifs du noyau Linux ? Sur https://packages.ubuntu.com/search?keywords=linux-image-generic, tous les noyaux ont une version de correctif de zéro.

La chaîne de version de base du noyau ne change pas, c'est vrai. Mais cela ne signifie pas que les versions stables en amont ne sont pas fusionnées, le numéro ABI est remplacé à la place lorsqu'il y a des changements de code.

Notez que vous avez choisi le méta-paquet qui n'affiche pas le journal des modifications approprié, vous pouvez voir ici que la dernière version 5.4.0-14.17 a fusionné la version stable 5.4.18 :
http://changelogs.ubuntu.com/changelogs/pool/main/l/linux-5.4/linux-5.4_5.4.0-14.17/changelog

Il semble qu'une détection automatique appropriée sur toutes les distributions soit presque impossible. Je vois trois options :

  • Ne fais rien.
  • Activez la solution de contournement.
  • Optez pour la solution de contournement.

Ou désactivez la préemption asynchrone par défaut sur 5.3.x et 5.4.x, et laissez les utilisateurs l'activer au moment de l'exécution.

https://github.com/golang/go/issues/37436#issuecomment -591237929 indique que la désactivation de la préemption asynchrone n'est pas une solution appropriée, n'est-ce pas ?

Pas à proprement parler, mais il n'y avait pas eu de rapports sur le problème avant l'arrivée de la préemption asynchrone.

En fait, le runtime pourrait-il créer un processus enfant au démarrage (pour 5.3.x et 5.4.x) qui déclenche le bogue et active la solution de contournement si c'est le cas ? IIRC il existe un reproducteur fiable, voir https://github.com/golang/go/issues/35326#issuecomment -558690446

La désactivation de la préemption asynchrone est une distraction. Les programmes exécutés sur des noyaux défectueux sont toujours cassés. C'est juste que le bris apparaît comme une corruption de mémoire étrange plutôt que comme une erreur de dépassement d'une limite mlock qui pointe vers les versions du noyau. Bien qu'évidemment nous voulions résoudre entièrement le problème, je pense qu'étant donné le choix d'une erreur claire ou d'une corruption aléatoire de la mémoire, nous devrions toujours choisir l'erreur claire.

Je suis d'accord que la détection de version du noyau est terrible, c'est juste que nous ne connaissons aucune autre option. Si quelqu'un a des suggestions à ce sujet, ce serait très utile.

Une chose que nous pourrions faire est d'ajouter un paramètre GODEBUG pour désactiver mlock ing la pile de signaux. Cela donnerait aux gens une solution de contournement centrée sur le problème réel. Nous pouvons mentionner ce paramètre dans le message d'erreur. Je crains que cela ne conduise les gens à activer le paramètre, qu'ils aient ou non un noyau patché. Mais au moins, cela donnera aux personnes qui ont vraiment un noyau patché un moyen de contourner ce problème. CC @aclements

En fait, le runtime pourrait-il créer un processus enfant au démarrage (pour 5.3.x et 5.4.x) qui déclenche le bogue et active la solution de contournement si c'est le cas ? IIRC il existe un reproducteur fiable, voir #35326 (commentaire)

C'est une idée intéressante mais je pense que dans ce cas, le test est beaucoup trop coûteux à exécuter au démarrage pour chaque programme Go.

J'ai peut-être raté quelque chose (ce fil est devenu long vite !), mais quel est l'inconvénient ou la difficulté de simplement augmenter la limite de mlock ? Il y a peu de raisons de ne pas simplement le définir sur illimité, mais même si vous ne voulez pas le faire, vous n'avez besoin que de 4 Ko par thread, donc un simple 64 MiB est littéralement plus que le temps d'exécution d'un seul processus est capable de mlocker . AFAIK, la plupart des distributions le laissent illimité par défaut. La seule exception notable que je connaisse est Docker, qui le définit à (je pense) 64 Kio par défaut, mais cela peut être augmenté en passant --ulimit memlock=67108864 à Docker.

Il semble que nous ayons déjà une solution de contournement assez simple en place. Y a-t-il quelque chose qui empêche les gens de faire ça ?

Le problème est que vous ne devriez pas avoir à appliquer une solution de contournement manuelle, si possible. Cela ressemble à une régression en 1.14.

Il ne peut pas être corrigé du côté de la bibliothèque Docker : https://github.com/docker-library/golang/issues/320

Le problème avec la solution ulimit contournement

Le scénario le plus intéressant est ce qui se passe si quelqu'un utilise une ancienne version d'un noyau qui n'est pas affectée, et à un moment donné bascule le noyau vers une nouvelle qui l'est. Tout à coup, les choses vont commencer à se briser, mais puisque Go est la couche d'application et le noyau est la couche du système d'exploitation, il faudra du temps pour établir la connexion et trouver un correctif. La question est de savoir quel sera le coût.


Cependant, ce qui n'est pas immédiatement clair, c'est si le compilateur Go ou toutes les applications créées par Go ont également des problèmes ? Si seulement le compilateur, c'est un cas chanceux et les retombées peuvent être contenues. Cependant, si toutes les applications Go construites avec 1.14 ont tendance à paniquer, cela pourrait vraiment nuire à la portabilité binaire pré-construite, car tout d'un coup, mon code pourrait ne pas fonctionner sur un système différent (c'est en fait tout à fait valide, utilise simplement un noyau différent schéma de version).

Cependant, ce qui n'est pas immédiatement clair, c'est si le compilateur Go ou toutes les applications créées par Go ont également des problèmes ?

Le bogue du noyau Linux n'est même pas spécifique à Go. Si je comprends bien, cela affecte n'importe quel programme, même un programme écrit en C ! — qui utilise les registres XMM ou YMM et peut recevoir des signaux. Les programmes Go sous 1.14 sont _plus_ gravement affectés que de nombreux autres programmes car ils utilisent des signaux en interne pour la préemption de goroutine, et c'est pourquoi l'environnement d'exécution Go 1.14 inclut la solution mlock contournement

Le bogue du noyau Linux n'est même pas spécifique à Go.

Ouais, mais mon noyau est patché, mais Go panique toujours dessus :)

@aclements

La seule exception notable que je connaisse est Docker, qui le définit à (je pense) 64 KiB par défaut, mais cela peut être déclenché en passant --ulimit memlock=67108864 à Docker.

Il semble que nous ayons déjà une solution de contournement assez simple en place. Y a-t-il quelque chose qui empêche les gens de faire ça ?

Malheureusement oui. Dans notre cas ici, nous ne pouvons pas dire à nos clients de reconfigurer leurs conteneurs docker. Il y en a trop et ils sont sensibles aux changements d'environnement dans leurs configurations ; en effet, c'est pourquoi nous avons choisi de livrer l'application dans un conteneur docker, de sorte que la façade d'un outil d'isolation atténuerait le souci de modifier les options de configuration. Changer le contenu du conteneur est bien, mais la façon dont nous invoquons le conteneur peut ne pas l'être aussi bien.

@aclements

Il y a peu de raisons de ne pas simplement le définir sur illimité, mais même si vous ne voulez pas le faire, vous n'avez besoin que de 4 Ko par thread, donc un simple 64 MiB est littéralement plus que le temps d'exécution d'un seul processus est capable de mlocker . AFAIK, la plupart des distributions le laissent illimité par défaut.

Cela ne semble pas être le cas - il s'agit d'un Ubuntu LTS avec seulement 6 Ko d'espace de verrouillage :

~$ 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

Quel est le contenu de /proc/version pour un exemple de noyau qui est corrigé pour fonctionner correctement mais pour lequel Go produit actuellement l'erreur mlock ? Merci.

@ucirello Voyez -vous un problème lors de l'exécution des programmes Go sur le système que vous décrivez ?

$ 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

Peut-être pourrions-nous utiliser la date enregistrée dans /proc/version comme signal supplémentaire. Cela devrait probablement être spécifique à la version, ce qui est pénible. Mais tout cela est douloureux.

La discussion semble porter soit sur l'acceptation de plus de faux positifs ou de faux négatifs. Voici un résumé :

Faux positif : la solution de contournement est activée sur un noyau corrigé.

  • Reproductible. Des instructions peuvent être affichées.
  • Cela ressemble à une régression.
  • Difficile à corriger dans certains environnements.
  • Go binary peut s'exécuter dans certains environnements mais ne s'exécute pas dans d'autres.

Faux négatif : la solution de contournement n'est pas activée sur un noyau non corrigé.

  • L'échec ne se produit que rarement, surtout si la préemption asynchrone est désactivée.
  • Peut-être des conséquences graves dues à la corruption de la mémoire.
  • Difficile à déboguer.

@ianlancetaylor Je ne vois aucun problème dans le système décrit, mais d'après ce que je comprends, cela pourrait être une coïncidence, à savoir le message d'erreur dit que je devrais mettre à niveau après 5.3.15, 5.4.2 ou 5.5 et c'est un 4.4.x boîte.

Dans tous les cas, ce que je voulais souligner, c'est que l'hypothèse selon laquelle la plupart des distributions livrent ulimit -l avec 64M semble être incorrecte. Il s'agit d'une machine Linux dont le ulimit -l vaut 64 Ko - et c'est une installation standard.

Existe-t-il un moyen de détecter le paramètre ulimit et d'éviter le plantage si la solution de contournement ne peut pas être appliquée ? De cette façon, nous pourrions encore améliorer la situation mais ne pas provoquer de nouveaux accidents ? Nous pourrions alors à la place afficher un avertissement concernant une corruption potentielle de la mémoire.

Existe-t-il un moyen de détecter le paramètre ulimit et d'éviter le plantage si la solution de contournement ne peut pas être appliquée ?

En effet, le code actuel détecte déjà le paramètre ulimit. Il ne l'interroge pas directement, mais il détecte quand il se heurte à lui, c'est pourquoi il imprime ce message d'erreur particulier demandant à l'utilisateur d'augmenter l'ulimit. Si quelque chose d'autre se passe mal lors du verrouillage, il imprimera en fait un message différent. Le crash est intentionnel car nous avons considéré cela préférable à une corruption aléatoire de la mémoire.

Dans tous les cas, ce que je voulais souligner, c'est que l'hypothèse selon laquelle la plupart des distributions fournissent ulimit -lwith 64M semble être incorrecte. Il s'agit d'une machine Linux dont l'ulimit -l est de 64K - et c'est une installation standard.

@ucirello , merci pour ce point de données. Je n'ai pas de boîte Ubuntu à portée de main, mais j'ai fait le débogage d'origine sur une installation Ubuntu et j'aurais pu jurer qu'il n'y avait pas d'ulimit défini. Ce n'est certainement pas dans un conteneur?

@ianlancetaylor J'ai créé un script rapide et sale pour vérifier ce que rapporte uname: https://gist.github.com/Tasssadar/7424860a2764e3ef42c7dcce7ecfd341

Voici le résultat des tests Debian à jour (enfin, -ish) :

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)

Étant donné que Go n'utilise que la chaîne de publication, la vérification de la version du correctif ne fonctionne fondamentalement que sur les noyaux vanille - Debian et RHEL/CentOS (qui ont heureusement un noyau trop ancien) le font de cette façon, ils conservent le .0 et spécifient le version de patch réel plus tard. Malheureusement, ils n'utilisent pas le même format pour version .

EDIT : et pour le rendre encore plus gênant, Ubuntu ne met pas du tout le numéro de patch dans uname, même s'ils ont probablement tous les correctifs intégrés. Peut-être que le meilleur plan d'action est d'en faire un avertissement au lieu d'un crash ? À ce stade, la plupart des noyaux sont probablement déjà mis à jour de toute façon.

Le crash est intentionnel car nous avons considéré cela préférable à une corruption aléatoire de la mémoire.

@aclements Il y a un petit défaut dans cet argument : le problème ne sont pas les vrais positifs qui se heurtent à l'ulimit, mais les faux positifs qui n'auraient pas de corruption de mémoire sans la solution de contournement.

@aclements

Ce n'est certainement pas dans un conteneur?

Je suis sûr à 100% que ce n'est pas un conteneur. C'est quand même une VM.

$ sudo virt-what
xen
xen-hvm

@ucirello , merci pour ce point de données. Je n'ai pas de boîte Ubuntu à portée de main, mais j'ai fait le débogage d'origine sur une installation Ubuntu et j'aurais pu jurer qu'il n'y avait pas d'ulimit défini. Ce n'est certainement pas dans un conteneur?

En tant que point de données différent, mon système principal Ubuntu Eoan est défini par défaut sur 64 Mo pour ulimit -l .

@ucirello Il n'y a pas de problème sur les noyaux Linux 4.4.x. Le bogue est apparu pour la première fois dans la version 5.2 du noyau.

@ianlancetaylor -- merci pour l'information. Je suppose que j'ai mal analysé le message d'erreur alors. Je n'étais pas au courant de ce fait, et le message d'erreur faisait croire que les nouveaux binaires seraient incompatibles avec les noyaux plus anciens.

Les programmes Go ne signaleront le message d'erreur que sur les noyaux avec un numéro de version indiquant qu'ils peuvent avoir le problème.

Voici quelque chose que nous pourrions faire :

  1. Utilisez uname pour vérifier la version du noyau pour un noyau vulnérable, comme nous le faisons aujourd'hui.
  2. Si le noyau est vulnérable selon la version, lisez /proc/version .
  3. Si /proc/version contient la chaîne "2020" , supposez que le noyau est patché.
  4. Si /proc/version contient la chaîne "gcc version 8" supposez que le noyau fonctionne même s'il est corrigé (car le bogue ne se produit que lorsque le noyau est compilé avec GCC 9 ou une version ultérieure).
  5. Sinon, appelez mlock sur les piles de signaux comme nous le faisons aujourd'hui sur les noyaux vulnérables.

Le but de ceci est de réduire le nombre de fois que les programmes Go manquent de mlock espace.

Est-ce que quelqu'un connaît des noyaux non corrigés qui peuvent avoir la chaîne "2020" dans /proc/version ?

Pour des raisons de sécurité, nous devrions probablement essayer d'identifier les moments où le noyau a été patché pour les principales distributions. Y a-t-il quelqu'un qui peut identifier cela pour une distribution particulière? Merci.

Je ne sais pas si cela est utile, mais Ubuntu rend apparemment la version standard du noyau disponible pour ceux qui recherchent :

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

@jrockway Merci, le problème n'est pas que nous n'ayons pas la version du noyau, c'est qu'Ubuntu utilise une version du noyau qui a le bogue, mais Ubuntu a appliqué un correctif pour le bogue, donc le noyau fonctionne réellement, mais nous ne Je ne sais pas comment détecter ce fait.

En ajoutant à l'heuristique de correspondance de chaîne de @ianlancetaylor , vous pouvez également vérifier /proc/version pour
5.3.x && x >= 15 || 5.4.x && x >= 2 (pas du vrai code, mais vous voyez l'idée)

Merci, nous le vérifions déjà dans les résultats uname .

Je fais référence à 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

EDIT : Sur Ubuntu, vous pouvez vérifier /proc/version_signature comme je l'ai suggéré.

Ah, désolé, j'ai raté ça. Ainsi, selon ce commentaire sur certains systèmes, le champ release et le champ version renvoyés par uname diffèrent de la version du noyau qu'ils rapportent. Nous vérifions actuellement le champ release mais pas le champ version .

Le fichier /proc/version contient à nouveau un ensemble d'informations différent.

Mon système Ubuntu a /proc/version mais n'a pas /proc/version_signature .

@bbarenblat a utilement collecté uname et /proc/version à partir de plusieurs versions de plusieurs distributions pour voir à quoi elles ressemblent dans la nature (j'en ai ajouté quelques-unes des miennes):

Debian instable :

$ 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 (stable actuelle) :

$ 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 (très ancienne) :

$ 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 « quelque part au-delà de la stabilité, avec le paquet instable occasionnel » :

$ 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

Ubuntu 19.10 avec le noyau GCP (c'est vraiment 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 avec noyau construit à la main :

$ 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 Je suis un peu confus quant à savoir lesquels des noyaux que vous avez publiés ont besoin de la solution de contournement et lesquels sont déjà corrigés. Peut-être pourriez-vous annoter votre message.

Le problème est que si vous vous trouvez dans les plages de versions potentiellement mauvaises, vous pourriez être affecté, si vous êtes en dehors de la plage de versions, vous êtes en sécurité.
Lorsque la corruption initiale a été découverte, nous avons immédiatement retiré le correctif, l'avons testé et l'avons déployé. Notre noyau était donc à 100% dans la mauvaise plage mais patché. Être en dehors de la plage signifie que vous êtes en sécurité, mais à l'intérieur de la plage, vous ne pouvez pas prouver que le bogue existe sans le tester. (En d'autres termes : les faux positifs se produiront par conception)

Je suis en train de construire un autre noyau Ubuntu patché avec des numéros de version en amont

  • make kernelversion
  • patcher debian{,.master}/{changelog,control} avec cette version

Les logiciels qui reposent sur la version du noyau sont la raison pour laquelle les distributions aiment la maintenir stable : si une mise à jour du numéro de version peut casser un logiciel qui fonctionnait auparavant, il vaut mieux ne pas changer de version (mais toujours appliquer les correctifs !).

Je ne suis pas content de lancer un nouveau noyau juste à cause de golang 1.14, mais je pense que cela fonctionnera.
Je déconseille à tout le monde d'expédier les binaires golang 1.14 en dehors des environnements contrôlés.

Le message de

Je pense qu'il devrait être opt-in pour éviter tous les faux positifs. Par exemple, ajoutez GOWORKAROUNDS env var boolean ou avec une liste de solutions de contournement ou pour permettre à l'heuristique d'essayer de les trouver.

Ce serait la solution la moins intrusive de l'OMI.

@fcuello-fudo le problème est que si la solution de contournement n'est pas activée sur un mauvais noyau, les symptômes sont très obscurs.

Que diriez-vous de réutiliser le concept « contaminé » du noyau Linux ? L'environnement d'exécution Go continuerait à détecter les mauvais noyaux et appliquerait la solution de contournement mlock, mais se marque lui-même entaché si mlock échoue (et ne plante pas). Ensuite, assurez-vous d'ajouter une note à toutes les paniques et de lancer des messages si l'indicateur de souillure est défini.

L'avantage est que les plantages faussement positifs sont évités, tout en fournissant une indication claire au cas où un mauvais noyau provoquerait un plantage.

L'inconvénient est qu'un mauvais noyau peut corrompre la mémoire en silence, sans provoquer de plantage visible.

@fcuello-fudo le problème est que si la solution de contournement n'est pas activée sur un mauvais noyau, les symptômes sont très obscurs.

Et le laisser activé par défaut mais il serait toujours utile de désactiver les solutions de contournement si l'utilisateur le souhaite ?

@howardjohn (développeur principal d'Istio de Google) sur https://github.com/istio/istio/issues/21672 :

Mon noyau sur ma machine personnelle n'est pas patché. Mais peu importe ce que ma machine possède, nous expédions Istio à des milliers d'utilisateurs. Je ne peux pas contrôler sur quelle machine ils l'exécutent, et j'aimerais ne pas avoir à restreindre cela à un sous-ensemble de versions du noyau

Le projet Prometheus retarde également sa mise à niveau go1.14 : https://github.com/prometheus/golang-builder/pull/85#issuecomment -592082645

J'ai rencontré ce problème lors de l'exécution d'applications go sur un cluster Kubernetes auto-hébergé. J'ai pu autoriser la solution de contournement _mlock_ à prendre effet en augmentant l'ulimit pertinent. Cependant, comme le processus de modification des ulimits pour les conteneurs Docker exécutés dans Kubernetes n'est pas exactement facile à trouver, cela pourrait aider quelqu'un d'autre à mettre les détails ici.

  1. Mettez /etc/security/limits.conf jour
* - memlock unlimited
  1. Mettez /etc/docker/daemon.json jour
"default-ulimits": { "memlock": { "name": "memlock", "hard": -1, "soft": -1 } }
  1. Redémarrez docker/kubernetes, ramenez vos pods.

  2. Entrez un conteneur en cours d'exécution et vérifiez que l'ulimit a été augmenté :

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

Vous pourrez peut-être vous en tirer en utilisant quelque chose de plus subtil que le marteau _illimité_ ; pour moi, une limite de 128KiB (131072) semblait fonctionner.

rencontrant également ce problème en essayant de créer une image docker pour https://github.com/RTradeLtd/Temporal on go 1.14

Et le tas continue de s'entasser. Tu vois, c'est pour ça que je me suis énervé au début de ce fil (ce qui était une grave erreur de ma part, j'en conviens). Même si j'ai fait beaucoup d'efforts pour expliquer et fournir une réplique qu'il s'agit d'un bloqueur, j'ai été fermé pour ne pas interférer avec la version. Même après qu'il était clair que ce n'était pas un problème de docker.

Maintenant, nous sommes dans un espace bien pire puisque divers projets mettent sur liste noire Go 1.14. Ce bug est actuellement prévu pour être corrigé dans Go 1.15 uniquement. Sur la base des problèmes liés ci-dessus, sommes-nous convaincus que c'est une bonne idée de reporter cela de 8 mois ? Je pense que ce serait bien de reconnaître le gâchis et d'essayer de le réparer dans une version de correctif, de ne pas attendre que d'autres projets soient mordus.

Oui, je suis conscient que je ne fais que harceler les gens ici au lieu de le réparer moi-même. Je suis désolé de ne pas pouvoir contribuer de manière plus significative, je ne veux tout simplement pas fragmenter l'écosystème. Les modules Go étaient déjà un coup dur pour de nombreux projets, ne doublons pas avec une autre bizarrerie dont les outils doivent prendre conscience.

Ian Lance-Taylor a déclaré que le correctif serait rétroporté une fois qu'il y en aurait un : https://groups.google.com/d/msg/golang-dev/_FbRwBmfHOg/mmtMSjO1AQAJ

@lmb Oh ça fait plaisir à entendre, merci pour le lien.

Pour info : Ubuntu 20.04 sera probablement disponible en version 5.4.0, ce qui signifie qu'à partir de la fin du mois prochain, le
les plus récents Ubuntu LTS et golang ne fonctionneront pas ensemble immédiatement.

Lorenz Bauer [email protected] schrieb am Fr., 6. März 2020, 12:42:

Ian Lance-Taylor a déclaré que le correctif sera rétroporté une fois qu'il y aura
un : https://groups.google.com/d/msg/golang-dev/_FbRwBmfHOg/mmtMSjO1AQAJ

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/golang/go/issues/37436?email_source=notifications&email_token=AAAO66R6S5VOWQBKETLI5UDRGDORJA5CNFSM4K3GBRRKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJLOment-5UDRGDORJA5CNFSM4K3GBRRKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2GODIZ69 ,
ou se désinscrire
https://github.com/notifications/unsubscribe-auth/AAAO66SH6J4M75WDJ5GO7HTRGDORJANCNFSM4K3GBRRA
.

@karalabe en termes aussi forts que possible, vous n'avez pas été "fermé pour ne pas interférer avec la sortie". Josh, qui a initialement fermé le problème, ne fait pas partie de l'équipe Go de Google (c'est-à-dire pas un décideur) et moi non plus. Nous avons initialement supposé que le projet Docker pouvait (et devrait) atténuer le problème dans leur version. Quand il est devenu clair qu'ils ne pouvaient pas, j'ai rapidement soulevé cette question sur golang-dev.

De plus, j'ai été le premier à remarquer que le problème provient de votre noyau hôte, et non du module Docker. Vous n'avez pas mentionné que vous étiez sur Ubuntu jusqu'à ce que je le signale.

Je pense que vous nous devez encore des excuses, après cette note.

EDIT : je vous ai également demandé de supprimer les traces de la pile de goroutine (à partir de goroutine x [runnable] ) de votre rapport, car elles rendent la page difficile à lire/naviguer. [Mise à jour : Russ a supprimé les piles.]

Tout le monde s'il vous plaît gardez à l'esprit qu'une communication réussie est difficile et une compétence qu'il faut pratiquer. Les émotions peuvent jouer contre nous et entraver notre objectif de communication réussie, j'y ai été moi-même. Oui, il y a eu une violation du code de conduite et le signaler est bien. Des excuses volontaires sont également utiles. Essayons maintenant de nous assurer que chaque publication a un impact net positif sur la collaboration et la résolution de ce problème.

@rtreffer Savez-vous si la version du noyau prévue pour Ubuntu 20.04 inclura le correctif ? Il semble qu'il ait été sélectionné dans le noyau 5.3.0 d'Ubuntu (https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426). Je suppose que c'est aussi dans leur noyau 5.4.0, mais ce serait bien d'en être certain. Merci.

@rtreffer Savez-vous si la version du noyau prévue pour Ubuntu 20.04 inclura le correctif ? Il semble qu'il ait été sélectionné dans le noyau 5.3.0 d'Ubuntu (https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426). Je suppose que c'est aussi dans leur noyau 5.4.0, mais ce serait bien d'en être certain. Merci.

Il sera inclus dans le noyau de la version 20.04, oui :

(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

Sur la base des discussions avec @aclements , @dr2chase , @randall77 et d'autres, notre plan pour la version 1.14.1 est :

  • écrire une page wiki décrivant le problème
  • continuer à utiliser mlock sur une version du noyau qui peut être boguée
  • si mlock échoue, notez silencieusement ce fait et continuez l'exécution
  • si nous voyons un SIGSEGV ou SIGBUS inattendu et que mlock échoué, alors dans la pile de crash, pointez les personnes sur la page wiki

L'espoir est que cela fournira une bonne combinaison d'exécution correcte dans le cas normal tout en dirigeant les personnes sur des noyaux potentiellement bogués vers des informations pour les aider à décider si le problème est leur noyau ou leur programme ou un bogue dans Go lui-même.

Cela peut également être combiné avec de meilleures tentatives pour identifier si un noyau particulier a été corrigé, sur la base du champ uname version (nous ne vérifions actuellement que le champ release ).

Une autre chose dont nous avons discuté : si un mlock échoue et que nous sommes sur le point de nous envoyer un signal, touchez d'abord la pile de signaux.

@ianlancetaylor ça a l'air d'être une excellente idée !

Si vous ne l'avez pas encore envisagé : lien vers cette page wiki à partir du message d'erreur (potentiellement avec une autre indirection)

Par exemple, iPXE le fait via des liens comme http://ipxe.org/1d0c6539 (leur sortie est optimisée pour les ROM de démarrage, l'espace limité, etc.)

  • écrire une page wiki décrivant le problème

  • continuer à utiliser mlock sur une version du noyau qui peut être boguée

  • si mlock échoue, notez silencieusement ce fait et continuez l'exécution

Ne vaudrait-il pas mieux désactiver également la préemption asynchrone ? Il est vrai que le problème peut arriver même avec asyncpreemptoff=1, mais le bug était suffisamment rare pour que sans lui personne ne s'en aperçoive pendant des mois.

Existe-t-il un moyen de tester (en dehors de l'environnement d'exécution Go) pour voir si votre noyau est corrigé ? Nous maintenons nos propres noyaux en dehors de la distribution, ce qui fait que cela fonctionne même.

@gopherbot, veuillez rétroporter sur go1.14. C'est un problème sérieux sans solution de contournement.

Problème(s) de rétroportage ouvert(s) : #37807 (pour la 1.14).

N'oubliez pas de créer le(s) CL(s) sélectionné(s) dès que le correctif est soumis au maître, selon https://golang.org/wiki/MinorReleases.

@nemith il y a un reproducteur C ici : https://github.com/golang/go/issues/35326#issuecomment -558690446

@ randall77 @aarzilli Après réflexion, je ne pense pas que ce soit une bonne idée d'ajouter des atténuations partielles supplémentaires, comme toucher la page de la pile de signaux ou désactiver la préemption asynchrone. C'est un bogue au niveau du noyau qui peut affecter n'importe quel programme qui reçoit un signal. Les personnes utilisant un noyau bogué devraient mettre à niveau le noyau d'une manière ou d'une autre. L'utilisation de mlock est une atténuation fiable qui devrait toujours fonctionner, et en tant que telle, il est raisonnable de l'essayer. Toucher la pile de signaux avant d'envoyer un signal de préemption, ou désactiver complètement la préemption du signal, n'est pas une atténuation fiable. Je pense qu'il ne faut pas retomber dans une atténuation peu fiable ; nous devrions dire à l'utilisateur de mettre à jour le noyau.

Les personnes qui ne peuvent pas mettre à niveau le noyau ont la possibilité d'exécuter avec GODEBUG=asyncpreemptoff=1 , ce qui sera une atténuation partielle tout aussi efficace que les deux autres.

Je pense qu'il ne faut pas retomber dans une atténuation peu fiable ; nous devrions dire à l'utilisateur de mettre à jour le noyau.

Je serais d'accord avec vous si le bug se manifestait toujours par un crash. Mais ce n'est pas le cas - cela corrompt simplement la mémoire au hasard. Peut-être qu'il se bloque, mais peut-être qu'il corrompt de manière aléatoire les données du programme et continue de s'exécuter. Je pense qu'il nous incombe de prendre toutes les mesures possibles pour empêcher la corruption des données, même si cela signifie que nous ne communiquerons pas aussi souvent sur la mise à niveau requise du noyau.

C'est une décision maintenant contre plus tard. Nous pouvons essayer d'empêcher la corruption des données maintenant en touchant les pages de signal avant utilisation. Ou nous pouvons essayer d'empêcher la corruption plus tard en envoyant un message à l'utilisateur en cas de plantage. On ne peut pas choisir les deux.

J'aimerais qu'il y ait un moyen de pouvoir envoyer un message lorsque nous détectons un échec de mlock, sans planter, puis de l'atténuer en touchant les pages. Je ne pense pas que le programme Go puisse le faire sur stdout/stderr, malheureusement. Peut-être pourrions-nous jeter quelque chose dans le syslog ? Cela n'aiderait que ceux qui jettent un coup d'œil au syslog, qui n'est probablement pas nombreux.

Point juste.

Que diriez-vous de poster un message sur l'installation? Cela implique une modification des procédures d'installation de vos versions binaires et des distributions, mais cela vous permettrait de lancer le reproducteur pour tester définitivement le bogue.

@networkimprov C'est une bonne idée, mais comme les gens envoient des programmes compilés sur des machines pouvant avoir des versions de noyau différentes, je pense que nous avons également besoin de l'approche décrite ci-dessus.

Le changement https://golang.org/cl/223121 mentionne ce problème : runtime: don't crash on mlock failure

J'ai envoyé https://golang.org/cl/223121. Il serait utile que les personnes ayant des problèmes avec la version 1.14 puissent voir si ce changement résout leur problème. Merci.,

Eh bien, @randall77 / @ianlancetaylor , j'ai tendance à ne pas être d'accord pour dire qu'il s'agit d'un problème de golang. Golang a découvert le problème de corruption de la mémoire, mais il s'agit d'un bogue très grave du noyau .

En tant que tel, il devrait s'intensifier à travers vos chemins de noyau.
Distributions a récupéré le patch et l'a expédié. Il a été rétroporté. Chaque nouvelle installation obtiendra un noyau non affecté.
Si vous lancez votre propre noyau, vous devez faire ce travail vous-même. Comme d'habitude.

Soyez utile pour les utilisateurs qui le touchent et soyez aussi utile que possible.
Mais je ne pense pas que ce soit la responsabilité de Golang de corriger un bogue du noyau ou même de forcer les utilisateurs à appliquer le correctif.

@rtreffer C'est ce que nous essayons de faire : être aussi utile que possible.

Sur les noyaux bogués, les programmes Go construits avec Go 1.14 se sont mal comportés de manière imprévisible. Nous ne voulons pas faire cela même sur un noyau bogué. Si un programme devait échouer rapidement et proprement, ce serait une chose. Mais ce que nous avons vu était une corruption de la mémoire conduisant à des erreurs obscures. Voir #35326, entre autres.

Pensez-vous que nous devrions prendre des mesures différentes de ce que nous faisons actuellement ?

@rtreffer Eh bien en

D'un autre côté, la solution de contournement basée sur les numéros de version du noyau nous a amenés à passer à des mlocks qui ont échoué en raison de problèmes ulimit. Ce n'est pas un bogue du noyau.

Cela étant dit, je ne suis pas sûr qu'il y ait une meilleure solution ici et l'équipe de Go a probablement pris la bonne décision.

@ianlancetaylor vous pourriez peut-être envoyer le reproducteur C dans les versions source et binaire, et le référencer sur la page wiki comme moyen de vérifier n'importe quel noyau.

Que diriez-vous de poster un message sur l'installation? Cela implique une modification des procédures d'installation de vos versions binaires et des distributions, mais cela vous permettrait de lancer le reproducteur pour tester définitivement le bogue.

Toute distribution qui prête suffisamment d'attention à cela ne fera que patcher son noyau à la place, sûrement.

@ianlancetaylor Je suis totalement d'accord avec la voie à suivre, le patch et la page wiki sont superbes.

Je voulais souligner que la corruption n'est pas une faute ou un bogue de Golang pour commencer et que les distributions expédient des noyaux corrigés. Il devrait déjà disparaître.

En conséquence, je pense que rien de plus que les conseils suggérés (wiki + panique) ne sont nécessaires.

@rtreffer Super, merci.

Le changement https://golang.org/cl/223417 mentionne ce problème : [release-branch.go1.14]runtime: don't crash on mlock failure

Juste pour clarifier, sur la base de ce que dit le wiki, si mlock échoue sur 1.14.1, cela signifie-t-il que le programme est vulnérable à la corruption de mémoire ?

Merci

@smasher164 Pas nécessairement. Nous n'imprimons plus le message "mlock fails" lorsque l'appel mlock échoue. Au lieu de cela, nous sauvegardons simplement le fait qu'il a échoué. si votre programme plante, nous affichons le fait que le mlock a échoué dans le texte d'erreur. Ce qui signifie "nous pensons que votre noyau est peut-être bogué. Nous avons essayé la solution de contournement mlock, et cela a échoué. Nous avons quand même exécuté votre programme et il a fini par planter." Peut-être que c'était dû au bogue du noyau, peut-être que c'est juste un bogue dans votre programme.

@ randall77 Merci d'avoir répondu. Alors, est-il sûr de dire que si mlock échoue et que le programme ne plante pas en touchant la pile avant d'envoyer un signal de préemption, cette corruption de mémoire liée à la préemption asynchrone n'existe pas dans le programme ?

Malheureusement non. Si mlock échoue et que vous avez un noyau bogué, une corruption de la mémoire peut se produire. Ce n'est pas parce que le programme ne plante pas qu'il n'y a pas eu de corruption quelque part. Le plantage est un effet secondaire de la corruption de la mémoire - seul l'échec du mlock ne provoquera pas de plantage. (Nous avions l'habitude de faire cela dans la 1.14. C'est l'une des choses que nous avons changées pour la 1.14.1.)
Même si vous désactivez la préemption asynchrone, une corruption de la mémoire peut toujours se produire. Juste à un taux inférieur, car votre programme reçoit probablement encore d'autres signaux (minuteries, etc.).

@smasher164 Faites-nous savoir si vous trouvez que la page wiki https://golang.org/wiki/LinuxKernelSignalVectorBug n'est pas claire. Merci.

Comment puis-je me retirer de cela ?

Je suis dans un conteneur lxc non privilégié créé par lxd , il a donc le même noyau que l'hôte mais ne peut pas définir de limites à l'échelle du système :

# 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

Selon https://github.com/golang/go/issues/37436#issuecomment -595836976, l'hôte (focal) contient le correctif du noyau.

Go a été construit il y a quelques jours en utilisant go get golang.org/dl/go1.14 et go1.14 download

$ go version
go version go1.14 linux/amd64

Encore une fois : comment puis-je me désinscrire ?

Je ne peux peut-être pas/ne veux pas changer les limites à l'échelle du système car d'autres programmes/pipelines sont affectés et un consensus collectif doit être fait pour toutes ces possibilités avant de changer les options à l'échelle du système.

C'est bien que nous ayons eu une telle détection, mais il est cassé que nous ne puissions pas nous retirer facilement. Quand on sait, les noyaux ont les patchs fixes, et la détection est sujette aux faux positifs.

@dionysius Cela a été corrigé dans 1.14.1 comme décrit dans https://github.com/golang/go/issues/37436#issuecomment -597360484. Je pense que vous devez passer à une version plus récente de go : https://godoc.org/golang.org/dl/go1.14.2.

Je vais essayer de charger explicitement la version go, je m'attendais go get golang.org/dl/go1.14 ce que

Edit, il semble que 1.14.3 soit le dernier 1.14 à ce jour

Mise à jour : semble bon avec go get golang.org/dl/go1.14.3 , inattendu que sans le correctif qui ne charge pas le dernier, bon à savoir (je n'aurais jamais atterri dans ce problème sinon)

Juste un avertissement - Go1.15 est sur le point d'être publié, et une version bêta a déjà été publiée, mais le correctif temporaire n'a pas encore été supprimé (il y a des commentaires à supprimer à Go1.15).

Je pense qu'il est important de supprimer la solution de contournement car Ubuntu 20.04 LTS utilise un noyau 5.4.0 corrigé. Cela signifie que tout utilisateur sur Ubuntu 20.04 bloquera toujours inutilement des pages, et s'il s'exécute dans un conteneur Docker, cet avertissement sera affiché pour chaque plantage, sans tenir compte du fait que son noyau n'est pas vraiment bogué. Ainsi, ces utilisateurs pourraient être envoyés dans une chasse à l'oie sauvage en essayant de comprendre et de lire toutes ces informations, et cela n'aura rien à voir avec leur bogue, probablement pour l'intégralité du cycle de vie d'Ubuntu 20.04.

@DanielShaulov merci. Pourriez-vous ouvrir un nouveau numéro pour cela ? Celui-ci concerne le problème en 1.14.

@networkimprov bien sûr : #40184

Le changement https://golang.org/cl/243658 mentionne ce problème : runtime: let GODEBUG=mlock=0 disable mlock calls

Le changement https://golang.org/cl/244059 mentionne ce problème : runtime: don't mlock on Ubuntu 5.4 systems

Quand go1.14.7 est-il publié avec cette modification ?

Le correctif est présent dans chaque version depuis la 1.14.1, qui a été publiée il y a des mois.

Le changement https://golang.org/cl/246200 mentionne ce problème : runtime: revert signal stack mlocking

Cette page vous a été utile?
0 / 5 - 0 notes