Go: tempo de execução: mlock da pilha de sinal falhou: 12

Criado em 25 fev. 2020  ·  128Comentários  ·  Fonte: golang/go

Qual versão do Go você está usando ( go version )?

 versão $ go
 go versão go1.14rc1 linux / amd64

Este problema se reproduz com a versão mais recente?

Eu acertei isso com a imagem golang:1.14-rc-alpine docker, o erro não acontece no 1.13.

Qual sistema operacional e arquitetura de processador você está usando ( go env )?

go env Saída

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

O que você fez?

Clone https://github.com/ethereum/go-ethereum , substitua a versão do builder em Dockerfile por golang:1.14-rc-alpine (ou use o Dockerfile abaixo) e , a partir da raiz, crie a imagem do 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

O que você esperava ver?

Go deve executar nossos scripts de construção com sucesso.

O que você viu em vez disso?

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

Comentários muito úteis

O bug do kernel se manifestou como corrupção de memória aleatória no Go 1.13 (com e sem agendamento preventivo). A novidade do Go 1.14 é que detectamos a presença do bug, tentamos contorná-lo e preferimos travar mais cedo e com muito barulho, se isso não for possível. Você pode ver os detalhes no problema ao qual indiquei.

Já que você me chamou de desonesto e desagradável, vou lembrá-lo novamente sobre o código de conduta: https://golang.org/conduct. Também cansei de participar desta conversa.

Todos 128 comentários

Essa é a consequência de tentar contornar um bug do kernel que afeta significativamente os programas Go. Consulte https://github.com/golang/go/issues/35777. A mensagem de erro sugere as duas únicas correções disponíveis conhecidas: aumentar o ulimit ou atualizar para um kernel mais recente.

A mensagem de erro sugere as duas únicas correções disponíveis conhecidas: aumentar o ulimit ou atualizar para um kernel mais recente.

Bem, estou executando a imagem oficial do docker alpine, cujo objetivo é ser capaz de construir um programa Go. Aparentemente, não pode. IMHO, a imagem upstream deve ser aquela corrigida para cumprir seu propósito, não nossa infra de compilação para contornar um bug na imagem upstream.

A imagem Alpine é mantida pela equipe Go? (Pergunta genuína. Não sei sobre isso.) De qualquer forma, sim, a imagem deve ser corrigida, de preferência com uma atualização do kernel.

