Rust: Problema de inseguridad de la memoria en Safe Rust

Creado en 17 feb. 2020  ·  38Comentarios  ·  Fuente: rust-lang/rust

Tengo un programa pequeño (una simplificación de una función de prueba de un proyecto más grande) que corta una pequeña matriz e intenta acceder a un elemento fuera de los límites de la porción. Ejecutarlo con cargo run --release usando la versión estable 1.41.0 imprime algo como esto (probado en macOS 10.15 y Ubuntu 19.10):

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

Parece que el segmento resultante tiene de alguna manera una longitud 2**64 - 1 , por lo que se omite la comprobación de límites, lo que, como era de esperar, da como resultado un defecto de segmento. En 1.39.0 y 1.40.0 el mismo programa imprime lo que esperaría:

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.

El problema desaparece si hago alguna de las siguientes acciones:

  • elimine cualquiera de las dos llamadas do_test(...); en main() ;
  • eliminar el bucle for _ in 0..1 { ;
  • reemplace el ciclo for y in 0..x { con for y in 0..1 { ;
  • elimine la línea z.extend(std::iter::repeat(0).take(x)); o reemplácela con z.extend(std::iter::repeat(0).take(1)); ;
  • reemplace el bucle for arr_ref in arr { con let arr_ref = &arr[0]; ;
  • especificar RUSTFLAGS="-C opt-level=2" ;
  • especificar RUSTFLAGS="-C codegen-units=1" .

Mi mejor suposición es que -C opt-level=3 habilita un pase de optimización problemático en LLVM, lo que resulta en una mala compilación. Esto se corrobora por el hecho de que MIR ( --emit mir ) y LLVM IR antes de las optimizaciones ( --emit llvm-ir -C no-prepopulate-passes ) es el mismo para -C opt-level=2 y -C opt-level=3 .

Alguna información adicional que podría ser útil:

  • No puedo reproducir el problema en el patio de juegos de Rust (presumiblemente porque usa codegen-units = 1 );
  • No puedo reproducir el problema en Windows 10 con el mismo lanzamiento de 1.41.0 (no tengo idea de qué lo hace diferente);
  • cargo-bisect-rustc dice que la regresión ocurrió primero en el 2019-12-12 todas las noches, específicamente en esta confirmación . Esto me parece sospechoso, dado que 1.40.0 , que no presenta el problema, se publicó después de esta fecha.

Adjunto el programa en línea en caso de que el repositorio de GitHub no funcione (si desea compilarlo sin 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

Comentario más útil

Reproductor de infrarrojos LLVM: https://gist.github.com/comex/881074b1bcc545e299e65527c719eef4

Ejecute opt bconfused.ll -scalar-evolution -loop-idiom -scalar-evolution -indvars -S -O3 -o - | grep xprint . Si el interior del paréntesis es i64 -1 , se produjo la optimización con errores. Si no lo es ... puede que no lo haya hecho, pero es difícil estar seguro.

Parece provenir de LLVM que agregó incorrectamente nuw a add nuw i64 %x, -1 como parte del pase de simplificación variable de inducción. x es el argumento de la función, y nuw significa que no hay ajuste sin firmar, por lo que esto afirma efectivamente que el argumento es 0, en un punto de la función donde no se garantiza que sea.

La bisección (editar: de LLVM 9 a LLVM 10, que @tmiasko dijo que no se vio afectada) produce esta confirmación:

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 prometedor, ya que r366419 (el compromiso que revierte el compromiso anterior) está incluido en la rama LLVM 9.0 que usa Rust.

Todos 38 comentarios

cc @ rust-lang / compilador
@rustbot ping rompehielos-llvm

El equipo de lanzamiento está considerando hacer un lanzamiento puntual para Rust 1.41 (lo discutimos brevemente en la reunión de la semana pasada), y me encantaría que esto se incluya en él si podemos obtener un PR pronto.

¡Hola, rompehielos LLVM! Este error se ha identificado como un buen
"Candidato LLVM ICE -break". En caso de que sea útil, aquí hay algunos
[instrucciones] para abordar este tipo de errores. ¿Quizás echar un vistazo?
¡Gracias! <3

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

Ejecutarlo con cargo run - release usando la versión estable 1.41.0 imprime algo como esto (probado en macOS 10.15 y Ubuntu 19.10):

No puedo reproducir esto en el patio de recreo . El programa funciona bien allí en 1.41.0 en modo de lanzamiento.

EDITAR: Ah, ya dijiste eso.
Además, el programa está bien en Miri, por lo que probablemente no sea UB sino una compilación incorrecta.

Solo para agregar un punto de datos, puedo reproducir esto en Linux con lo último de la noche:

[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

Pude reproducir lo anterior con exactamente la misma salida con Rust 1.41 estable. Rust 1.40 estable no presenta el 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.

Creo que todo esto es consistente con el informe de @dfyz , excepto que al menos esto confirma que no es específico de macOS.

  • cargo-bisect-rustc dice que la regresión ocurrió por primera vez en 2019-12-12 todas las noches, específicamente en esta confirmación . Esto me parece sospechoso, dado que 1.40.0 , que no presenta el problema, se publicó después de esta fecha.

Se esperaba esto. 1.40.0 fue lanzado el 2019-12-19 basado en lo que entonces era la rama beta , que se ramificó desde master seis semanas antes (alrededor del momento del lanzamiento 1.39.0). Consulte https://doc.rust-lang.org/book/appendix-07-nightly-rust.html para obtener más información sobre los canales de lanzamiento.

Si tuviera que adivinar, diría que https://github.com/rust-lang/rust/pull/67015 es el posible culpable. Esto solucionó 3 problemas de codegen, por lo que toca el código crítico de codegen.
Cc @ osa1 @ oli-obk @wesleywiser

Gracias por el ping. Actualmente estoy haciendo una compilación para investigar. Es posible que este sea un error introducido con # 67015. Otra posibilidad es que # 67015 acaba de revelar un error existente.

Logré reproducir el segfault usando rustc nightly en Linux, pero no con mi compilación generada con 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 rustc todas las noches, verifiqué los MIR antes y después de ConstProp y los MIR son idénticos. Entonces, si esto es causado por ConstProp, entonces se debe a una diferencia en el código generado de una biblioteca, no a este programa.

Regresión en 033662dfbca088937b9cdfd3d9584015b5e375b2

@rustbot modificar etiquetas: -E-necesita-bisección


@ osa1 el debug-assertions = true es probablemente el culpable. Cuando trato de compilar (con un compilador nocturno vainilla) el programa con -C debug-assertions=y el programa entra en pánico en lugar del segfault

¡Creo que lo resolví! La reversión de a983e0590a43ed8b0f60417828efd4e79b51f494 soluciona el problema. Me pareció el culpable todo el día, pero no pude probarlo en el trabajo :) ¿Puede alguien ayudarme a hacer un PR para este problema? Creo que la mejor manera sería agregar un caso de prueba que debe fallar, pero parece que esto es muy específico de la plataforma, etc., así que tal vez no sea una buena idea después de todo. ¿Alguna idea aquí? ¡Gracias!

(Esto ya fue bisecado por el OP)

Logré reproducir esto con una compilación local con debug-assertions desactivado (gracias por eso @ hellow554).

Algunos de los RP en el resumen causan conflictos cuando se revierten porque desde entonces hemos rustfmt -ed todo, pero creo que este problema se debe a # 67174.

editar: parece que encontramos esto al mismo tiempo @shahn :)

@lqd sí, este es el problema que incluye la confirmación que

Para agregar otro punto de datos, el problema desaparece cuando codegen-units se establece en 3 o menos (asumiendo el perfil de lanzamiento, con incremental=false ). En otras palabras, puedo reproducir cuando codegen-units es 4 o más.

La llamada a panic_bounds_check desaparece después de la pasada LLVM Jump Threading. Puedo reproducir el problema optando desde LLVM 9, pero no desde LLVM 10.

Entonces, revisé y construí una etapa 1 rustc ( ./x.py build -i --stage 1 ), reconstruí libstd ( ./x.py build -i --stage 1 --keep-stage 0 src/libstd ) sin # 67174, y volví a compilar el programa de segmentación con cuatro unidades de codegen ( rustc +stage1 -C opt-level=3 -C codegen-units=4 main.rs ). Como era de esperar, esto hizo que desapareciera la falla secundaria. Si vuelvo a aplicar # 67174, el segfault regresa.

Esto significa que ahora tengo dos compiladores que solo difieren en la biblioteca estándar que usan. Llamemos a estos compiladores GOOD (no segfault) y BAD (segfault).

Luego me di cuenta de que los 4 archivos _unoptimizados_ *.ll generados por GOOD ( -C no-prepopulate-passes ) son prácticamente los mismos que los generados por BAD (la única diferencia Vi las diferentes ID aleatorias en los nombres de las funciones), pero los archivos _optimized_ *.ll (no -C no-prepopulate-passes ) son tremendamente diferentes. No soy un experto en compiladores de ninguna manera, pero dado que el programa que se está compilando es exactamente el mismo en ambos casos, ambos compiladores son exactamente iguales, y la única diferencia está en una biblioteca estándar precompilada, creo que LTO podría estar involucrado .

De hecho, si paso cualquiera de -Z thinlto=no , -C lto=no , -C lto=yes , -C lto=thin (en este punto no estoy seguro, pero supongo que todas las formas de -C lto son diferentes de ThinLTO, que se usa por defecto) a BAD , el segfault desaparece una vez más.

¿Parece probable que LTO tenga la culpa aquí?

He leído el caso de prueba. He leído el compromiso que se está revirtiendo. Todavía no tengo ni la más remota idea de lo que está pasando. ¿Qué se rompió?

¿Qué se rompió?

No creo que en este momento nadie pueda decir con certeza qué se rompió exactamente, pero mi análisis tentativo es el siguiente (por favor, tome lo siguiente con un grano de sal, no estoy ni en el equipo de Rust ni en el equipo de LLVM, todo lo que puede hacer es jugar con el compilador y mirar el LLVM IR):

  • eliminamos las comprobaciones de desbordamiento de una sola línea en Layout::repeat() de la biblioteca estándar, lo que eventualmente llevó a esta memoria insegura. Matemáticamente, usar la suma sin marcar aquí debería ser perfectamente seguro: el comentario en esta función (y también en Layout::pad_to_align() ) explica por qué;
  • mi ejemplo de código que demuestra el problema ni siquiera llama a esta función, pero explícitamente usa Vec , que implícitamente usa Vec::reserve_internal() , que a su vez llama a Layout::repeat() ;
  • Layout::repeat() está marcado como #[inline] , y aparentemente la única diferencia relevante entre GOOD y BAD es si esta función está insertada en do_test() o no. Por ejemplo, restaurar las comprobaciones de desbordamiento prohíbe la inserción y soluciona el problema; eliminar el atributo #[inline] provoca el mismo efecto; deshabilitar LTO deshabilita la inserción para las funciones de la biblioteca y nuevamente corrige el problema.

Si esto es cierto (de nuevo, no estoy 100% seguro de nada de lo anterior), esto significa que algún pase LLVM deshonesto o una combinación de pases optimiza mal el IR después de la alineación. Eso es lo que estoy tratando de investigar actualmente, pero lamentablemente no es fácil (al menos para un rompedor de ICE que no es LLVM como yo) porque las diferencias de IR entre GOOD y BAD son moderadamente grande. Parece que la versión incorrecta omite panic_bounds_check , pero todavía no estoy seguro de por qué.

Además, inspirado por el comentario de @tmiasko , intenté compilar rustc con diferentes versiones de LLVM, y parece que LLVM 10rc1 soluciona el problema (la última versión defectuosa de LLVM que probé es 9.0.1). Sin embargo, no parece que el pase Jump Threading sea ​​el culpable, porque no veo ninguna confirmación relevante entre 9.0.1 y 10rc1.

Si revertir el cambio Layout::repeat() solo oculta un síntoma de una ocurrencia

Si revertir el cambio Layout :: repeat () solo oculta un síntoma de una ocurrencia específica de un error no relacionado, ¿revertir es realmente lo correcto?

Creo que puede estar bien si:

  • El cambio se envía
  • Hace que el error sea mucho más fácil de activar y afecta a muchos usuarios.
  • Arreglarlo correctamente llevará mucho tiempo

Si estos se mantienen, creo que revertiría el cambio, enviaría una versión menor para desbloquear a los usuarios (las cosas funcionaron bien sin el cambio aunque el error todavía estaba allí) y luego me concentraría en el error real.

En otro compilador recuerdo haber hecho esto. Revertimos un cambio en una rama de lanzamiento, pero no en el maestro (lo cual no es una buena práctica, causó problemas más adelante), enviamos una nueva versión menor. Luego corrigió el error real.

En cualquier caso, siempre y cuando el error sea priorizado y corregido, y la confirmación que hace que el error sea mucho más fácil de activar no es una corrección de errores en sí, no veo ningún problema para revertirlo por ahora.

Entonces, una pregunta es, ¿este error es realmente fácil de activar? Hasta ahora, hemos tenido un informe con un caso de prueba algo complicado en el que intentar minimizar aún más (como desenrollar un bucle for _ in 0..1 aparentemente trivial) no se reproduce.

No creo que el error sea fácil de desencadenar, parece que tuve una mala suerte.

De todos modos, realmente aprecio el problema por el que @shahn pasó para revertir el cambio Layout::new() , pero en mi opinión, revertirlo no es lo correcto en este caso. Mi razonamiento (además de lo que dijo @SimonSapin ):

  • la eliminación de comprobaciones de desbordamiento en Layout::repeat() permite que LLVM incorpore Vec::reserve() en las versiones de versión. Podría dar un buen impulso al rendimiento en algunos casos (aunque esto debería medirse, por supuesto);
  • literalmente, la función anterior en libcore/alloc.rs ( Layout::pad_to_align() ) usa el mismo patrón de suma sin marcar con exactamente el mismo comentario explicando qué lo hace posible. Restaurar las comprobaciones de desbordamiento en Layout::repeat() pero no en Layout::pad_to_align() me parece realmente extraño;
  • Si alguien está realmente bloqueado en este problema (definitivamente no lo estoy), hay muchas otras soluciones que no involucran cambios de stdlib (por ejemplo, deshabilite ThinLTO, cambie el nivel de optimización, disminuya el número de unidades de codegen).

¿Quizás lanzar localmente una afirmación defensiva incluida en la versión del invariante como condición previa para que entre en pánico con detalles específicos para buscar este caso de borde específico o algún debugger-fu? Apuesto a que es un cálculo no verificado que se realiza bajo ciertas condiciones.

Luego, cuando se rastrea (en algún lugar de LLVM como acabo de aprender, ty @dyfz), un caso de prueba de regresión sería increíble para que no vuelva a suceder. 🙏

Reproductor de infrarrojos LLVM: https://gist.github.com/comex/881074b1bcc545e299e65527c719eef4

Ejecute opt bconfused.ll -scalar-evolution -loop-idiom -scalar-evolution -indvars -S -O3 -o - | grep xprint . Si el interior del paréntesis es i64 -1 , se produjo la optimización con errores. Si no lo es ... puede que no lo haya hecho, pero es difícil estar seguro.

Parece provenir de LLVM que agregó incorrectamente nuw a add nuw i64 %x, -1 como parte del pase de simplificación variable de inducción. x es el argumento de la función, y nuw significa que no hay ajuste sin firmar, por lo que esto afirma efectivamente que el argumento es 0, en un punto de la función donde no se garantiza que sea.

La bisección (editar: de LLVM 9 a LLVM 10, que @tmiasko dijo que no se vio afectada) produce esta confirmación:

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 prometedor, ya que r366419 (el compromiso que revierte el compromiso anterior) está incluido en la rama LLVM 9.0 que usa Rust.

Triaje del compilador T: P-medio, basado en el siguiente resumen de situación:

pnkfelix: Parece que los elementos de trabajo restantes para # 69225 son 1. arreglar LLVM (ya sea seleccionando con precisión su 58e8c793d0e43150a6452e971a32d7407a8a7401 o actualizando a LLVM 10) y luego 2. readd PR # 67174.
pnkfelix: sin embargo, ninguno de estos me parece de alta prioridad.
pnkfelix: Al menos, este error de LLVM no parece ni mejor ni peor que otros errores de codegen de LLVM. Lo que supongo que es lo que acaba de decir @simulacrum .

Actualización: se está intentando actualizar a LLVM 10 en PR # 67759

Actualización 2: Puede que no sea prudente seleccionar a ciegas su confirmación de reversión, ya que presumiblemente elegimos la original por alguna razón y, por lo tanto, la reversión podría tener efectos posteriores no deseados. Como mínimo, no deberíamos intentarlo sin comprender las consecuencias (y, dado el esfuerzo para actualizar a LLVM 10, probablemente no deberíamos intentar seleccionar la reversión en absoluto, ya que sería un esfuerzo en gran parte en vano ...)

¿El compromiso original fue elegido con precisión? Al menos por el comentario de @comex que no me queda claro ("está incluido en la rama LLVM 9.0 que usa Rust" también podría significar que es solo parte de LLVM 9.0).

El cometen en cuestión es un muy local y pequeño cambio que añade un único parámetro a una llamada de función y literalmente dice it is safe [in this case] to add SCEV::FlagNSW (ya juzgar por el código, el nuevo parámetro también puede ser SCEV::FlagNUW ), así que creo que es muy probable que esto sea exactamente lo que causa la mala optimización. Puedo confirmar que eliminar este parámetro (es decir, cambiar (void)getAddRecExpr(getAddExpr(StartVal, Accum, Flags), Accum, L, Flags); a (void)getAddRecExpr(getAddExpr(StartVal, Accum), Accum, L, Flags); ) soluciona el problema.

Además, esta confirmación problemática _no_ fue seleccionada. Es solo mala suerte: parece que la reversión ocurrió después de que se creó 9.0.0, por lo que la versión anterior de 9.0.0 todavía tiene el parámetro ofensivo. La reversión tampoco se actualizó a 9.0.1, por alguna razón. 10.0.0-rc1 y versiones posteriores tienen la reversión.

Aquí hay un comentario que explica por qué, de hecho, no es seguro agregar nsw o nuw aquí. Probablemente sea una buena idea hablar con un desarrollador de LLVM sobre esto, pero creo que seleccionar cuidadosamente la reversión solucionará este problema y no tendrá ningún efecto no deseado, ya que es muy pequeño y autónomo.

PD: Enormes felicitaciones a @comex por causar esto. Trabajo fantástico.

FWIW Puedo confirmar que https://github.com/llvm/llvm-project/commit/58e8c793d0e43150a6452e971a32d7407a8a7401 es seguro para elegir, es un cambio conservador. Consulte también https://lists.llvm.org/pipermail/llvm-dev/2019-September/135195.html si está interesado en más contexto sobre cuál es el problema con las banderas nowrap de SCEV.

Creo que acabo de encontrar una manera de reproducir el problema incluso después de revertir # 67174. Aquí hay un programa un poco más largo, pero aún seguro, que segmenta de manera confiable en Windows, Linux y macOS usando la última versión nocturna con # 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);
}

Ventanas:

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 no depende del número de unidades CODEGEN, por lo que segfaults en el patio, también (en estable, beta y noche). También reproduje esto compilando rustc del maestro (con # 67174 revertido) vinculado con LLVM 9.

El error de LLVM subyacente sigue siendo el mismo, por lo que actualizar a LLVM 10 o seleccionar la corrección de LLVM hace que desaparezca la falla de segmentación.

Realmente desearía entender mejor lo que está pasando. Parece que las comprobaciones de los límites se eliminan debido a los nuw adicionales, que provienen de valores SCEV almacenados en caché incorrectamente (al igual que en el programa C del hilo al que @nikic está vinculado). Pero cuando ocurre la mala optimización, apenas puedo reconocer mi programa simple a través de las capas de bloques básicos LLVM; peor aún, cualquier cambio aparentemente no operativo en el código fuente (por ejemplo, eliminar la variable cnt ) conduce a un LLVM IR de aspecto muy diferente y hace que el problema desaparezca.

Mi impresión es que 1.41.1 acaba de finalizar en # 69359 (mal momento de mi parte), por lo que no hay mucho que se pueda hacer en este momento. ¿Es al menos una buena idea actualizar el comentario en Layout::repeat() con una explicación más detallada del problema LLVM? Si es así, puedo enviar un PR.

Mi impresión es que 1.41.1 acaba de finalizar en # 69359 (mal momento de mi parte), por lo que no hay mucho que se pueda hacer en este momento.

Si el parche que incluimos en 1.41.1 en realidad no soluciona el problema, deberíamos reconsiderar si queremos respaldar la nueva corrección y reconstruir la versión. Hubo consenso en la reunión del equipo de lanzamiento para no respaldar la corrección de LLVM, pero personalmente creo que otra nueva PoC podría justificar otra discusión sobre el tema.

cc @ Mark-Simulacrum @ rust-lang / lanzamiento

@dfyz intentaremos obtener otra compilación de 1.41.1 con la corrección de LLVM retroportada, mientras esperamos el consenso sobre cómo enviarla.

FWIW, para mí el nuevo reproductor funciona como se esperaba ( index out of bounds ) en estable 1.38.0 y anteriores, pero segfaults en 1.39.0 y posteriores. No hay mucha diferencia en LLVM entre 1.38 y 1.39 (https://github.com/rust-lang/llvm-project/compare/71fe7ec06b85f612fc0e4eb4134c7a7d0f23fac5...8adf9bdccfefb8d03f0ef8db413bust it podría generar cualquier diferencia) en el camino también.

el nuevo reproductor funciona como se esperaba (índice fuera de los límites) en 1.38.0 estable

Descubrí (accidentalmente) que la configuración de -C codegen-units=1 en 1.38.0 reproduce el segfault. 1.37.0 me parece seguro (ninguna combinación de opciones que probé produce un error de segmento).

Ignore eso, 1.37.0 usa LLVM 8.
Curiosamente, la diferencia de LLVM IR entre 1.37.0 y 1.38.0 (con -C codegen-units=1 ) es solo una línea:

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

(donde %70 se deriva del resultado de <core::slice::IterMut<T> as core::iter::traits::iterator::Iterator>::next() )

Esto solo es suficiente para engañar a LLVM para que agregue el temido nuw a add nuw i64 %x, -1 .

1.37.0 me parece seguro (ninguna combinación de opciones que probé produce un error de segmento).

Eso es usar LLVM 8, por lo que el cambio SCEV culpable no debería existir en absoluto.

Eso es usando LLVM 8

Mi mal, perdón por la confusión (estaba tan feliz de reducirlo a una diferencia de una línea que ni siquiera me molesté en verificar la versión LLVM).

Preparamos nuevos artefactos 1.41.1 con la corrección LLVM seleccionada. Puede probarlos localmente con:

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

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

[triagebot] El problema se resolvió con éxito sin la participación del equipo del compilador.
Bretty bien.

1.41.1 está disponible, creo que es hora de cerrar finalmente este problema.

¿Fue útil esta página
0 / 5 - 0 calificaciones