Tecnologia de verificação em Rust aplicada a código de sistemas de baixo nível
(github.com/verus-lang)- Verus é uma ferramenta para verificar a correção de código escrito em Rust: quando o desenvolvedor especifica o que o código deve fazer, ela verifica estaticamente se o código Rust executável satisfaz essa especificação em todas as execuções possíveis
- Em vez de adicionar verificações em tempo de execução, ela usa um solver poderoso para provar que o código está correto; atualmente, oferece suporte apenas a uma parte do Rust
- Em alguns casos, consegue verificar estaticamente a correção de código que manipula raw pointers, indo além do sistema de tipos padrão do Rust
- O projeto está em desenvolvimento ativo, e recursos podem estar quebrados ou ausentes; a documentação ainda não está completa, então usuários devem estar preparados para pedir ajuda no Zulip
- Verus Playground para navegador, instruções de instalação, tutorial e referência, documentação da API da biblioteca padrão, guia de verificação de código concorrente, exemplos e testes são oferecidos como caminhos para aprendizado e experimentação
O que o Verus verifica
- Verus é uma ferramenta para verificar a correção de código Rust
- O desenvolvedor escreve, como uma especificação, o comportamento que o código deve realizar
- O Verus verifica estaticamente se o código Rust executável sempre satisfaz essa especificação em todas as execuções possíveis
- Em vez de adicionar verificações em tempo de execução, ele usa um solver para provar que o código está correto
- A cobertura atual é um subconjunto do Rust, e há trabalho em andamento para ampliar essa cobertura
- Em alguns casos, ele consegue verificar estaticamente a correção de código além do sistema de tipos padrão do Rust, por exemplo código que manipula raw pointers
Estado de desenvolvimento e cuidados ao usar
- O Verus é um projeto em desenvolvimento ativo
- Recursos podem estar quebrados ou ausentes
- A documentação ainda não está completa
- Para experimentar o Verus, é preciso estar preparado para pedir ajuda no Zulip
- A comunidade do Verus publicou vários artigos de pesquisa, e diversos projetos na indústria e na academia usam o Verus
- A lista relacionada pode ser conferida na página publications and projects
Como começar e ferramentas de desenvolvimento
- Para experimentar o Verus no navegador, é possível usar o Verus Playground
- Para um desenvolvimento mais sério, é necessário seguir as instruções de instalação
- O aprendizado pode começar em Tutorial and reference
- Também há suporte ao formatador automático verusfmt para código Verus
Documentação e materiais de aprendizado
- Os recursos de documentação em andamento incluem:
- Tutorial and reference: tutorial e referência do Verus
- API documentation for Verus's standard library: documentação da API da biblioteca padrão do Verus
- Guide for verifying concurrent code: guia de verificação de código concorrente
- Contributing to Verus
- Best Practices para publicar crates relacionados ao Verus no crates.io
- Verus License
- Verus Logos
Exemplos e participação na comunidade
- Exemplos de uso do Verus também oferecem vários pontos de partida além da documentação
- Publications and projects: publicações e projetos que usam o Verus
- Videos, slides, and exercises: vídeos, slides e exercícios de um tutorial de um dia sobre Verus
- Standalone examples: exemplos independentes de uso do Verus em tarefas pequenas e concretas
- Small and medium-sized examples: exemplos que demonstram diversos recursos do Verus
- Unit tests: testes com exemplos da sintaxe e dos recursos do Verus
- Relatos de issues e discussões podem ser feitos no GitHub ou no Zulip
- O projeto usa GitHub discussions para solicitações de recursos e conversas abertas, e mantém bugs executáveis de recursos existentes em GitHub issues
- Quem quiser contribuir com código pode consultar as instruções de Contributing to Verus
1 comentários
Opiniões no Hacker News
Escrevi um controlador do Kubernetes formalmente verificado com Verus
Basicamente, é possível provar propriedades de vivacidade como “em algum momento, o controlador reconcilia o cluster para o estado-alvo solicitado”
Mas, quando se considera que o estado-alvo muda rapidamente, assincronicidade, falhas etc., a própria especificação do que é “correto” tem muitas sutilezas
Código: https://github.com/vmware-research/verifiable-controllers/, e o artigo relacionado deve aparecer no OSDI 2024
Como um pequeno passo rumo ao Verus, dá para colocar debug_assert do Rust em pré-condições e pós-condições
O compilador Rust, por padrão, remove isso em builds de produção
Nos exemplos de verificação do tutorial do Verus, escrevem-se o intervalo de entrada e as condições do resultado com
requireseensures; na versão com checagens em tempo de execução, as mesmas condições são verificadas durante a execução, como emdebug_assert(-16 <= x1)edebug_assert(x8 == 8 * x1)Outras ferramentas de prova/verificação/contratos em Rust, como Creusot, usam uma sintaxe baseada em atributos, que em geral parece mais leve e mais “Rust”
Seria bom se esse estilo também se tornasse possível em futuras versões do Verus
É uma ótima ferramenta de documentação e complementa muito bem o sistema de tipos e os testes
"contracts": https://docs.rs/contracts/latest/contracts/Coloco pré-condições e pós-condições na maioria das funções, e a JVM tem uma flag que permite removê-las facilmente em builds de produção
Como alguém que não tem muita experiência real em ciência da computação, fiquei curioso: no README, qual é a diferença entre verificação, em “verificar a correção do código”, e “prova”, como se diz em outros lugares?
Também queria materiais bons para um programador profissional sem uma formação forte em ciência da computação/matemática aprender sobre “provas” aplicadas a código
Além disso, não entendo muito bem por que provas de conhecimento zero são tão importantes e relevantes. Por exemplo, ouvi falar de coisas como x.com/ZorpZK, mas não entendo por que isso é legal
Mas o Coq usado no Verus e em Software Foundations tem abordagens diferentes
O Verus tenta provar propriedades automaticamente usando um sistema automático de resolução de restrições chamado solucionador SMT, enquanto no Coq é preciso provar muito mais coisas manualmente, com automação limitada
Ambos têm prós e contras; a automação é ótima quando funciona, mas frustrante quando não funciona
Provas de conhecimento zero devem ser vistas como uma área um pouco diferente, e muita gente que trabalha com verificação/provas formais nem mexe com elas. É melhor pensar nelas como uma primitiva criptográfica
Provas de conhecimento zero têm overhead alto e carecem do chamado “killer app”, então sua utilidade prática, importância e relevância ainda não são grandes, mas conceitualmente são interessantes
Também gostaria de ter bons materiais de estudo. A documentação do Dafny é bem boa, mas verificação formal de software ainda não parece estar em um estágio bom para programadores comuns que não têm doutorado em ciência da computação/matemática
Olhando só os exemplos, parece relativamente fácil, mas logo você esbarra em “não foi possível provar”, e a resposta para o porquê costuma entrar em detalhes profundos de implementação que só o autor parece saber
Por exemplo, é possível verificar que você sabe a senha sem enviá-la ao servidor, dificultando que um servidor malicioso ou um atacante man-in-the-middle espione a senha
Também podem oferecer opções melhores para verificação de identidade. Você pode provar que possui um documento de identidade emitido pelo governo sem entregar o documento em si ao servidor, reduzindo situações em que ele é “armazenado por no máximo 2 anos/3 anos/6 meses” e acaba vazando
Provas sobre código ainda não são algo feito por programadores profissionais
Lógica de Hoare é um bom ponto de partida e às vezes é ensinada em cursos introdutórios de ciência da computação
Coq tem uma curva de aprendizado íngreme, especialmente se você não estiver familiarizado com OCaml ou linguagens parecidas. Why3 talvez seja mais amigável para iniciantes: https://www.why3.org
Prova e verificação podem significar a mesma coisa, mas prova dá mais uma sensação de algo interativo, enquanto verificação dá a sensação de algo que pode ser automatizado, como model checking ou resolução SMT de programas anotados
Para quem não conhecia projetos parecidos, Dafny é uma “linguagem de programação consciente de verificação” que pode ser compilada para Rust: https://github.com/dafny-lang/dafny
Alguns dias atrás, escrevi um texto introdutório para iniciantes em Dafny: https://www.linkedin.com/pulse/getting-started-dafny-your-fi...
Parece muito legal. Acho que seria útil para as pessoas ter orientações ou exemplos de como adicionar provas a uma base de código existente
Por exemplo, imagine um app GUI mínimo com apenas uma caixa de texto que recebe, via requisição HTTP, um array não confiável e desconhecido em tempo de compilação, faz bubble sort nele e depois o exibe
O bubble sort tem um bug intencional, como um erro off-by-one que deixa o último elemento inalterado, e os testes unitários por acaso não pegam esse bug. A preocupação de que os testes estejam incompletos pode ser a principal motivação para partir para provas
Então seria bom mostrar o processo de substituir os testes unitários por provas, descobrir o bug e corrigi-lo
Não é preciso explicar em detalhes o próprio código de prova; basta focar em detalhes práticos, como a fronteira entre o código matemático provado e o código de entrada/saída não provado, as linhas de comando usadas para provar e compilar, e um arquivo zip para a pessoa mexer
Na verdade, talvez só ler da entrada padrão e escrever na saída padrão já seja suficiente
Um dos principais contribuidores fez uma excelente apresentação sobre Verus no meetup de Rust em Zürich: https://www.youtube.com/watch?v=ZZTk-zS4ZCY
Achei impressionante como esse código “ghost” se encaixa de forma limpa no programa, e me lembrou um pouco Ada
Fico me perguntando se Rust já tem um padrão como C/C++, Common Lisp e Ada/SPARK2014
Se não tiver, isso vira um alvo em movimento em comparação com as ferramentas de verificação desenvolvidas para Ada/SPARK2014
O legado de Ada/SPARK2014, que vai de bare metal a aplicações críticas de segurança de alta integridade, também é difícil de ignorar
Você está falando disso?
Fico curioso sobre qual é a relação entre isso e o Kani. Eles funcionam de formas diferentes?
https://github.com/model-checking/kani
Verificadores baseados em SMT automático, como Verus, Dafny, F* e o meu VCC, exigem anotações em quase todas as funções e loops, mas oferecem garantias mais amplas sobre a correção do programa
Ferramentas baseadas em provadores interativos, como Coq ou Lean, geralmente precisam de mais orientação do usuário, mas conseguem garantir propriedades mais complexas
Fico me perguntando como Verus se compara ao SPARK
É o mesmo tipo geral de verificador? Tirando o fato de ser um verificador para Rust em vez de Ada, em que Verus é diferente?
Seria bom se alguém que conhece bem Verus pudesse explicar as diferenças de desempenho e expressividade entre Verus e Lean4
Entendo que Verus é uma ferramenta de verificação baseada em SMT, enquanto Lean é um provador interativo e também uma ferramenta baseada em SMT
Mas meu entendimento da área de verificação formal é limitado, então gostaria de ouvir a visão de alguém que conhece bem métodos formais de software
Por exemplo, assim como no livro “Software Foundations” de Coq, é possível formular e provar proposições sobre código C, mas parece que quase ninguém faz isso com Lean, e o ferramental é insuficiente
Também é possível escrever programas em Lean4 e provar coisas sobre esses programas, e há algumas pessoas fazendo isso aos poucos
Formalizar matemática pura e publicar artigos sobre isso é hoje a principal forma de uso de Lean4 e Coq
O tipo de coisa que Lean/Coq conseguem de fato enunciar e provar é mais geral, mas talvez programas do mundo real não precisem necessariamente de tanta generalidade