Não tenho certeza de quem e como mantém as imagens do docker (https://hub.docker.com/_/golang), mas o repositório de hub do docker é uma "Imagem oficial", o que é muito difícil de obter, então presumo que alguém alto o suficiente na cadeia alimentar seja o responsável.

É "mantido pela Comunidade Docker". Os problemas devem ser arquivados em

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

EDITAR: o problema é o kernel do host, não a imagem da biblioteca Docker, então eles não podem consertá-lo.

Então, a solução oficial para travar é apontar o dedo para todos os outros para hackear seu código? Faz sentido.

@karalabe , gostaria de lembrá-lo de https://golang.org/conduct. Em particular, seja respeitoso e caridoso.

Por favor, responda a questão

É uma prática padrão redirecionar os problemas para o sistema de rastreamento de problemas correto.

Há uma extensa discussão sobre as possíveis soluções alternativas e correções para o problema relacionado anteriormente, se você quiser ver quais opções foram consideradas no lado Go.

Esse problema não ocorre com o Go 1.13. Portanto, é um bug introduzido no Go 1.14.

Dizer que você não pode consertar e dizer às pessoas para usarem soluções alternativas é desonesto, porque reverter um trecho de código na verdade consertaria. Uma solução alternativa seria detectar as plataformas / kernels problemáticos e fornecer um mecanismo de fallback embutido no Go.

Dizer às pessoas para usar um kernel diferente é especialmente desagradável, porque não é como se a maioria das pessoas pudesse sair por aí e construir um novo kernel. Se o alpine não lançar um novo kernel, não há muito o que a maioria dos desenvolvedores pode fazer. E, por último, se o seu projeto depende de uma infraestrutura estável onde você não pode simplesmente trocar os kernels, você está novamente em apuros.

É uma prática padrão redirecionar os problemas para o sistema de rastreamento de problemas correto.

O fato de Go travar não é culpa do docker. Redirecionar uma falha Go para um repositório docker é um desvio.

Você também pode desativar o agendamento preventivo em tempo de execução

$ GODEBUG=asyncpreemptoff=1 ./your_app

@ianlancetaylor , temos uma sugestão para fazer isso ao executar em um kernel afetado; isso é viável?

BTW, é um problema conhecido que os módulos da biblioteca Docker não obtêm atualizações oportunas, o que é um risco de segurança. Caveat emptor.

O bug do kernel se manifestou como corrupção de memória aleatória no Go 1.13 (com e sem agendamento preventivo). A novidade do Go 1.14 é que detectamos a presença do bug, tentamos contorná-lo e preferimos travar mais cedo e com muito barulho, se isso não for possível. Você pode ver os detalhes no problema ao qual indiquei.

Já que você me chamou de desonesto e desagradável, vou lembrá-lo novamente sobre o código de conduta: https://golang.org/conduct. Também cansei de participar desta conversa.

@karalabe , falei mal, o problema é o kernel do host, não a imagem do Docker. Você não consegue atualizá-lo?

Estou no Ubuntu mais recente e no kernel mais recente disponível. Aparentemente, todos os kernels do Ubuntu disponíveis são inadequados para Go 1.14 https://packages.ubuntu.com/search?keywords=linux-image-generic com base na mensagem de erro.

Você pode adicionar a saída de $ uname -a ao texto do problema principal? E talvez remover os rastros de pilha do goroutine?

Publiquei uma nota em golang-dev.

cc @aclements

Quando você diz que está no ubuntu e kernel mais recentes, o que exatamente você quer dizer (ou seja, saída de dpkg -l linux-image- *, lsb_release -a, uname -a, esse tipo de coisa) porque pelo que eu posso ver o correção está no kernel no bolso de atualizações para 19.10 (versão estável atual) e 20.04 (versão devel). Não está no kernel GA para 18.04, mas está no kernel HWE, mas otoh aqueles não são construídos com gcc 9 e, portanto, não devem ser afetados de qualquer maneira.

@networkimprov Desativar a preempção do sinal torna o bug menos provável de ocorrer, mas ainda está presente. É um bug em certas versões do kernel Linux. O bug afeta todos os programas em todos os idiomas. É particularmente provável que seja observável com programas Go que usam preempção de sinal, mas está presente para todos os outros programas também.

Go tenta contornar o bug bloqueando a pilha de sinal. Isso funciona bem, a menos que você alcance o limite do mlock. Suponho que uma desvantagem dessa solução alternativa é que tornamos o problema muito visível, em vez de falhar ocasionalmente devido à corrupção de memória aleatória, como aconteceria se não fizéssemos o mlock.

Em algum ponto, não há como contornar um bug do kernel.

@karalabe

Estou no Ubuntu mais recente e no kernel mais recente disponível

$ 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

que satisfaz os requisitos mínimos de versão.

Similarmente:

$ 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

Você pode esclarecer o que está vendo?

@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

Desculpe, meu comentário anterior foi enganoso. Porque, é claro, a versão do kernel retornada por uname -a no contêiner do Docker será a do host.

Portanto, por:

$ 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

você precisa atualizar o kernel do sistema operacional host.

FWIW, as etapas que você definiu acima usando Alpine para make geth funcionar para mim:

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

Sim, mas em meus posts anteriores eu destaquei que já estou no Ubuntu mais recente e instalei o kernel mais recente disponível do repositório de pacotes. Não vejo como poderia atualizar meu kernel para funcionar com Go 1.14 além de reconstruir todo o kernel a partir da fonte. Talvez eu esteja faltando alguma coisa?

Só para enfatizar, eu entendo qual é a solução alternativa e, se quiser que funcione, posso. Abri este relatório de problema porque esperava que outras pessoas tivessem o mesmo problema eventualmente. Se apenas atualizar meu sistema resolveria o problema, eu aceitaria de bom grado isso como uma solução, mas a menos que esteja faltando alguma coisa, o kernel corrigido não está disponível para usuários (recentes) do Ubuntu, então uma grande base de usuários pode ser afetada.

Sim, mas em meus posts anteriores eu destaquei que já estou no Ubuntu mais recente e instalei o kernel mais recente disponível do repositório de pacotes. Não vejo como poderia atualizar meu kernel para funcionar com Go 1.14 além de reconstruir todo o kernel a partir da fonte. Talvez eu esteja faltando alguma coisa?

Sim, acabei de reproduzir no focal também. A correção está presente no git para o kernel eoan do Ubuntu: https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426 e esse commit está na ancestralidade do 5.3. 0-40.32 então a correção deve estar no kernel que você está usando. Em outras palavras, acho que precisamos envolver a equipe do kernel - vou tentar fazer isso.

@karalabe - Acabei de perceber meu erro: pensei que estava usando o Ubuntu mais recente, estou na verdade usando eoan .

@mwhudson - apenas uma coisa a se notar (embora você provavelmente já saiba disso), uma olhada superficial no código responsável por essa mudança:

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

parece sugerir que o lado Go está verificando a versão do patch 15 ou superior. O que 5.3.0-40.32 relata como uma versão de patch? Estou adivinhando 0?

Reabrindo esta discussão até que resolvamos o problema aqui.

Um pequeno resumo, porque eu mesmo tive que montá-lo:

Portanto, parece que o kernel do Ubuntu foi corrigido, mas a solução alternativa é habilitada de qualquer maneira.

Portanto, parece que o kernel do Ubuntu foi corrigido, mas a solução alternativa é habilitada de qualquer maneira.

Ah, certo, sim, eu deveria realmente ler a falha, não deveria? Esta é a falha da solução alternativa em vez do bug original, em um caso em que a solução alternativa não é realmente necessária, mas não há uma boa maneira de Go saber disso. Posso corrigir o check-out do pacote Go 1.14 no Ubuntu, mas isso não ajuda os usuários a executar, por exemplo, o docker golang: 1.14-alpine image. Hrm.

Eu acho que a questão é, quantos usuários estão usando kernels "vulneráveis" neste momento. Não pode haver tantas distribuições que estão compilando um kernel sem patch com o gcc 9 agora.

Não pode haver tantos ...

Últimas palavras famosas? : D

Mas, falando sério, não acho que as pessoas atualizem os kernels com frequência, e muitos sistemas não conseguem. Talvez uma pergunta melhor a fazer seja quais sistemas / distros usam kernels vulneráveis ​​por padrão e apenas supor que haverá muitas pessoas presas a eles.

Posso corrigir o check-out do pacote Go 1.14 no Ubuntu, mas isso não ajuda os usuários a executar, por exemplo, o docker golang: 1.14-alpine image. Hrm.

Você também sentiria falta de usuários que criam a partir do código-fonte. Por exemplo, Ethereum faz compilações de código-fonte para nossos PPAs porque não há um pacote Go recente para todas as distros no Launchpad.

É comum para o Ubuntu (e outras distribuições?) Usar a seleção seletiva em vez de seguir os lançamentos de patch do kernel do Linux? Em https://packages.ubuntu.com/search?keywords=linux-image-generic todos os kernels têm uma versão de patch zero.

No que diz respeito a uma busca rápida no Google, o Ubuntu não libera novos kernels após o lançamento da distro, ao invés disso, apenas escolhe as correções de segurança (ou seja, sem problemas na versão do patch). A exceção a isso são as versões LTS (com suporte por 5 anos), que podem obter atualizações do kernel a cada poucos anos para oferecer suporte a novo hardware (mas isso também é extremamente raro). Não sei sobre outras distros.

Com meu conhecimento limitado agora, isso parece estranho. Os lançamentos de patches têm como objetivo distribuir patches de forma controlada para que as pessoas (e, neste caso, o tempo de execução Go) possam saber quais patches estão incluídos e quais não estão. No entanto, é assim que as coisas são, então temos que conviver com isso.

Perguntas abertas:

  • Quantas pessoas são afetadas por este problema?
  • ulimit uma solução alternativa viável?
  • O Ubuntu é a única distribuição que "desconsidera" os números de patch do kernel do Linux?
  • Dependendo das respostas às perguntas acima: Seria razoável adicionar uma detecção especial para o Ubuntu?

@neelance

É comum para o Ubuntu (e outras distribuições?) Usar a seleção seletiva em vez de seguir os lançamentos de patch do kernel do Linux?

Muitas distribuições fazem isso, não apenas o Ubuntu. O Debian faz isso, o Red Hat Enterprise Linux faz isso, e espero que o SUSE faça isso em suas distribuições corporativas também. A escolha seletiva é a única maneira de obter qualquer correção de bug se você não puder seguir agressivamente as versões estáveis ​​do upstream (e alternar as versões estáveis ​​quando o suporte do upstream for encerrado). O Fedora é uma exceção; depois de um tempo, ele muda para o kernel upstream de versão estável mais recente.

Há também a questão dos kernels proprietários usados ​​pelos motores de contêiner. Não podemos nem olhar os fontes para eles, e alguns deles mentiram sobre os números de versão do kernel no passado. Imagino que eles também usem a seleção seletiva.

Geralmente, as verificações de versão para recursos do kernel (ou bugs) são uma ideia realmente ruim. É pior para Go devido à vinculação estática, portanto, é impossível trocar o tempo de execução sob um aplicativo para corrigir suas expectativas de kernel.

É comum para o Ubuntu (e outras distribuições?) Usar a seleção seletiva em vez de seguir os lançamentos de patch do kernel do Linux? Em https://packages.ubuntu.com/search?keywords=linux-image-generic todos os kernels têm uma versão de patch zero.

A string da versão base do kernel não muda, isso é verdade. Mas isso não significa que as versões estáveis ​​do upstream não sejam mescladas, o número ABI é aumentado quando há mudanças no código.

Note que você escolheu o meta-pacote que não mostra o changelog apropriado, aqui você pode ver que a última versão 5.4.0-14.17 juntou a versão estável 5.4.18:
http://changelogs.ubuntu.com/changelogs/pool/main/l/linux-5.4/linux-5.4_5.4.0-14.17/changelog

Parece que a detecção automática adequada em todas as distribuições é quase impossível. Vejo três opções:

  • Fazer nada.
  • Faça a opção de solução alternativa.
  • Faça o opt-out da solução alternativa.

Ou desative a preempção assíncrona por padrão em 5.3.x e 5.4.x, e deixe os usuários ativá-la no tempo de execução.

https://github.com/golang/go/issues/37436#issuecomment -591237929 afirma que desativar a preempção assíncrona não é uma solução adequada, não é?

Não estritamente falando, mas não houve relatos do problema antes que a preempção assíncrona pousasse.

Na verdade, o tempo de execução poderia bifurcar um processo filho na inicialização (para 5.3.x e 5.4.x) que aciona o bug e habilitar a solução alternativa se isso acontecer? IIRC há um reprodutor confiável, consulte https://github.com/golang/go/issues/35326#issuecomment -558690446

Desativar a preempção assíncrona é uma distração. Os programas em execução em kernels defeituosos ainda estão danificados. O problema é que a falha aparece como uma estranha corrupção de memória, em vez de um erro sobre a execução de um limite de mlock que aponta para versões do kernel. Embora, obviamente, queiramos corrigir o problema inteiramente, acho que, dada a escolha de um erro claro ou corrupção de memória aleatória, devemos sempre escolher o erro claro.

Eu concordo que a detecção da versão do kernel é terrível, só que não conhecemos nenhuma outra opção. Se alguém tiver sugestões a esse respeito, será muito útil.

Uma coisa que poderíamos fazer é adicionar uma configuração GODEBUG para desabilitar mlock ing a pilha de sinais. Isso daria às pessoas uma solução alternativa que se concentrasse no problema real. Podemos mencionar essa configuração na mensagem de erro. Temo que isso levará as pessoas a ativar a configuração, quer tenham um kernel com patch ou não. Mas pelo menos dará às pessoas que realmente têm um kernel corrigido uma maneira de contornar esse problema. CC @aclements

Na verdade, o tempo de execução poderia bifurcar um processo filho na inicialização (para 5.3.x e 5.4.x) que aciona o bug e habilitar a solução alternativa se isso acontecer? IIRC há um reprodutor confiável, consulte # 35326 (comentário)

É uma ideia interessante, mas acho que, neste caso, o teste é muito caro para ser executado na inicialização de cada programa Go.

Posso ter perdido algo (este tópico ficou longo rápido!), Mas qual é a desvantagem ou dificuldade de apenas aumentar o limite do mlock? Há pouca razão para não apenas definir como ilimitado, mas mesmo se você não quiser fazer isso, você só precisa de 4 KiB por thread, então meros 64 MiB é literalmente mais do que o tempo de execução de um único processo é capaz de mlocking . AFAIK, a maioria das distros deixa-o ilimitado por padrão. A única exceção notável que conheço é o Docker, que o define para (eu acho) 64 KiB por padrão, mas isso pode ser gerado passando --ulimit memlock=67108864 para o Docker.

Parece que já temos uma solução alternativa bastante simples. Existe algo impedindo as pessoas de fazerem isso?

O ponto do problema é que você não deveria ter que aplicar uma solução alternativa manual, se possível. Parece uma regressão em 1.14.

Não pode ser corrigido no lado da biblioteca Docker: https://github.com/docker-library/golang/issues/320

O problema com a solução alternativa ulimit é que ela é uma bomba-relógio. Atualmente, nenhum processo automatizado precisa aumentar o ulimit com Go 1.13. Qualquer pessoa para quem o Go 1.14 falhar imediatamente após a atualização notará e corrigirá o problema.

O cenário mais interessante é o que acontece se alguém usa uma versão antiga de um kernel que não é afetado e, em algum ponto, muda o kernel para um novo que é. De repente, as coisas começarão a falhar, mas como Go é a camada de aplicativo e o kernel é a camada de sistema operacional, levará tempo para fazer a conexão e descobrir uma correção. A questão é qual será o custo.


O que não está imediatamente claro é se apenas o compilador Go ou todos os aplicativos desenvolvidos por Go também têm problemas? Se apenas o compilador for um caso de sorte e as consequências podem ser contidas. No entanto, se todos os aplicativos Go construídos com 1.14 têm uma tendência a entrar em pânico, isso pode realmente prejudicar a portabilidade binária pré-construída, porque de repente meu código pode deixar de funcionar em um sistema diferente (na verdade, é completamente válido, apenas usa um kernel diferente esquema de controle de versão).

O que não está imediatamente claro é se apenas o compilador Go ou todos os aplicativos desenvolvidos por Go também têm problemas?

O bug do kernel do Linux nem mesmo é específico do Go. Se bem entendi, isso afeta qualquer programa - mesmo um escrito em C! - que usa os registros XMM ou YMM e pode receber sinais. Programas Go abaixo de 1.14 são _mais_ severamente afetados do que muitos outros programas porque eles usam sinais internamente para preempção de goroutina, e é por isso que o tempo de execução Go 1.14 inclui a solução alternativa mlock .

O bug do kernel do Linux nem mesmo é específico do Go.

Sim, mas meu kernel foi corrigido, mas Go ainda entra em pânico com ele :)

