Rust: Problema de insegurança de memória em Rust seguro

Criado em 17 fev. 2020  ·  38Comentários  ·  Fonte: rust-lang/rust

Eu tenho um pequeno programa (uma simplificação de uma função de teste de um projeto maior) que divide uma pequena matriz e tenta acessar um elemento fora dos limites da fatia. Executá-lo com cargo run --release usando a versão estável 1.41.0 imprime algo como este (testado em macOS 10.15 e Ubuntu 19.10):

0 0 3 18446744073709551615
[1]    21065 segmentation fault  cargo run --release

Parece que a fatia resultante de alguma forma tem o comprimento 2**64 - 1 , então a verificação de limites é omitida, o que previsivelmente resulta em um segfault. Em 1.39.0 e 1.40.0 o mesmo programa imprime o que eu esperaria:

0 0 3 0
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 16777216', src/main.rs:13:35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

O problema desaparece se eu fizer o seguinte:

  • remova qualquer uma das duas chamadas do_test(...); em main() ;
  • remova o laço for _ in 0..1 { ;
  • substitua o loop for y in 0..x { por for y in 0..1 { ;
  • remova a linha z.extend(std::iter::repeat(0).take(x)); ou substitua-a por z.extend(std::iter::repeat(0).take(1)); ;
  • substitua o loop for arr_ref in arr { por let arr_ref = &arr[0]; ;
  • especifique RUSTFLAGS="-C opt-level=2" ;
  • especifique RUSTFLAGS="-C codegen-units=1" .

Meu melhor palpite é que -C opt-level=3 permite uma passagem de otimização problemática no LLVM, que resulta em uma compilação incorreta. Isso é corroborado pelo fato de que MIR ( --emit mir ) e LLVM IR antes das otimizações ( --emit llvm-ir -C no-prepopulate-passes ) são iguais para -C opt-level=2 e -C opt-level=3 .

Algumas informações adicionais que podem ser úteis:

  • Não consigo reproduzir o problema no campo de jogos Rust (provavelmente porque ele usa codegen-units = 1 );
  • Não consigo reproduzir o problema no Windows 10 com a mesma versão 1.41.0 (não tenho ideia do que o torna diferente);
  • cargo-bisect-rustc diz que a regressão aconteceu primeiro no 2019-12-12 noturno, especificamente neste commit . Isso me parece suspeito, visto que 1.40.0 , que não apresenta o problema, foi lançado após essa data.

Estou anexando o programa in-line para o caso de o repositório GitHub não funcionar (se quiser compilá-lo sem o Cargo, use rustc -C opt-level=3 main.rs ):

fn do_test(x: usize) {
    let arr = vec![vec![0u8; 3]];

    let mut z = Vec::new();
    for arr_ref in arr {
        for y in 0..x {
            for _ in 0..1 {
                z.extend(std::iter::repeat(0).take(x));
                let a = y * x;
                let b = (y + 1) * x - 1;
                let slice = &arr_ref[a..b];
                eprintln!("{} {} {} {}", a, b, arr_ref.len(), slice.len());
                eprintln!("{:?}", slice[1 << 24]);
            }
        }
    }
}

fn main() {
    do_test(1);
    do_test(2);
}
A-LLVM C-bug I-unsound 💥 ICEBreaker-LLVM P-medium T-compiler regression-from-stable-to-stable

Comentários muito úteis

Reprodutor LLVM IR: https://gist.github.com/comex/881074b1bcc545e299e65527c719eef4

Execute opt bconfused.ll -scalar-evolution -loop-idiom -scalar-evolution -indvars -S -O3 -o - | grep xprint . Se o interior dos parênteses for i64 -1 , a otimização com erros ocorreu. Se não for ... pode não ser, mas é difícil ter certeza.

Parece vir do LLVM adicionando incorretamente nuw a add nuw i64 %x, -1 como parte do passo de Simplificação da Variável de Indução. x é o argumento para a função, e nuw significa sem quebra automática sem sinal, então isso efetivamente afirma que o argumento é 0, em um ponto na função onde não há garantia de que ele esteja.

A divisão (editar: do LLVM 9 para o LLVM 10, que @tmiasko disse não ter sido afetado) produz este commit:

commit 58e8c793d0e43150a6452e971a32d7407a8a7401
Author: Tim Northover <[email protected]>
Date:   Mon Sep 30 07:46:52 2019 +0000

    Revert "[SCEV] add no wrap flag for SCEVAddExpr."

    This reverts r366419 because the analysis performed is within the context of
    the loop and it's only valid to add wrapping flags to "global" expressions if
    they're always correct.

    llvm-svn: 373184

Parece promissor, já que r366419 (o commit que o commit acima reverte) está incluído no branch LLVM 9.0 que Rust usa.

Todos 38 comentários

cc @ rust-lang / compiler
@rustbot ping icebreakers-llvm

A equipe de lançamento está considerando fazer um lançamento pontual para o Rust 1.41 (discutimos brevemente sobre isso na reunião da semana passada), e eu adoraria que isso fosse incluído se pudermos fazer um PR logo.

Ei disjuntores de LLVM! Este bug foi identificado como um bom
"Candidato de quebra de ICE LLVM". Caso seja útil, aqui estão alguns
[instruções] para lidar com esses tipos de bugs. Talvez dê uma olhada?
Obrigado! <3

cc @comex @DutchGhost @ hanna-kruppe @hdhoang @heyrutvik @ JOE1994 @jryans @mmilenko @nagisa @nikic @ Noah-Kennedy @SiavoshZarrasvand @spastorino @vertexclique @vgxbj

Executá-lo com o cargo run --release usando a versão estável 1.41.0 imprime algo assim (testado no macOS 10.15 e no Ubuntu 19.10):

Não consigo reproduzir isso no playground . O programa funciona bem lá em 1.41.0 no modo de lançamento.

EDIT: Ah, você já disse isso.
Além disso, o programa está bom em Miri, então isso provavelmente não é UB, mas uma compilação incorreta.

Apenas para adicionar um ponto de dados, posso reproduzir isso no Linux com o último nightly:

[andrew<strong i="6">@krusty</strong> rust-69225]$ rustc --version
rustc 1.43.0-nightly (5e7af4669 2020-02-16)

[andrew<strong i="7">@krusty</strong> rust-69225]$ cat main.rs
fn do_test(x: usize) {
    let arr = vec![vec![0u8; 3]];

    let mut z = Vec::new();
    for arr_ref in arr {
        for y in 0..x {
            for _ in 0..1 {
                z.extend(std::iter::repeat(0).take(x));
                let a = y * x;
                let b = (y + 1) * x - 1;
                let slice = &arr_ref[a..b];
                eprintln!("{} {} {} {}", a, b, arr_ref.len(), slice.len());
                eprintln!("{:?}", slice[1 << 24]);
            }
        }
    }
}

fn main() {
    do_test(1);
    do_test(2);
}

[andrew<strong i="8">@krusty</strong> rust-69225]$ rustc -C opt-level=3 main.rs

[andrew<strong i="9">@krusty</strong> rust-69225]$ ./main
0 0 3 18446744073709551615
zsh: segmentation fault (core dumped)  ./main

Consegui reproduzir o acima com exatamente a mesma saída com Rust 1,41 estável. Ferrugem 1,40 estável não apresenta o problema:

$ ./main
0 0 3 0
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 16777216', main.rs:13:35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Acho que tudo isso é consistente com o relatório de @dfyz , exceto que pelo menos confirma que não é específico do macOS.

  • cargo-bisect-rustc diz que a regressão aconteceu primeiro no 2019-12-12 noturno, especificamente neste commit . Isso me parece suspeito, visto que 1.40.0 , que não apresenta o problema, foi lançado após essa data.

Isso é esperado. 1.40.0 foi lançado em 19/12/2019 com base no que era então o branch beta , que foi ramificado de master seis semanas antes (na época do lançamento 1.39.0). Veja https://doc.rust-lang.org/book/apêndice-07-nightly-rust.html para mais informações sobre canais de lançamento.

Se eu tivesse que adivinhar, diria que https://github.com/rust-lang/rust/pull/67015 é o provável culpado. Isso corrigiu 3 problemas do codegen, portanto, tocou o código crítico do codegen.
Cc @ osa1 @ oli-obk @wesleywiser

Obrigado pelo ping. No momento, estou fazendo uma construção para investigar. É possível que este seja um bug introduzido com # 67015. Outra possibilidade é que # 67015 acaba de revelar um bug existente.

Consegui reproduzir o segfault usando rustc nightly no Linux, mas não com minha compilação gerada com este config.toml:

config.toml

[llvm]

[build]

[install]

[rust]
optimize = true
debug = true
codegen-units = 0
debug-assertions = true
debuginfo-level = 2

[target.x86_64-unknown-linux-gnu]
llvm-config = "/usr/bin/llvm-config-9"

[dist]

Usando o rustc nightly, verifiquei os MIRs antes e depois do ConstProp e os MIRs são idênticos. Portanto, se isso é causado por ConstProp, é por causa de uma diferença no código gerado de uma biblioteca, não deste programa.

Regressão em 033662dfbca088937b9cdfd3d9584015b5e375b2

@rustbot modificar rótulos: -E-needs-bisection


@ osa1 o debug-assertions = true provavelmente é o culpado. Quando tento compilar (com um compilador noturno vanilla) o programa com -C debug-assertions=y o programa entra em pânico em vez do segfault

Acho que resolvi! Reverter a983e0590a43ed8b0f60417828efd4e79b51f494 corrige o problema. Pareceu-me ser o culpado o dia todo, mas não consegui testá-lo no trabalho :) Alguém pode me ajudar a melhor fazer um RP para esse problema? Acho que a melhor maneira seria adicionar um caso de teste que deve falhar, mas parece que é muito específico da plataforma, etc., então talvez não seja uma boa ideia afinal? Alguma ideia aqui? Obrigado!

(Isso já foi dividido ao meio pelo OP)

Consegui reproduzir isso com uma compilação local com debug-assertions desativado (obrigado por isso @ hellow554).

Alguns dos PRs no rollup causam conflitos quando revertidos porque desde então rustfmt -ed tudo, mas acredito que esse problema se deve ao # 67174.

editar: parece que encontramos isso ao mesmo tempo @shahn :)

@lqd sim, esse é o problema que inclui o commit que mencionei acima. Esse é o culpado.

Para adicionar outro ponto de dados, o problema desaparece quando codegen-units é definido como 3 ou menos (assumindo o perfil de lançamento, com incremental=false ). Em outras palavras, sou capaz de reproduzir quando codegen-units é 4 ou maior.

A chamada para panic_bounds_check desaparece após a passagem do Jump Threading do LLVM. Posso reproduzir o problema com opt do LLVM 9, mas não do LLVM 10.

Então, eu verifiquei e construí um estágio 1 rustc ( ./x.py build -i --stage 1 ), reconstruí libstd ( ./x.py build -i --stage 1 --keep-stage 0 src/libstd ) sem # 67174 e recompilei o programa de falha de seg. Com quatro unidades codegen ( rustc +stage1 -C opt-level=3 -C codegen-units=4 main.rs ). Como esperado, isso fez com que o segfault fosse embora. Se eu aplicar o # 67174 novamente, o segfault retorna.

Isso significa que agora tenho dois compiladores que diferem apenas na biblioteca padrão que usam. Vamos chamar esses compiladores de GOOD (sem segfault) e BAD (segfault).

Percebi então que os 4 arquivos _não otimizados_ *.ll gerados por GOOD ( -C no-prepopulate-passes ) são praticamente iguais aos gerados por BAD (a única diferença Eu vi que havia diferentes IDs aleatórios em nomes de função), mas os arquivos _optimized_ *.ll (sem -C no-prepopulate-passes ) são totalmente diferentes. Não sou um especialista em compilador de forma alguma, mas como o programa que está sendo compilado é exatamente o mesmo em ambos os casos, os dois compiladores propriamente ditos são exatamente os mesmos e a única diferença está em uma biblioteca padrão pré-compilada, eu _penso_ LTO pode estar envolvido .

Na verdade, se eu passar qualquer um dos -Z thinlto=no , -C lto=no , -C lto=yes , -C lto=thin (neste ponto, não tenho certeza, mas acho que todas as formas de -C lto são diferentes de ThinLTO, que é usado por padrão) para BAD , o segfault desaparece novamente.

Parece provável que o LTO seja o culpado aqui?

Eu li o caso de teste. Eu li o commit que está sendo revertido. Eu ainda não tenho a menor idéia do que está acontecendo. O que quebrou?

O que quebrou?

Não acredito que neste ponto alguém possa dizer com certeza o que quebrou exatamente, mas minha análise provisória é esta (por favor, considere o seguinte com um grão de sal, eu não estou nem na equipe Rust nem na equipe LLVM, todos eu pode fazer é mexer no compilador e olhar para o LLVM IR):

  • removemos as verificações de estouro de uma única linha em Layout::repeat() da biblioteca padrão, o que acabou gerando essa memória insegura. Matematicamente, usar adição não verificada aqui deve ser perfeitamente seguro - o comentário nesta função (e também em Layout::pad_to_align() ) explica o porquê;
  • meu exemplo de código que demonstra o problema nem mesmo chama esta função, mas usa explicitamente Vec , que implicitamente usa Vec::reserve_internal() , que por sua vez chama Layout::repeat() ;
  • Layout::repeat() está marcado como #[inline] e, aparentemente, a única diferença relevante entre GOOD e BAD é se esta função está alinhada em do_test() ou não. Por exemplo, restaurar verificações de estouro proíbe o inlining e corrige o problema; remover o atributo #[inline] causa o mesmo efeito; desativar LTO desativa inlining para funções de biblioteca e novamente corrige o problema.

Se isso for verdade (novamente, não estou 100% certo sobre nenhuma das opções acima), isso significa que alguma passagem LLVM desonesta ou uma combinação de passagens otimiza erroneamente o IR após o in-line. Isso é o que estou tentando investigar, mas infelizmente não é fácil (pelo menos para um não quebrador de LLVM-ICE como eu) porque as diferenças de IR entre GOOD e BAD são moderadas ampla. Parece que a versão ruim omite panic_bounds_check , mas ainda não tenho certeza do porquê.

Além disso, inspirado no comentário de @tmiasko , tentei compilar o rustc com diferentes versões do LLVM, e parece que o LLVM 10rc1 corrige o problema (a última versão com defeito do LLVM que tentei é a 9.0.1). Não parece que o passo do Jump Threading seja o culpado, porque não vejo nenhum commit relevante entre 9.0.1 e 10rc1.

Se a reversão da mudança Layout::repeat() ocultar apenas um sintoma de uma ocorrência

Se reverter a alteração Layout :: repeat () esconde apenas um sintoma de uma ocorrência específica de um bug não relacionado, reverter é realmente a coisa certa a fazer?

Acho que pode estar tudo bem se:

  • A mudança é enviada
  • Isso torna o bug muito mais fácil de ser acionado, afetando muitos usuários
  • Consertar isso de forma adequada levará muito tempo

Se isso se mantiver, acho que vou reverter a alteração, enviar uma versão menor para desbloquear os usuários (as coisas funcionaram bem sem a alteração, embora o bug ainda estivesse lá) e, em seguida, focar no bug real.

Em outro compilador, lembro-me de realmente fazer isso. Revertemos uma mudança em um branch de lançamento, mas não no master (o que não é uma boa prática, causou problemas mais tarde) e enviamos um novo lançamento secundário. Então consertou o bug real.

Em qualquer caso, contanto que o bug seja priorizado e corrigido, e o commit que torna o bug muito mais fácil de acionar não é uma correção de bug em si, não vejo problemas em revertê-lo por enquanto.

Então, a questão é: esse bug é realmente fácil de acionar? Até agora, tivemos um relatório com um caso de teste um tanto complicado em que a tentativa de minimizar ainda mais (como desenrolar um loop for _ in 0..1 aparentemente trivial) falha na reprodução.

Não acho que o bug seja fácil de acionar, parece que tive um azar especial.

De qualquer forma, eu realmente aprecio o trabalho que @shahn teve para reverter a mudança Layout::new() , mas a reversão da IMO _não_ é a coisa certa a fazer neste caso. Meu raciocínio (além do que @SimonSapin disse):

  • remover verificações de estouro em Layout::repeat() permite que o LLVM inline Vec::reserve() em compilações de lançamento. Pode dar um bom aumento de desempenho em alguns casos (embora isso deva ser medido, é claro);
  • literalmente, a função anterior em libcore/alloc.rs ( Layout::pad_to_align() ) usa o mesmo padrão de adição não verificada com exatamente o mesmo comentário explicando o que o torna possível. Restaurar os cheques excedentes em Layout::repeat() mas não em Layout::pad_to_align() parece muito estranho para mim;
  • se alguém está realmente bloqueado neste problema (definitivamente não estou), existem muitas outras soluções alternativas que não envolvem alterações de stdlib (por exemplo, desabilitar ThinLTO, alterar o nível de otimização, diminuir o número de unidades codegen).

Talvez lançando localmente uma afirmação defensiva do invariante incluída no lançamento como uma pré-condição para que entre em pânico com detalhes específicos para caçar esse caso extremo específico ou algum depurador-fu? Eu apostaria que é um cálculo não verificado passando sob certas condições.

Então, quando for rastreado (em algum lugar no LLVM como acabei de aprender, ty @dyfz), um caso de teste de regressão seria incrível para que não aconteça novamente. 🙏

Reprodutor LLVM IR: https://gist.github.com/comex/881074b1bcc545e299e65527c719eef4

Execute opt bconfused.ll -scalar-evolution -loop-idiom -scalar-evolution -indvars -S -O3 -o - | grep xprint . Se o interior dos parênteses for i64 -1 , a otimização com erros ocorreu. Se não for ... pode não ser, mas é difícil ter certeza.

Parece vir do LLVM adicionando incorretamente nuw a add nuw i64 %x, -1 como parte do passo de Simplificação da Variável de Indução. x é o argumento para a função, e nuw significa sem quebra automática sem sinal, então isso efetivamente afirma que o argumento é 0, em um ponto na função onde não há garantia de que ele esteja.

A divisão (editar: do LLVM 9 para o LLVM 10, que @tmiasko disse não ter sido afetado) produz este commit:

commit 58e8c793d0e43150a6452e971a32d7407a8a7401
Author: Tim Northover <[email protected]>
Date:   Mon Sep 30 07:46:52 2019 +0000

    Revert "[SCEV] add no wrap flag for SCEVAddExpr."

    This reverts r366419 because the analysis performed is within the context of
    the loop and it's only valid to add wrapping flags to "global" expressions if
    they're always correct.

    llvm-svn: 373184

Parece promissor, já que r366419 (o commit que o commit acima reverte) está incluído no branch LLVM 9.0 que Rust usa.

Triagem do compilador T: meio P, com base no seguinte resumo da situação:

pnkfelix: Parece que os itens de trabalho restantes para # 69225 são 1. corrigir LLVM (selecionando seu 58e8c793d0e43150a6452e971a32d7407a8a7401 ou atualizando para LLVM 10) e, em seguida, 2. lendo PR # 67174.
pnkfelix: no entanto, nenhum desses itens me parece de alta prioridade.
pnkfelix: pelo menos, este bug LLVM não parece melhor ou pior do que outros bugs codegen LLVM. O que eu acho que foi o que @simulacrum acabou de dizer.

Atualização: a atualização para LLVM 10 está sendo tentada em PR # 67759

Atualização 2: pode ser imprudente escolher cegamente o commit de reversão, já que presumivelmente escolhemos o original por algum motivo e, portanto, a reversão pode ter efeitos downstream indesejados. No mínimo, não devemos tentar sem entender as consequências (e, dado o esforço para atualizar para o LLVM 10, provavelmente não devemos tentar selecionar a reversão, pois seria um grande desperdício de esforço ...)

O commit original foi escolhido a dedo? Pelo menos a partir do comentário de @comex , que não está claro para mim ("está incluído no branch do LLVM 9.0 que o Rust usa" também pode significar que é apenas parte do LLVM 9.0).

O commit em questão é uma mudança muito local e pequena que adiciona um único parâmetro a uma chamada de função e literalmente diz it is safe [in this case] to add SCEV::FlagNSW (e a julgar pelo código, o novo parâmetro também pode ser SCEV::FlagNUW ), Portanto, acho muito provável que seja exatamente isso que causa a otimização incorreta. Posso confirmar que remover esse parâmetro (ou seja, alterar (void)getAddRecExpr(getAddExpr(StartVal, Accum, Flags), Accum, L, Flags); para (void)getAddRecExpr(getAddExpr(StartVal, Accum), Accum, L, Flags); ) corrige o problema.

Além disso, esse commit problemático _não_ foi escolhido a dedo. É apenas azar - parece que a reversão aconteceu depois que o 9.0.0 foi criado, então o upstream 9.0.0 ainda tem o parâmetro incorreto. A reversão não foi retransmitida para 9.0.1 também, por algum motivo. 10.0.0-rc1 e versões posteriores têm a reversão.

Aqui está um comentário que explica por que não é, de fato, seguro adicionar nsw ou nuw aqui. Provavelmente é uma boa ideia conversar com um desenvolvedor de LLVM sobre isso, mas acho que escolher a reversão irá resolver o problema e não terá nenhum efeito indesejado, já que é tão pequeno e independente.

PS Muitos parabéns a

FWIW, posso confirmar que https://github.com/llvm/llvm-project/commit/58e8c793d0e43150a6452e971a32d7407a8a7401 é seguro para escolher, é uma mudança conservadora. Consulte também https://lists.llvm.org/pipermail/llvm-dev/2019-September/135195.html se estiver interessado em mais contexto sobre o que é o problema com sinalizadores nowrap SCEV.

Acho que acabei de encontrar uma maneira de reproduzir o problema mesmo depois de reverter o # 67174. Este é um programa um pouco mais longo, mas ainda seguro, que faz segfaults confiável no Windows, Linux e macOS usando o último nightly com # 67174 revertido:

fn do_test(x: usize) {
    let mut arr = vec![vec![0u8; 3]];

    let mut z = vec![0];
    for arr_ref in arr.iter_mut() {
        for y in 0..x {
            for _ in 0..1 {
                z.reserve_exact(x);
                let iterator = std::iter::repeat(0).take(x);
                let mut cnt = 0;
                iterator.for_each(|_| {
                    z[0] = 0;
                    cnt += 1;
                });
                let a = y * x;
                let b = (y + 1) * x - 1;
                let slice = &mut arr_ref[a..b];
                slice[1 << 24] += 1;
            }
        }
    }
}

fn main() {
    do_test(1);
    do_test(2);
}

Janelas:

PS> rustup run nightly rustc --version
rustc 1.43.0-nightly (6d0e58bff 2020-02-23)
PS> rustup run nightly cargo run --release
    Finished release [optimized] target(s) in 0.01s
     Running `target\release\rust-segfault.exe`
error: process didn't exit successfully: `target\release\rust-segfault.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

Linux:

$ rustup run nightly rustc --version
rustc 1.43.0-nightly (6d0e58bff 2020-02-23)
$ rustup run nightly cargo run --release
    Finished release [optimized] target(s) in 1.13s
     Running `target/release/rust-segfault`
Segmentation fault (core dumped)

Mac OS:

λ rustup run nightly rustc --version
rustc 1.43.0-nightly (6d0e58bff 2020-02-23)
λ rustup run nightly cargo run --release
    Finished release [optimized] target(s) in 0.01s
     Running `target/release/rust-segfault`
[1]    24331 segmentation fault  rustup run nightly cargo run --release

Este programa não depende do número de unidades CodeGen, por isso segfaults no campo de jogos, também (em estável, beta e noturno). Eu também reproduzi isso compilando rustc do mestre (com # 67174 revertido) vinculado ao LLVM 9.

O bug subjacente do LLVM ainda é o mesmo, então atualizar para o LLVM 10 ou escolher a correção do LLVM faz com que o segfault desapareça.

Eu realmente gostaria de entender melhor o que está acontecendo. Parece que as verificações de limites foram eliminadas por causa dos nuw extras, que vêm de valores SCEV armazenados incorretamente em cache (assim como no programa C do thread @nikic vinculado). Mas quando a má otimização acontece, mal consigo reconhecer meu programa simples através das camadas de blocos básicos do LLVM; pior, qualquer mudança aparentemente sem operação no código-fonte (por exemplo, remover a variável cnt ) leva a um IR LLVM de aparência muito diferente e faz com que o problema desapareça.

Minha impressão é que 1.41.1 acabou de ser finalizado em # 69359 (tempo ruim da minha parte), então não há muito que possa ser feito neste momento. É pelo menos uma boa ideia atualizar o comentário em Layout::repeat() com uma explicação mais detalhada do problema do LLVM? Se sim, posso enviar um PR.

Minha impressão é que 1.41.1 acabou de ser finalizado em # 69359 (tempo ruim da minha parte), então não há muito que possa ser feito neste momento.

Se o patch que incluímos em 1.41.1 não corrige realmente o problema, devemos reconsiderar se queremos transportar a nova correção e reconstruir a versão. Houve consenso na reunião da equipe de lançamento para não fazer backport da correção do LLVM, mas eu pessoalmente acho que outro ponto de vista desse novo poderia justificar outra discussão sobre o assunto.

cc @ Mark-Simulacrum @ rust-lang / release

@dfyz , tentaremos obter outro build do 1.41.1 com a correção do LLVM retroativa, enquanto esperamos por um consenso sobre realmente enviá-lo.

FWIW, para mim, o novo reprodutor funciona conforme o esperado ( index out of bounds ) no estável 1.38.0 e anteriores, mas segfaults no 1.39.0 e posteriores. Não há muita diferença no LLVM entre 1,38 e 1,39 (https://github.com/rust-lang/llvm-project/compare/71fe7ec06b85f612fc0e4eb4134c7a7d0f23fac5...8adf9bdccfefb8d03f0e8db de qualquer diferença de 15b012fb41, mas poderia serbustda8db012fb41), mas poderia serbutda8d154b012b41. ao longo do caminho também.

o novo reprodutor funciona como esperado (índice fora dos limites) no estável 1.38.0

Eu (acidentalmente) descobri que definir -C codegen-units=1 em 1.38.0 reproduz o segfault. 1.37.0 parece seguro para mim (nenhuma combinação de opções que tentei produz um segfault).

Desconsidere isso, 1.37.0 usa LLVM 8.
Curiosamente, o diff IR LLVM entre 1.37.0 e 1.38.0 (com -C codegen-units=1 ) é apenas uma linha:

- %71 = icmp eq {}* %70, null
+ %71 = icmp ule {}* %70, null

(onde %70 é derivado do resultado de <core::slice::IterMut<T> as core::iter::traits::iterator::Iterator>::next() )

Isso por si só é suficiente para enganar o LLVM e fazê-lo adicionar o temido nuw a add nuw i64 %x, -1 .

1.37.0 parece seguro para mim (nenhuma combinação de opções que tentei produz um segfault).

Isso está usando LLVM 8, então a mudança SCEV culpada não deveria existir.

Isso está usando LLVM 8

Que pena, desculpe a confusão (fiquei tão feliz em reduzi-lo a uma diferença de uma linha que nem me preocupei em verificar a versão do LLVM).

Preparamos novos artefatos 1.41.1 com a correção LLVM escolhida a dedo nele. Você pode testá-los localmente com:

RUSTUP_DIST_SERVER=https://dev-static.rust-lang.org rustup update stable

ping em https://github.com/rust-lang/rust/issues/69225#issuecomment -586941455

[triagebot] O problema foi resolvido com sucesso sem qualquer envolvimento da equipe do compilador pingado.
Bretty bom.

1.41.1 foi lançado, acho que é hora de finalmente encerrar este problema.

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