Miri é uma ferramenta de detecção de comportamento indefinido para Rust. Ele pode executar binários e conjuntos de testes de projetos de carga e detectar códigos inseguros que não atendem aos seus requisitos de segurança. Por exemplo:
unreachable_unchecked
sendo alcançado, chamando copy_nonoverlapping
com intervalos sobrepostos, ...)bool
que não é 0 ou 1, por exemplo, ou um discriminante enum inválido) Além disso, Miri também falará sobre vazamentos de memória: quando ainda houver memória alocada no final da execução e essa memória não for acessível a partir de uma static
global, Miri gerará um erro.
Você pode usar o Miri para emular programas em outros alvos, por exemplo, para garantir que a manipulação de dados em nível de byte funcione corretamente tanto em sistemas little-endian quanto em sistemas big-endian. Veja a interpretação cruzada abaixo.
Miri já descobriu muitos bugs do mundo real. Se você encontrou um bug no Miri, agradeceríamos se você nos contasse e nós o adicionaremos à lista!
Por padrão, o Miri garante uma execução totalmente determinística e isola o programa do sistema host. Algumas APIs que normalmente acessariam o host, como coleta de entropia para geradores de números aleatórios, variáveis de ambiente e relógios, são substituídas por implementações determinísticas "falsas". Defina MIRIFLAGS="-Zmiri-disable-isolation"
para acessar as APIs reais do sistema. (Em particular, as APIs RNG do sistema "falsas" tornam o Miri não adequado para uso criptográfico ! Não gere chaves usando o Miri.)
Dito isso, esteja ciente de que Miri não detecta todas as violações da especificação Rust em seu programa, até porque não existe tal especificação. Miri usa sua própria aproximação do que é e não é comportamento indefinido no Rust. Até onde sabemos, todo Comportamento Indefinido que tenha o potencial de afetar a correção de um programa está sendo detectado pelo Miri (módulo bugs), mas você deve consultar a Referência para a definição oficial de Comportamento Indefinido. Miri será atualizado com o compilador Rust para proteção contra UB conforme é entendido pelo compilador atual, mas não faz promessas sobre versões futuras do Rustc.
Outras advertências que os usuários do Miri devem estar cientes:
-Zrandomize-layout
para detectar alguns desses casos.)-Zmiri-seed
, mas isso ainda não explorará todas as execuções possíveis.--target x86_64-unknown-linux-gnu
para obter melhor suporte.SeqCst
que não são realmente permitidas pelo modelo de memória Rust e não pode produzir todos os comportamentos possivelmente observáveis em hardware real.Além disso, Miri fundamentalmente não pode garantir que seu código seja correto . Solidez é a propriedade de nunca causar comportamento indefinido quando invocado a partir de código seguro arbitrário, mesmo em combinação com outro código de som. Em contraste, Miri pode apenas dizer se uma maneira específica de interagir com seu código (por exemplo, um conjunto de testes) causa algum comportamento indefinido em uma execução específica (da qual pode haver muitos, por exemplo, quando a simultaneidade ou outras formas de não-determinismo estão envolvidos). Quando Miri encontra UB, seu código é definitivamente incorreto, mas quando Miri não encontra UB, talvez você precise testar mais entradas ou mais opções não determinísticas possíveis.
Instale Miri no Rust todas as noites via rustup
:
rustup +nightly component add miri
Todos os comandos a seguir assumem que o conjunto de ferramentas nightly está fixado por meio de rustup override set nightly
. Alternativamente, use cargo +nightly
para cada um dos comandos a seguir.
Agora você pode executar seu projeto no Miri:
cargo miri test
.cargo miri run
.Na primeira vez que você executar o Miri, ele realizará algumas configurações extras e instalará algumas dependências. Ele pedirá confirmação antes de instalar qualquer coisa.
cargo miri run/test
suporta exatamente os mesmos sinalizadores que cargo run/test
. Por exemplo, cargo miri test filter
executa apenas os testes que contêm filter
em seu nome.
Você pode passar sinalizadores para Miri via MIRIFLAGS
. Por exemplo, MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri run
executa o programa sem verificar o alias das referências.
Ao compilar o código via cargo miri
, o sinalizador de configuração cfg(miri)
é definido para o código que será interpretado no Miri. Você pode usar isso para ignorar casos de teste que falham no Miri porque eles fazem coisas que o Miri não suporta:
# [ test ]
# [ cfg_attr ( miri , ignore ) ]
fn does_not_work_on_miri ( ) {
tokio :: run ( futures :: future :: ok :: < _ , ( ) > ( ( ) ) ) ;
}
Não há como listar todas as infinitas coisas que Miri não pode fazer, mas o intérprete lhe dirá explicitamente quando encontrar algo sem suporte:
error: unsupported operation: can't call foreign function: bind
...
= help: this is likely not a bug in the program; it indicates that the program
performed an operation that Miri does not support
Miri não só pode executar um conjunto binário ou de testes para seu destino de host, mas também pode realizar interpretação cruzada para alvos estrangeiros arbitrários: cargo miri run --target x86_64-unknown-linux-gnu
executará seu programa como se fosse um Linux programa, não importa o seu sistema operacional host. Isto é particularmente útil se você estiver usando o Windows, pois o destino Linux é muito melhor suportado do que os destinos Windows.
Você também pode usar isso para testar plataformas com propriedades diferentes da sua plataforma host. Por exemplo, cargo miri test --target s390x-unknown-linux-gnu
executará seu conjunto de testes em um destino big-endian, o que é útil para testar código sensível a endian.
Certas partes da execução são escolhidas aleatoriamente pelo Miri, como as alocações exatas de endereços base armazenadas e a intercalação de threads em execução simultânea. Às vezes, pode ser útil explorar múltiplas execuções diferentes, por exemplo, para garantir que seu código não dependa de "superalinhamento" incidental de novas alocações e para testar diferentes intercalações de threads. Isso pode ser feito com a sinalização --many-seeds
:
cargo miri test --many-seeds # tries the seeds in 0..64
cargo miri test --many-seeds=0..16
O padrão de 64 sementes diferentes é bastante lento, então você provavelmente desejará especificar um intervalo menor.
Ao executar o Miri no CI, use o seguinte snippet para instalar um conjunto de ferramentas noturno com o componente Miri:
rustup toolchain install nightly --component miri
rustup override set nightly
cargo miri test
Aqui está um exemplo de trabalho para GitHub Actions:
miri :
name : " Miri "
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v4
- name : Install Miri
run : |
rustup toolchain install nightly --component miri
rustup override set nightly
cargo miri setup
- name : Test with Miri
run : cargo miri test
A cargo miri setup
ajuda a manter limpa a saída da etapa de teste real.
Miri não oferece suporte a todos os alvos suportados pelo Rust. A boa notícia, entretanto, é que não importa o sistema operacional/plataforma do host, é fácil executar código para qualquer destino usando --target
!
Os seguintes alvos são testados em CI e, portanto, devem sempre funcionar (na medida documentada abaixo):
s390x-unknown-linux-gnu
é suportado como nosso "alvo big endian de escolha".linux
, macos
ou windows
, o Miri geralmente deve funcionar, mas não fazemos promessas e não executamos testes para tais alvos.solaris
/ illumos
: mantido por @devnexen. Suporta std::{env, thread, sync}
, mas não std::fs
.freebsd
: mantenedor procurado . Suporta std::env
e partes de std::{thread, fs}
, mas não std::sync
.android
: mantenedor procurado . Suporte muito incompleto, mas um "olá mundo" básico funciona.wasi
: mantenedor procurado . Suporte muito incompleto, nem a saída padrão funciona, mas uma função main
vazia funciona.main
.No entanto, mesmo para destinos que oferecemos suporte, o grau de suporte para acessar APIs de plataforma (como o sistema de arquivos) difere entre os destinos: geralmente, os destinos Linux têm o melhor suporte, e os destinos macOS geralmente estão no mesmo nível. O Windows é menos suportado.
Embora implemente threading Rust, o próprio Miri é um intérprete de thread único. Isso significa que ao executar cargo miri test
, você provavelmente verá um aumento dramático no tempo necessário para executar todo o seu conjunto de testes devido à lentidão inerente ao interpretador e à perda de paralelismo.
Você pode recuperar o paralelismo do seu conjunto de testes executando cargo miri nextest run -jN
(observe que você precisará cargo-nextest
instalado). Isso funciona porque cargo-nextest
coleta uma lista de todos os testes e, em seguida, lança uma cargo miri run
separada para cada teste. Você precisará especificar -j
ou --test-threads
; por padrão cargo miri nextest run
executa um teste de cada vez. Para obter mais detalhes, consulte a documentação cargo-nextest
Miri.
Nota: Este modelo de um teste por processo significa que cargo miri test
é capaz de detectar corridas de dados onde dois testes correm em um recurso compartilhado, mas cargo miri nextest run
não detectará tais corridas.
Nota: cargo-nextest
não suporta doctests, consulte nextest-rs/nextest#16
Ao usar as instruções acima, você pode encontrar vários erros confusos do compilador.
RUST_BACKTRACE=1
variável de ambiente para exibir um backtrace" Você pode ver isso ao tentar fazer com que Miri exiba um backtrace. Por padrão, Miri não expõe nenhum ambiente ao programa, portanto, executar RUST_BACKTRACE=1 cargo miri test
não fará o que você espera.
Para obter um backtrace, você precisa desabilitar o isolamento usando -Zmiri-disable-isolation
:
RUST_BACKTRACE=1 MIRIFLAGS= " -Zmiri-disable-isolation " cargo miri test
std
compilado por uma versão incompatível do Rustc" Você pode estar executando cargo miri
com uma versão de compilador diferente daquela usada para construir o libstd personalizado que Miri usa, e Miri não conseguiu detectar isso. Tente executar cargo miri clean
.
-Z
e variáveis de ambiente Miri adiciona seu próprio conjunto de sinalizadores -Z
, que geralmente são definidos por meio da variável de ambiente MIRIFLAGS
. Primeiro documentamos os sinalizadores mais relevantes e mais comumente usados:
-Zmiri-address-reuse-rate=<rate>
altera a probabilidade de que uma alocação não-pilha liberada seja adicionada ao pool para reutilização de endereço e a probabilidade de que uma nova alocação não-pilha seja retirada do pool. As alocações de pilha nunca são adicionadas ou retiradas do pool. O padrão é 0.5
.-Zmiri-address-reuse-cross-thread-rate=<rate>
altera a probabilidade de que uma alocação que tenta reutilizar um bloco de memória liberado anteriormente também considerará blocos liberados por outros threads . O padrão é 0.1
, o que significa que por padrão, em 90% dos casos em que for feita uma tentativa de reutilização de endereço, apenas endereços do mesmo thread serão considerados. A reutilização de um endereço de outro thread induz a sincronização entre esses threads, o que pode mascarar corridas de dados e erros de memória fraca.-Zmiri-compare-exchange-weak-failure-rate=<rate>
altera a taxa de falha das operações compare_exchange_weak
. O padrão é 0.8
(portanto, 4 em cada 5 operações fracas falharão). Você pode alterá-lo para qualquer valor entre 0.0
e 1.0
, onde 1.0
significa que sempre falhará e 0.0
significa que nunca falhará. Observe que defini-lo como 1.0
provavelmente causará travamentos, pois significa que os programas que usam compare_exchange_weak
não podem progredir.-Zmiri-disable-isolation
desativa o isolamento do host. Como consequência, o programa tem acesso a recursos do host, como variáveis de ambiente, sistemas de arquivos e aleatoriedade.-Zmiri-disable-leak-backtraces
desativa relatórios de backtraces para vazamentos de memória. Por padrão, um backtrace é capturado para cada alocação quando ela é criada, caso haja vazamento. Isso gera alguma sobrecarga de memória para armazenar dados que quase nunca são usados. Este sinalizador está implícito em -Zmiri-ignore-leaks
.-Zmiri-env-forward=<var>
encaminha a variável de ambiente var
para o programa interpretado. Pode ser usado várias vezes para encaminhar diversas variáveis. A execução ainda será determinística se o valor das variáveis encaminhadas permanecer o mesmo. Não tem efeito se -Zmiri-disable-isolation
estiver definido.-Zmiri-env-set=<var>=<value>
define a variável de ambiente var
como value
no programa interpretado. Pode ser usado para passar variáveis de ambiente sem a necessidade de alterar o ambiente host. Pode ser usado várias vezes para definir diversas variáveis. Se -Zmiri-disable-isolation
ou -Zmiri-env-forward
estiver definido, os valores definidos com esta opção terão prioridade sobre os valores do ambiente host.-Zmiri-ignore-leaks
desativa o verificador de vazamento de memória e também permite que alguns threads restantes existam quando o thread principal é encerrado.-Zmiri-isolation-error=<action>
configura a resposta da Miri às operações que exigem acesso ao host enquanto o isolamento está habilitado. abort
, hide
, warn
e warn-nobacktrace
são as ações suportadas. O padrão é abort
, o que interrompe a máquina. Algumas operações (mas não todas) também suportam a execução contínua com um erro de "permissão negada" sendo retornado ao programa. warn
imprime um backtrace completo cada vez que isso acontece; warn-nobacktrace
é menos detalhado e mostrado no máximo uma vez por operação. hide
oculta totalmente o aviso.-Zmiri-num-cpus
indica o número de CPUs disponíveis a serem relatadas pelo miri. Por padrão, o número de CPUs disponíveis é 1
. Observe que esse sinalizador não afeta como o miri lida com threads de forma alguma.-Zmiri-permissive-provenance
desativa o aviso para conversões de número inteiro para ponteiro e ptr::with_exposed_provenance
. Isso necessariamente deixará passar alguns bugs, pois essas operações não são implementáveis de maneira eficiente e precisa em um sanitizador, mas só deixará passar bugs que dizem respeito à memória/ponteiros que estão sujeitos a essas operações.-Zmiri-preemption-rate
configura a probabilidade de que, no final de um bloco básico, o thread ativo seja preemptado. O padrão é 0.01
(ou seja, 1%). Definir isso como 0
desativa a preempção.-Zmiri-report-progress
faz com que Miri imprima o stacktrace atual de vez em quando, para que você possa saber o que ele está fazendo quando um programa continua em execução. Você pode personalizar a frequência com que o relatório é impresso por meio de -Zmiri-report-progress=<blocks>
, que imprime o relatório a cada N blocos básicos.-Zmiri-seed=<num>
configura a semente do RNG que Miri usa para resolver o não determinismo. Este RNG é usado para escolher endereços base para alocações, para determinar preempção e falha de compare_exchange_weak
e para controlar o buffer de armazenamento para emulação de memória fraca. Quando o isolamento está habilitado (o padrão), também é usado para emular a entropia do sistema. A semente padrão é 0. Você pode aumentar a cobertura do teste executando o Miri várias vezes com sementes diferentes.-Zmiri-strict-provenance
permite uma verificação rigorosa da proveniência em Miri. Isto significa que converter um número inteiro para um ponteiro produz um resultado com proveniência 'inválida', ou seja, com proveniência que não pode ser usada para qualquer acesso à memória.-Zmiri-symbolic-alignment-check
torna a verificação de alinhamento mais rigorosa. Por padrão, o alinhamento é verificado convertendo o ponteiro para um número inteiro e certificando-se de que seja um múltiplo do alinhamento. Isso pode levar a casos em que um programa passa na verificação de alinhamento por puro acaso, porque as coisas "aconteceram estar" suficientemente alinhadas - não há UB nesta execução, mas haveria UB em outras. Para evitar tais casos, a verificação do alinhamento simbólico leva em consideração apenas o alinhamento solicitado da alocação relevante e o deslocamento nessa alocação. Isso evita a falta de tais bugs, mas também incorre em alguns falsos positivos quando o código faz aritmética manual de números inteiros para garantir o alinhamento. (O método align_to
da biblioteca padrão funciona bem em ambos os modos; no alinhamento simbólico, ele apenas preenche a fatia intermediária quando a alocação garante alinhamento suficiente.)Os sinalizadores restantes são apenas para uso avançado e têm maior probabilidade de serem alterados ou removidos. Alguns deles não são sólidos, o que significa que podem fazer com que Miri não consiga detectar casos de comportamento indefinido em um programa.
-Zmiri-disable-alignment-check
desativa a verificação do alinhamento do ponteiro, para que você possa se concentrar em outras falhas, mas isso significa que Miri pode perder bugs em seu programa. Usar esse sinalizador não é correto .-Zmiri-disable-data-race-detector
desativa a verificação de corridas de dados. Usar esse sinalizador não é correto . Isso implica -Zmiri-disable-weak-memory-emulation
.-Zmiri-disable-stacked-borrows
desativa a verificação das regras de alias experimentais para rastrear empréstimos (empréstimos empilhados e empréstimos em árvore). Isso pode fazer o Miri rodar mais rápido, mas também significa que nenhuma violação de alias será detectada. Usar este sinalizador não é correto (mas as regras de integridade afetadas são experimentais). Os sinalizadores posteriores têm precedência: o rastreamento de empréstimo pode ser reativado por -Zmiri-tree-borrows
.-Zmiri-disable-validation
desativa a aplicação de invariantes de validade, que são aplicadas por padrão. Isso é mais útil para focar primeiro em outras falhas (como acessos fora dos limites). Definir este sinalizador significa que Miri pode perder bugs em seu programa. No entanto, isso também pode ajudar a Miri a correr mais rápido. Usar esse sinalizador não é correto .-Zmiri-disable-weak-memory-emulation
desativa a emulação de alguns efeitos de memória fraca do C++ 11.-Zmiri-native-lib=<path to a shared object file>
é um sinalizador experimental para fornecer suporte para chamar funções nativas de dentro do interpretador via FFI. Funções não fornecidas por esse arquivo ainda são executadas através dos calços Miri usuais. AVISO : Se um arquivo .so
inválido/incorreto for especificado, isso pode causar comportamento indefinido no próprio Miri! E, claro, Miri não pode verificar as ações executadas pelo código nativo. Observe que o Miri possui seu próprio manuseio de descritores de arquivo, portanto, se você quiser substituir algumas funções que funcionam em descritores de arquivo, você terá que substituir todas elas, ou os dois tipos de descritores de arquivo serão confundidos. Este é um trabalho em andamento ; atualmente, apenas argumentos inteiros e valores de retorno são suportados (e não, conversões de ponteiro/número inteiro para contornar essa limitação não funcionarão; elas falharão terrivelmente). Por enquanto, também só funciona em hosts Unix.-Zmiri-measureme=<name>
habilita o perfil de measureme
para o programa interpretado. Isso pode ser usado para descobrir quais partes do seu programa estão sendo executadas lentamente no Miri. O perfil é gravado em um arquivo dentro de um diretório chamado <name>
e pode ser processado usando as ferramentas do repositório https://github.com/rust-lang/measureme.-Zmiri-mute-stdout-stderr
ignora silenciosamente todas as gravações em stdout e stderr, mas informa ao programa que ele realmente gravou. Isso é útil quando você não está interessado na saída real do programa, mas deseja apenas ver os erros e avisos da Miri.-Zmiri-recursive-validation
é um sinalizador altamente experimental que torna a verificação de validade recursiva abaixo das referências.-Zmiri-retag-fields[=<all|none|scalar>]
controla quando Stacked Borrows remarcação recursiva em campos. all
significa que sempre é recursivo (o padrão e equivalente a -Zmiri-retag-fields
sem um valor explícito), none
significa que nunca é recursivo, scalar
significa que é recursivo apenas para tipos onde também emitiríamos anotações noalias
no LLVM IR gerado ( tipos passados como escalares individuais ou pares de escalares). Definir isso como none
não é correto .-Zmiri-provenance-gc=<blocks>
configura a frequência com que o coletor de lixo de origem do ponteiro é executado. O padrão é procurar e remover proveniência inacessível uma vez a cada 10000
blocos básicos. Definir como 0
desativa o coletor de lixo, o que faz com que alguns programas tenham uso explosivo de memória e/ou tempo de execução superlinear.-Zmiri-track-alloc-accesses
mostra não apenas alocação e eventos gratuitos para alocações rastreadas, mas também leituras e gravações.-Zmiri-track-alloc-id=<id1>,<id2>,...
mostra um backtrace quando as alocações fornecidas estão sendo alocadas ou liberadas. Isso ajuda na depuração de vazamentos de memória e no uso após erros gratuitos. Especificar este argumento várias vezes não substitui os valores anteriores; em vez disso, anexa seus valores à lista. Listar um ID várias vezes não tem efeito.-Zmiri-track-pointer-tag=<tag1>,<tag2>,...
mostra um backtrace quando uma determinada tag de ponteiro é criada e quando (se alguma vez) ela é retirada de uma pilha de empréstimo (que é onde a tag se torna inválido e qualquer uso futuro dele causará erro). Isso ajuda você a descobrir por que o UB está acontecendo e onde no seu código seria um bom lugar para procurá-lo. Especificar este argumento várias vezes não substitui os valores anteriores; em vez disso, anexa seus valores à lista. Listar uma tag várias vezes não tem efeito.-Zmiri-track-weak-memory-loads
mostra um backtrace quando a emulação de memória fraca retorna um valor desatualizado de uma carga. Isso pode ajudar a diagnosticar problemas que desaparecem em -Zmiri-disable-weak-memory-emulation
.-Zmiri-tree-borrows
substitui Stacked Borrows pelas regras de Tree Borrows. Tree Borrows é ainda mais experimental do que Stacked Borrows. Embora o Tree Borrows ainda seja sólido no sentido de capturar todas as violações de alias que as versões atuais do compilador podem explorar, é provável que o eventual modelo de alias final do Rust seja mais rigoroso do que o Tree Borrows. Em outras palavras, se você usar Tree Borrows, mesmo que seu código seja aceito hoje, ele poderá ser declarado UB no futuro. Isso é muito menos provável com Stacked Borrows.-Zmiri-force-page-size=<num>
substitui o tamanho de página padrão para uma arquitetura, em múltiplos de 1k. 4
é o padrão para a maioria dos destinos. Este valor deve ser sempre uma potência de 2 e diferente de zero.-Zmiri-unique-is-unique
executa verificações adicionais de alias para core::ptr::Unique
para garantir que teoricamente possa ser considerado noalias
. Este sinalizador é experimental e tem efeito apenas quando usado com -Zmiri-tree-borrows
. Alguns sinalizadores nativos Rustc -Z
também são muito relevantes para Miri:
-Zmir-opt-level
controla quantas otimizações MIR são executadas. Miri substitui o padrão por 0
; esteja ciente de que usar qualquer nível superior pode fazer com que Miri perca bugs em seu programa porque eles foram otimizados.-Zalways-encode-mir
faz o Rustc despejar o MIR mesmo para funções completamente monomórficas. Isso é necessário para que Miri possa executar tais funções, então Miri define esse sinalizador por padrão.-Zmir-emit-retag
controla se as instruções Retag
são emitidas. Miri habilita isso por padrão porque é necessário para empréstimos empilhados e empréstimos em árvore.Além disso, Miri reconhece algumas variáveis de ambiente:
MIRIFLAGS
define sinalizadores extras a serem passados para Miri.MIRI_LIB_SRC
define o diretório onde Miri espera as fontes da biblioteca padrão que irá construir e usar para interpretação. Este diretório deve apontar para o subdiretório library
de uma verificação do repositório rust-lang/rust
.MIRI_SYSROOT
indica o sysroot a ser usado. Ao usar cargo miri test
/ cargo miri run
, isso ignora a configuração automática - defina isso apenas se não quiser usar o sysroot criado automaticamente. Ao invocar cargo miri setup
, isso indica onde o sysroot será colocado.MIRI_NO_STD
garante que o sysroot do destino seja construído sem libstd. Isso permite testar e executar programas no_std. Isto normalmente não deve ser usado ; Miri tem uma heurística para detectar alvos sem padrão com base no nome do alvo. Definir isso em um destino que suporte libstd pode levar a resultados confusos. extern
Miri Miri fornece algumas funções extern
que os programas podem importar para acessar funcionalidades específicas do Miri. Eles são declarados em /tests/utils/miri_extern.rs.
Espera-se que os binários que não usam a biblioteca padrão declarem uma função como esta para que Miri saiba onde deve iniciar a execução:
# [ cfg ( miri ) ]
# [ no_mangle ]
fn miri_start ( argc : isize , argv : * const * const u8 ) -> isize {
// Call the actual start function that your project implements, based on your target's conventions.
}
Se você quiser contribuir com a Miri, ótimo! Por favor, verifique nosso guia de contribuição.
Para obter ajuda com a execução do Miri, você pode abrir um problema aqui no GitHub ou usar o stream do Miri no Rust Zulip.
Este projeto começou como parte de um curso de graduação em 2015 por @solson na Universidade de Saskatchewan. Existem slides e um relatório desse projeto disponíveis. Em 2016, @oli-obk se juntou para preparar Miri para eventualmente ser usado como avaliador const no próprio compilador Rust (basicamente, para coisas const
e static
), substituindo o antigo avaliador que trabalhava diretamente no AST. Em 2017, @RalfJung fez um estágio na Mozilla e começou a desenvolver o Miri para uma ferramenta de detecção de comportamento indefinido, e também a usar o Miri como forma de explorar as consequências de várias definições possíveis para comportamento indefinido no Rust. A mudança do mecanismo Miri para o compilador por @oli-obk finalmente foi concluída no início de 2018. Enquanto isso, no final daquele ano, @RalfJung fez um segundo estágio, desenvolvendo ainda mais o Miri com suporte para verificação de invariantes de tipo básico e verificação de que as referências são usadas de acordo às suas restrições de alias.
Miri já encontrou vários bugs na biblioteca padrão do Rust e além, alguns dos quais coletamos aqui. Se Miri ajudou você a encontrar um bug sutil de UB em seu código, agradeceríamos um PR adicionando-o à lista!
Erros definitivos encontrados:
Debug for vec_deque::Iter
acessando memória não inicializadaVec::into_iter
fazendo uma leitura ZST desalinhadaFrom<&[T]> for Rc
criando uma referência não suficientemente alinhadaBTreeMap
criando uma referência compartilhada apontando para uma alocação muito pequenaVec::append
criando uma referência pendentestr
transformando uma referência compartilhada em mutávelrand
realizando leituras desalinhadasposix_memalign
de forma inválidagetrandom
chamando o syscall getrandom
de forma inválidaVec
e BTreeMap
vazando memória sob algumas condições (de pânico)beef
vazando memóriaEbrCell
usando memória não inicializada incorretamenteservo_arc
criando uma referência compartilhada pendenteencoding_rs
fazendo aritmética de ponteiro fora dos limitesVec::from_raw_parts
incorretamenteAtomicPtr
e Box::from_raw_in
ThinVec
crossbeam-epoch
chamando assume_init
em um MaybeUninit
parcialmente inicializadointeger-encoding
desreferenciando um ponteiro desalinhadorkyv
construindo um Box<[u8]>
a partir de uma alocação sobrealinhadaarc-swap
thread::scope
regex
manipulando incorretamente buffers Vec<u8>
desalinhadoscompare_exchange_weak
em once_cell
vec::IntoIter
Iterator::collect
in-locoportable-atomic-util
std::mpsc
(código original em crossbeam)Descobrimos violações de Stacked Borrows que são prováveis bugs (mas Stacked Borrows é atualmente apenas um experimento):
VecDeque::drain
criando referências mutáveis sobrepostasBTreeMap
BTreeMap
criando referências mutáveis que se sobrepõem a referências compartilhadasBTreeMap::iter_mut
criando referências mutáveis sobrepostasBTreeMap
usando ponteiros brutos fora de sua área de memória válidaLinkedList
criando referências mutáveis sobrepostasVec::push
invalidando referências existentes no vetoralign_to_mut
violando a exclusividade de referências mutáveissized-chunks
criando referências mutáveis de aliasString::push_str
invalidando referências existentes na stringryu
usando ponteiros brutos fora de sua área de memória válidaEnv
usando um ponteiro bruto fora de sua área de memória válidaVecDeque::iter_mut
criando referências mutáveis sobrepostas<[T]>::copy_within
usando um empréstimo após invalidá-lo Licenciado sob qualquer um dos
a sua opção.
A menos que você declare explicitamente o contrário, qualquer contribuição enviada intencionalmente para inclusão no trabalho por você deverá ser licenciada duplamente conforme acima, sem quaisquer termos ou condições adicionais.