@aclements

A única exceção notável que conheço é o Docker, que o define para (eu acho) 64 KiB por padrão, mas isso pode ser gerado passando --ulimit memlock = 67108864 para o Docker.

Parece que já temos uma solução alternativa bastante simples. Existe algo impedindo as pessoas de fazerem isso?

Infelizmente sim. Em nosso caso aqui, não podemos dizer aos nossos clientes para reconfigurar seus contêineres docker. São muitos e são sensíveis às mudanças de ambiente em suas configurações; na verdade, é por isso que escolhemos entregar o aplicativo em um contêiner docker, para que a fachada de uma ferramenta de isolamento aliviasse a preocupação com a alteração das opções de configuração. Alterar o conteúdo do contêiner é bom, mas a forma como invocamos o contêiner pode não ser tão bom.

@aclements

Há pouca razão para não apenas definir como ilimitado, mas mesmo se você não quiser fazer isso, você só precisa de 4 KiB por thread, então meros 64 MiB é literalmente mais do que o tempo de execução de um único processo é capaz de mlocking . AFAIK, a maioria das distros deixa-o ilimitado por padrão.

Não parece ser o caso - este é um Ubuntu LTS com apenas 6 KiB de espaço de bloqueio:

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

Qual é o conteúdo de /proc/version para um kernel de exemplo que foi corrigido para funcionar corretamente, mas para o qual Go está atualmente produzindo o erro mlock ? Obrigado.

