viperproject / prusti-dev

A static verifier for Rust, based on the Viper verification infrastructure.
http://prusti.org
Other
1.52k stars 102 forks source link

Wrong encoding of signed discriminants in pure code #1501

Closed fpoli closed 4 months ago

fpoli commented 4 months ago

The following program is encoded to an assert false (more precisely, assert (2 == 1) ? true : false), but it should be assert true:

use std::cmp::Ordering;
use prusti_contracts::prusti_assert;

fn main() {
    prusti_assert!(matches!(Ordering::Greater, Ordering::Greater)); // Prusti raises a verification error, but it shouldn't
}

This is an unsoundness; it's enough to flip the boolean:

use std::cmp::Ordering;
use prusti_contracts::prusti_assert;

fn main() {
    prusti_assert!(!matches!(Ordering::Greater, Ordering::Greater)); // Prusti doesn't report a verification error, but it should
}

The following is encoded correctly, so the issue seems to be related to the prusti_assert! procedural macro:

use std::cmp::Ordering;
use prusti_contracts::prusti_assert;

fn main() {
    assert!(matches!(Ordering::Greater, Ordering::Greater)); //  Prusti doesn't report a verification error, as expected
}
fpoli commented 4 months ago

The MIR encoding of matches!(Ordering::Greater, Ordering::Greater):

MIR ![image](https://github.com/viperproject/prusti-dev/assets/4523232/ebe5f97d-6aeb-4976-a774-57d447516a76)

So, Prusti is encoding the discriminant of Ordering::Greater as 2, while the true arm of the match (on the right) expected the discriminant of Ordering::Greater to be 1.