@ucirello Você está vendo algum problema ao executar programas Go no sistema que descreveu?

$ 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

Talvez possamos usar a data registrada em /proc/version como um sinal adicional. Provavelmente deve ser específico de liberação, o que é uma dor. Mas a coisa toda é dolorosa.

A discussão parece ser sobre a aceitação de mais falsos positivos ou falsos negativos. Aqui está um resumo:

Falso positivo: a solução alternativa é habilitada em um kernel com patch.

  • Reproduzível. As instruções podem ser mostradas.
  • Parece uma regressão.
  • Difícil de consertar em certos ambientes.
  • O binário Go pode ser executado em alguns ambientes, mas falha em outros.

Falso negativo: a solução alternativa não está habilitada em um kernel sem patch.

  • A falha raramente acontece, especialmente se a preempção assíncrona estiver desabilitada.
  • Possíveis consequências graves devido à corrupção da memória.
  • Difícil de depurar.

@ianlancetaylor Não vejo nenhum problema no sistema descrito, mas pelo que percebi, isso pode ser uma coincidência, ou seja, a mensagem de erro diz que devo atualizar após 5.3.15, 5.4.2 ou 5.5 e este é um 4.4.x caixa.

Em qualquer caso, o que eu queria destacar é que a suposição de que a maioria das distribuições entrega ulimit -l com 64 ulimit -l é 64 K - e é uma instalação padrão.

Existe uma maneira de detectar a configuração ulimit e evitar o travamento se a solução alternativa não puder ser aplicada? Assim poderíamos ainda melhorar a situação, mas não causar novos travamentos? Em vez disso, poderíamos imprimir um aviso sobre a possível corrupção da memória.

Existe uma maneira de detectar a configuração de ulimit e evitar o travamento se a solução alternativa não puder ser aplicada?

Na verdade, o código atual já está detectando a configuração de ulimit. Ele não o consulta diretamente, mas detecta quando é executado nele, e é por isso que está imprimindo aquela mensagem de erro específica pedindo ao usuário para aumentar o ulimit. Se algo mais der errado no mlocking, ele irá imprimir uma mensagem diferente. A falha é intencional porque consideramos isso preferível à corrupção de memória aleatória.

Em qualquer caso, o que eu queria destacar é que a suposição de que a maioria das distros entrega ulimit -l com 64M parece estar incorreta. Esta é uma caixa Linux cujo ulimit -l é 64K - e é uma instalação padrão.

@ucirello , obrigado por esse dado. Não tenho uma caixa do Ubuntu à mão, mas fiz a depuração original em uma instalação do Ubuntu e poderia jurar que não tinha um conjunto de ulimit. Isso definitivamente não está em um contêiner?

@ianlancetaylor Eu criei um script rápido e sujo para verificar os relatórios do uname: https://gist.github.com/Tasssadar/7424860a2764e3ef42c7dcce7ecfd341

Aqui está o resultado do teste Debian atualizado (bem, -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)

Uma vez que Go está usando apenas a string de lançamento, a verificação da versão do patch basicamente não funciona em nenhum lugar a não ser em kernels vanilla - tanto o Debian quanto o RHEL / CentOS (que felizmente tem um kernel muito antigo) fazem isso dessa forma, eles mantêm o .0 e especificam o versão de patch real posterior. Infelizmente, eles não usam o mesmo formato para version .

EDITAR: e para torná-lo ainda mais estranho, o Ubuntu não coloca o número do patch no uname de forma alguma, embora eles provavelmente tenham todas as correções incorporadas. Talvez o melhor curso de ação seja tornar isso um aviso em vez de um acidente? Neste ponto, a maioria dos kernels provavelmente já está atualizada de qualquer maneira.

A falha é intencional porque consideramos isso preferível à corrupção de memória aleatória.

@aclements Há uma pequena falha neste argumento: o problema não são os verdadeiros positivos que são executados no ulimit, mas os falsos positivos que não teriam memória corrompida sem a solução alternativa.

@aclements

Isso definitivamente não está em um contêiner?

Tenho 100% de certeza de que não é um contêiner. No entanto, é uma VM.

$ sudo virt-what
xen
xen-hvm

@ucirello , obrigado por esse dado. Não tenho uma caixa do Ubuntu à mão, mas fiz a depuração original em uma instalação do Ubuntu e poderia jurar que não tinha um conjunto de ulimit. Isso definitivamente não está em um contêiner?

Como um ponto de dados diferente, meu sistema principal Ubuntu Eoan tem 64 MB definido por padrão para ulimit -l .

@ucirello Não há problema nos kernels Linux 4.4.x. O bug apareceu pela primeira vez na versão 5.2 do kernel.

@ianlancetaylor - obrigado pela informação. Acho que analisei mal a mensagem de erro então. Eu não sabia desse fato, e a mensagem de erro fez acreditar que os novos binários seriam incompatíveis com os kernels mais antigos.

Os programas Go reportarão apenas a mensagem de erro nos kernels com um número de versão que indica que eles podem ter o problema.

Aqui está algo que podemos fazer:

  1. Use uname para verificar a versão do kernel para um kernel vulnerável, como fazemos hoje.
  2. Se o kernel é vulnerável de acordo com a versão, leia /proc/version .
  3. Se /proc/version contém a string "2020" , assuma que o kernel foi corrigido.
  4. Se /proc/version contém a string "gcc version 8" assume que o kernel funciona mesmo se corrigido (já que o bug só ocorre quando o kernel é compilado com GCC 9 ou posterior).
  5. Caso contrário, chame mlock em stacks de sinal como fazemos hoje em kernels vulneráveis.

O objetivo disso é reduzir o número de vezes que os programas Go ficam sem mlock espaço.

Alguém sabe de algum kernel não corrigido que possa ter a string "2020" em /proc/version ?

Por segurança, provavelmente devemos tentar identificar os momentos em que o kernel foi corrigido para as principais distros. Existe alguém que pode identificar isso para alguma distro em particular? Obrigado.

Não tenho certeza se isso é útil, mas aparentemente o Ubuntu disponibiliza a versão padrão do kernel para aqueles que procuram:

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

@jrockway Obrigado, o problema não é que não tenhamos a versão do kernel, é que o Ubuntu está usando uma versão do kernel com o bug, mas o Ubuntu aplicou um patch para o bug, então o kernel realmente funciona, mas nós não não sei como detectar esse fato.

Adicionando à heurística de correspondência de string de @ianlancetaylor , você também pode verificar /proc/version para
5.3.x && x >= 15 || 5.4.x && x >= 2 (código não real, mas essa é a ideia)

Obrigado, já verificamos isso nos uname resultados.

Estou me referindo a 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

EDITAR: No Ubuntu, você pode verificar /proc/version_signature como sugeri.

Ah, desculpe, perdi isso. Portanto, de acordo com esse comentário em alguns sistemas, o campo release e o campo version retornados por uname diferem na versão do kernel que relatam. Atualmente verificamos o campo release , mas não o campo version .

O arquivo /proc/version tem um conjunto diferente de informações novamente.

Meu sistema Ubuntu tem /proc/version mas não tem /proc/version_signature .

@bbarenblat coletou utilmente uname e / proc / version de várias versões de várias distros para ver como elas se parecem (adicionei algumas das minhas):

Debian instável:

$ 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 (atualmente estável):

$ 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 (muito antigo):

$ 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 “em algum lugar além do estável, com o pacote instável ocasional”:

$ 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 com kernel GCP (na verdade é 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 com kernel construído à mão:

$ 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 Estou um pouco confuso sobre quais kernels você postou precisam de uma solução alternativa e quais já foram corrigidos. Talvez você possa anotar sua postagem.

O problema é que se você estiver dentro das faixas de versões potencialmente ruins, você _pode ser afetado_; se estiver fora da faixa de versões, você está seguro.
Quando a corrupção inicial foi encontrada, imediatamente retiramos o patch, testamos e implementamos. Portanto, nosso kernel estava 100% dentro da faixa ruim, mas corrigido. Estar fora do intervalo significa que você está seguro, mas dentro do intervalo você não pode provar que o bug existe sem testá-lo. (Em outras palavras: falsos positivos acontecerão por design)

Estou agora construindo outro kernel ubuntu corrigido com números de versão upstream

  • make kernelversion
  • patch debian{,.master}/{changelog,control} com esta versão

O software que depende da versão do kernel é a razão pela qual as distribuições gostam de mantê-lo estável: se uma atualização do número da versão pode quebrar o software que funcionava antes, é melhor não alterar a versão (mas ainda aplicar os patches!).

Não estou feliz em lançar um novo kernel apenas por causa do golang 1.14, mas acho que funcionará.
Eu desencorajaria todos a enviar binários do Golang 1.14 para fora de ambientes controlados.

A postagem de @rtreffer novamente soa como falsos positivos são inevitáveis. Atualmente, ainda voto a favor da solução de limitar o impacto negativo de um falso positivo.

Acho que deve ser opt-in para evitar todos os falsos positivos. Por exemplo, adicione GOWORKAROUNDS env var booleano ou com uma lista de soluções alternativas ou para permitir que a heurística tente encontrá-las.

Esta seria a solução menos intrusiva da IMO.

@ fcuello-fudo o problema é que se a solução alternativa não estiver habilitada em um kernel defeituoso, os sintomas são muito obscuros.

Que tal reutilizar o conceito "contaminado" do kernel do Linux? O tempo de execução Go continuaria detectando kernels ruins e aplicaria a solução alternativa do mlock, mas se marca contaminado se o mlock falhar (e não travar). Em seguida, certifique-se de adicionar uma nota a qualquer pânico e enviar mensagens se o sinalizador de contaminação estiver definido.

A vantagem é que travamentos falsos positivos são evitados, ao mesmo tempo em que fornece uma indicação clara no caso de um kernel ruim causar um travamento.

A desvantagem é que um kernel ruim pode corromper a memória silenciosamente, não causando um travamento visível.

@ fcuello-fudo o problema é que se a solução alternativa não estiver habilitada em um kernel defeituoso, os sintomas são muito obscuros.

E deixá-lo habilitado por padrão, mas ainda seria útil desabilitar soluções alternativas se o usuário quiser?

@howardjohn (desenvolvedor principal do Istio do Google) em https://github.com/istio/istio/issues/21672 :

Meu kernel em minha máquina pessoal não foi corrigido. Mas realmente não importa o que minha máquina tem, nós enviamos o Istio para milhares de usuários. Não consigo controlar em qual máquina eles o executam e adoraria não ter que restringir isso a algum subconjunto de versões do kernel

O projeto Prometheus também está retendo sua atualização go1.14: https://github.com/prometheus/golang-builder/pull/85#issuecomment -592082645

Eu encontrei esse problema ao executar aplicativos go em um cluster Kubernetes auto-hospedado. Consegui permitir que a solução alternativa _mlock_ tivesse efeito aumentando o ulimit relevante. No entanto, como o processo de alteração de ulimits para contêineres do Docker em execução no Kubernetes não é exatamente fácil de encontrar, pode ajudar outra pessoa a colocar os detalhes aqui.

  1. Atualize /etc/security/limits.conf para incluir algo como
* - memlock unlimited
  1. Atualize /etc/docker/daemon.json para incluir
"default-ulimits": { "memlock": { "name": "memlock", "hard": -1, "soft": -1 } }
  1. Reinicie o docker / kubernetes, traga seus pods de volta.

  2. Insira um contêiner em execução e verifique se o ulimit foi aumentado:

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

Você pode conseguir usar algo mais sutil do que o martelo _unlimited_; para mim, um limite de 128 KiB (131072) parecia funcionar.

também enfrentando esse problema ao tentar construir uma imagem docker para https://github.com/RTradeLtd/Temporal on go 1.14

E a pilha continua se acumulando. Veja, é por isso que fiquei com raiva no início deste tópico (o que foi um grande erro da minha parte, concordo). Embora eu tenha feito muito esforço para explicar e fornecer uma representação de que este é um bloqueador, fui encerrado para não interferir no lançamento. Mesmo depois de ficar claro que não é um problema do docker.

Agora estamos em uma situação muito pior, já que vários projetos estão na lista negra do Go 1.14. Este bug está programado para ser corrigido apenas no Go 1.15. Com base nos problemas vinculados acima, temos certeza de que é uma boa ideia adiar isso por 8 meses? Acho que seria bom reconhecer a bagunça e tentar consertá-la em um lançamento de patch, sem esperar que mais projetos sejam prejudicados.

Sim, estou ciente de que estou apenas importunando as pessoas aqui, em vez de consertar sozinho. Lamento não poder contribuir de forma mais significativa, só não quero fragmentar o ecossistema. Os módulos Go já foram um golpe para muitos projetos, não vamos dobrar para outra peculiaridade que as ferramentas precisam conhecer.

Ian Lance-Taylor disse que a correção será retransmitida assim que houver uma: https://groups.google.com/d/msg/golang-dev/_FbRwBmfHOg/mmtMSjO1AQAJ

@lmb Oh, é muito bom ouvir, obrigado pelo link.

Para sua informação: o Ubuntu 20.04 provavelmente enviará 5.4.0, ou seja, a partir do final do próximo mês,
O Ubuntu LTS e o golang mais recentes não funcionam juntos fora da caixa.

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

Ian Lance-Taylor disse que a correção será retransmitida assim que houver
um: https://groups.google.com/d/msg/golang-dev/_FbRwBmfHOg/mmtMSjO1AQAJ

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/golang/go/issues/37436?email_source=notifications&email_token=AAAO66R6S5VOWQBKETLI5UDRGDORJA5CNFSM4K3GBRRKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEOBCIDI#issuecomment-595731469 ,
ou cancelar
https://github.com/notifications/unsubscribe-auth/AAAO66SH6J4M75WDJ5GO7HTRGDORJANCNFSM4K3GBRRA
.

@karalabe em termos tão fortes quanto posso reunir, você não foi "desligado para não interferir no lançamento". Josh, que originalmente fechou o problema, não está na equipe Go no Google (ou seja, não é um tomador de decisões) nem eu. Inicialmente, presumimos que o projeto Docker poderia (e deveria) mitigar o problema em sua compilação. Quando ficou claro que eles não podiam, imediatamente levantei isso no golang-dev.

Além disso, fui o primeiro a notar que o problema decorre do kernel do host, não do módulo Docker. Você não mencionou que está no Ubuntu até que eu indiquei isso.

Acho que você nos deve mais um pedido de desculpas, depois dessa nota.

EDIT: Também pedi que você remova os rastreamentos de pilha de goroutine (começando goroutine x [runnable] ) de seu relatório, pois eles tornam a página difícil de ler / navegar. [Atualização: Russ editou as pilhas.]

Todos, por favor, tenham em mente que uma comunicação bem-sucedida é difícil e uma habilidade que você precisa praticar. As emoções podem trabalhar contra nós e atrapalhar nosso objetivo de uma comunicação bem-sucedida, eu mesmo estive lá. Sim, houve uma violação do código de conduta e apontar isso é bom. Um pedido de desculpas voluntário também é útil. Agora, vamos tentar garantir que cada postagem tenha um impacto líquido positivo na colaboração e na solução desse problema.

@rtreffer Você sabe se a versão do kernel planejada para o Ubuntu 20.04 incluirá o patch? Parece que foi escolhido no kernel 5.3.0 do Ubuntu (https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426). Eu imagino que também esteja em seu kernel 5.4.0, mas seria bom ter certeza. Obrigado.

@rtreffer Você sabe se a versão do kernel planejada para o Ubuntu 20.04 incluirá o patch? Parece que foi escolhido no kernel 5.3.0 do Ubuntu (https://kernel.ubuntu.com/git/ubuntu/ubuntu-eoan.git/commit/?id=59e7e6398a9d6d91cd01bc364f9491dc1bf2a426). Eu imagino que também esteja em seu kernel 5.4.0, mas seria bom ter certeza. Obrigado.

Ele será incluído no kernel de lançamento para 20.04, sim:

(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

Com base na discussão com @aclements , @ dr2chase , @ randall77 e outros, nosso plano para a versão 1.14.1 é:

  • escreva uma página wiki descrevendo o problema
  • continue a usar mlock em uma versão do kernel que pode estar com bugs
  • se mlock falhar, silenciosamente observe esse fato e continue executando
  • se virmos SIGSEGV inesperados ou SIGBUS e mlock falharam, então, na pilha de falhas, o rastreamento de pessoas apontará para a página wiki

A esperança é que forneça uma boa combinação de execução correta no caso normal, enquanto direciona as pessoas em kernels potencialmente problemáticos a informações para ajudá-los a decidir se o problema é seu kernel ou seu programa ou um bug no próprio Go.

Isso também pode ser combinado com melhores tentativas de identificar se um kernel específico foi corrigido, com base no campo uname version (atualmente verificamos apenas o campo release ).

Outra coisa que discutimos: se um mlock falhar e estivermos prestes a enviar um sinal para nós mesmos, toque na pilha de sinais primeiro.

@ianlancetaylor parece uma ótima ideia!

Se você ainda não considerou: link para essa página wiki da mensagem de erro (potencialmente com mais uma indireção)

Por exemplo, o iPXE faz isso por meio de links como http://ipxe.org/1d0c6539 (sua saída é otimizada para ROMs de inicialização, espaço limitado e similares)

  • escreva uma página wiki descrevendo o problema

  • continue a usar mlock em uma versão do kernel que pode estar com bugs

  • se mlock falhar, silenciosamente observe esse fato e continue executando

Não seria melhor também desativar a preempção assíncrona? É verdade que o problema pode acontecer mesmo com asyncpreemptoff = 1, mas o bug era raro o suficiente para que ninguém percebesse por meses.

Existe uma maneira de testar (fora do tempo de execução Go) para ver se o kernel foi corrigido? Nós mantemos nossos próprios kernels fora da distro, o que faz com que isso funcione bem.

@gopherbot ,

Problemas do backport abertos: # 37807 (para 1.14).

Lembre-se de criar os CL (s) selecionados assim que o patch for enviado ao mestre, de acordo com https://golang.org/wiki/MinorReleases.

@nemith, há um reprodutor C aqui: https://github.com/golang/go/issues/35326#issuecomment -558690446

@ randall77 @aarzilli Após consideração, na verdade não acho uma grande ideia adicionar atenuações parciais adicionais, como tocar na página da pilha de sinais ou desativar a preempção assíncrona. É um bug no nível do kernel que pode afetar qualquer programa que receba um sinal. Pessoas rodando com um kernel cheio de bugs devem atualizar o kernel de uma forma ou de outra. Usar mlock é uma mitigação confiável que sempre deve funcionar e, como tal, é razoável tentar. Tocar na pilha de sinais antes de enviar um sinal de preempção ou desabilitar totalmente a preempção de sinal não é uma mitigação confiável. Acho que não devemos recorrer a uma mitigação não confiável; devemos dizer ao usuário para atualizar o kernel.

Pessoas que não podem atualizar o kernel têm a opção de executar GODEBUG=asyncpreemptoff=1 , que será uma mitigação parcial tão eficaz quanto os outros dois.

Acho que não devemos recorrer a uma mitigação não confiável; devemos dizer ao usuário para atualizar o kernel.

Eu concordaria com você se o bug sempre se manifestasse em uma falha. Mas isso não acontece - apenas corrompe a memória aleatoriamente. Talvez ele trave, mas talvez apenas corrompa aleatoriamente os dados do programa e continue em execução. Acho que é nossa responsabilidade tomar todas as medidas que pudermos para evitar a corrupção de dados, mesmo que isso signifique que não enviaremos mensagens sobre a atualização necessária do kernel com frequência.

É uma decisão do agora contra o mais tarde. Podemos tentar evitar a corrupção de dados agora tocando nas páginas de sinal antes de usar. Ou podemos tentar evitar a corrupção posteriormente enviando uma mensagem ao usuário no caso de um travamento. Não podemos escolher os dois.

Eu gostaria que houvesse uma maneira de enviar uma mensagem quando detectássemos uma falha de mlock, sem travar, e então mitigar tocando nas páginas. Não acho que o programa Go possa fazer isso em stdout / stderr, infelizmente. Talvez pudéssemos lançar algo no syslog? Isso ajudaria apenas aqueles que olhassem para o syslog, o que provavelmente não é muitos.

Ponto justo.

Que tal postar uma mensagem sobre a instalação? Isso envolve uma mudança nos procedimentos de instalação para seus lançamentos binários e as distros, mas permitiria que você rodasse o reprodutor para testar definitivamente o bug.

@networkimprov Essa é uma boa ideia, mas como as pessoas enviam programas compilados através de máquinas que podem ter versões de kernel diferentes, acho que também precisamos da abordagem descrita acima.

Alterar https://golang.org/cl/223121 menciona este problema: runtime: don't crash on mlock failure

Enviei https://golang.org/cl/223121. Seria útil se as pessoas que estão tendo problemas com o 1.14 pudessem ver se essa mudança corrige o problema. Obrigado.,

Bem, @ randall77 / @ianlancetaylor , tendo a discordar que este é um problema de golang. Golang descobriu o problema de corrupção de memória, mas é um bug de kernel muito grave.

Como tal, deve escalar através dos caminhos do kernel.
As distribuições pegaram o patch e o enviaram. Foi backported. Cada nova instalação obterá um kernel não afetado.
Se você lançar seu próprio kernel, terá que fazer esse trabalho sozinho. Como sempre.

Seja útil para os usuários que o acessam e seja o mais útil possível.
Mas eu não acho que seja uma responsabilidade golang consertar um bug do kernel ou mesmo forçar os usuários a aplicar o patch.

@rtreffer É isso que estamos tentando fazer: ser o mais útil possível.

Em kernels com erros, os programas Go desenvolvidos com Go 1.14 se comportavam de maneira imprevisível e ruim. Não queremos fazer isso mesmo em um kernel com bugs. Se um programa simplesmente falhasse de forma rápida e limpa, isso seria uma coisa. Mas o que vimos foi a corrupção da memória levando a erros obscuros. Veja # 35326, entre outros.

Você acha que devemos realizar alguma ação diferente da que estamos fazendo agora?

@rtreffer Bem sorta. Temos alguns kernels de produção 5.2 que não são afetados porque não são compilados com gcc9 e também poderíamos facilmente corrigir a correção em nossa linha de kernel sem afetar nada mais e ficar bem. Bug do kernel não existe em nosso ambiente e atualizar versões principais requer muito mais testes e implantação cuidadosa em toda a frota, então apenas "atualizar seu kernel" não é uma boa situação.

Por outro lado, a solução alternativa baseada nos números de versão do kernel nos fez mover para mlocks, que falhou devido a problemas de ulimit. Isso não é um bug do kernel.

Dito isso, não tenho certeza se há uma solução melhor aqui e a equipe Go provavelmente fez a escolha certa.

@ianlancetaylor talvez você possa enviar o reprodutor C nas versões de código-fonte e binário, e referenciá-lo na página wiki como uma forma de examinar qualquer kernel.

Que tal postar uma mensagem sobre a instalação? Isso envolve uma mudança nos procedimentos de instalação para seus lançamentos binários e as distros, mas permitiria que você rodasse o reprodutor para testar definitivamente o bug.

Qualquer distro que estiver prestando atenção suficiente para fazer isso irá apenas corrigir seu kernel, com certeza.

@ianlancetaylor Eu concordo totalmente com o caminho a seguir, o patch e a página wiki estão ótimos.

Eu queria enfatizar que a corrupção não é falha ou bug do Golang para começar e as distribuições estão enviando kernels fixos. Já deveria estar desaparecendo.

Como resultado, acho que nada além das dicas sugeridas (wiki + pânico) são necessárias.

@rtreffer Ótimo, obrigado.

Alterar https://golang.org/cl/223417 menciona este problema: [release-branch.go1.14]runtime: don't crash on mlock failure

Apenas para esclarecer, com base no que diz o wiki, se mlock falhar em 1.14.1, isso significa que o programa é vulnerável a corrupção de memória?

Obrigado

@ smasher164 Não necessariamente. Não imprimimos mais a mensagem "mlock falha" quando a chamada mlock falha. Em vez disso, apenas salvamos o fato de que ele falhou. se o seu programa travar, imprimimos o fato de que o mlock falhou no texto do erro. O que significa "achamos que seu kernel pode estar com bugs. Tentamos a solução alternativa mlock e ela falhou. Executamos seu programa mesmo assim e ele acabou travando". Talvez seja devido ao bug do kernel, talvez seja apenas um bug no seu programa.

@ randall77 Obrigado por responder. Portanto, é seguro dizer que se o mlock falhar e o programa não travar ao tocar na pilha antes de enviar um sinal de preempção, a corrupção de memória relacionada à preempção assíncrona não existe no programa?

Infelizmente não. Se o mlock falhar e você tiver um kernel cheio de erros, pode estar ocorrendo corrupção de memória. Só porque o programa não está travando, não significa que não houve corrupção em algum lugar. O travamento é um efeito colateral da corrupção da memória - apenas a falha do mlock não causará um travamento. (Costumávamos fazer isso no 1.14. Essa é uma das coisas que mudamos no 1.14.1.)
Mesmo se você desativar a preempção assíncrona, a corrupção de memória ainda pode estar ocorrendo. Apenas a uma taxa mais baixa, pois seu programa provavelmente ainda está recebendo outros sinais (temporizadores, etc.).

@ smasher164 Informe-nos se você achar que a página wiki https://golang.org/wiki/LinuxKernelSignalVectorBug não está clara. Obrigado.

Como posso cancelar isso?

Estou em um contêiner lxc sem privilégios criado pelo lxd , portanto, ele tem o mesmo kernel do host, mas não é capaz de definir limites para todo o sistema:

# 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

De acordo com https://github.com/golang/go/issues/37436#issuecomment -595836976 o host (focal) contém o patch do kernel.

Go foi construído há alguns dias usando go get golang.org/dl/go1.14 e go1.14 download

$ go version
go version go1.14 linux/amd64

Novamente: Como posso cancelar isso?

Talvez eu não possa / não queira mudar os limites de todo o sistema, pois outros programas / pipelines são afetados e um consenso coletivo deve ser feito para todas essas possibilidades antes de mudar as opções de todo o sistema.

É bom que nós tivemos tal detecção, mas é quebrado que não podemos optar por sair facilmente. Quando sabemos, os kernels têm os patches fixos e a detecção está sujeita a falsos positivos.

@dionysius Isso foi corrigido em 1.14.1 conforme descrito em https://github.com/golang/go/issues/37436#issuecomment -597360484. Acredito que você precise atualizar para uma versão mais recente de go: https://godoc.org/golang.org/dl/go1.14.2.

Vou tentar carregar explicitamente a versão go, esperava que go get golang.org/dl/go1.14 carregasse a versão 1.14 mais recente. Apresentarei um relatório.

Editar, parece que 1.14.3 é o mais recente 1.14 a partir de hoje

Atualização: parece bom com go get golang.org/dl/go1.14.3 , inesperado que sem o patch que não está carregando o mais recente, é bom saber (eu nunca teria me envolvido neste problema de outra forma)

Apenas um aviso - Go1.15 está prestes a ser lançado, e um beta já foi lançado, mas o patch temporário ainda não foi removido (há comentários de tarefas a serem removidos em Go1.15).

Acho que é importante remover a solução alternativa, pois o Ubuntu 20.04 LTS usa um kernel 5.4.0 corrigido. Isso significa que qualquer usuário no Ubuntu 20.04 ainda usará páginas mlock desnecessariamente e, se ele for executado em um contêiner docker, esse aviso será exibido a cada travamento, desconsiderando o fato de que seu kernel não tem muitos bugs. Portanto, esses usuários podem ser enviados em uma perseguição de ganso selvagem tentando entender e ler todas essas informações, e isso não terá nada a ver com o bug, provavelmente durante todo o ciclo de vida do Ubuntu 20.04.

@DanielShaulov obrigado. Você poderia abrir uma nova edição para isso? Este diz respeito ao problema em 1.14.

@networkimprov sure: # 40184

Mudança https://golang.org/cl/243658 menciona este problema: runtime: let GODEBUG=mlock=0 disable mlock calls

Alterar https://golang.org/cl/244059 menciona este problema: runtime: don't mlock on Ubuntu 5.4 systems

Quando é que o go1.14.7 é lançado com esta modificação?

A correção está em todas as versões desde 1.14.1, lançada meses atrás.

Mudança https://golang.org/cl/246200 menciona este problema: runtime: revert signal stack mlocking

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

michaelsafyan picture michaelsafyan  ·  3Comentários

longzhizhi picture longzhizhi  ·  3Comentários

dominikh picture dominikh  ·  3Comentários

stub42 picture stub42  ·  3Comentários

natefinch picture natefinch  ·  3Comentários