>_ DevTrendsnl

Taal

Home

Talen

Secties

Frontend Backend Mobiel DevOps AI / ML GameDev Blockchain Beveiliging
Rust

Miri: je persoonlijke detective voor ongedefinieerd gedrag in Rust

6.379 sterren

Schrijf je in Rust en ben je zelfverzekerd over de veiligheid van je code? Natuurlijk, want Rust staat bekend om zijn typesysteem en geheugenveiligheidsgaranties. Maar wat als ik je vertel dat je zelfs in Rust ongedefinieerd gedrag (UB) kunt tegenkomen, dat kan leiden tot onvoorspelbare crashes, kwetsbaarheden of gewoon heel vreemd programmagedrag? Ja, unsafe-blokken zijn een krachtig hulpmiddel dat ongelooflijke flexibiliteit en prestaties biedt, maar het brengt ook grote verantwoordelijkheid met zich mee. Dit is precies waar Miri in beeld komt — een onmisbare helper voor elke Rust-ontwikkelaar.

Wat is Miri en waarom heb je het nodig?

Stel je voor dat je een superdetective hebt die in je programma kan kijken en alle verborgen valstrikken kan vinden die kunnen leiden tot ongedefinieerd gedrag. Miri is precies zo'n detective. Het is een tool voor het detecteren van ongedefinieerd gedrag in Rust die werkt als een interpreter voor Rust's intermediate representation (MIR). Het voert de binaries en testsuites van je project uit cargo en identificeert unsafe code die niet voldoet aan Rust's veiligheidsvereisten.

Wie heeft dit nodig? Als je met unsafe code werkt, low-level abstracties schrijft, bibliotheken maakt die zo betrouwbaar mogelijk moeten zijn, of gewoon absoluut zeker wilt zijn van je Rust-applicatie, wordt Miri je beste vriend. Het helpt om die fouten op te sporen die de compiler zal missen en die reguliere tests niet dekken, omdat ze zich alleen onder specifieke omstandigheden of op bepaalde platforms kunnen manifesteren.

Miri in actie: welke problemen detecteert het?

Miri kijkt niet alleen naar fouten, het identificeert specifiek concrete soorten ongedefinieerd gedrag die de stabiliteit van je applicatie kunnen ondermijnen. Hier zijn er slechts enkele van:

  • Geheugentoegangs fouten: Dit is een klassieker. Miri detecteert als je buiten de grenzen van toegewezen geheugen gaat (out-of-bounds access) of geheugen probeert te gebruiken nadat het is vrijgegeven (use-after-free). Herkenbaar situatie wanneer het programma crasht op de meest onverwachte plaatsen? Miri helpt de oorzaak van het probleem te vinden.
  • Incorrect gebruik van niet-geïnitialiseerde data: Proberen om data te lezen uit geheugen dat niet is geïnitialiseerd, is een directe weg naar UB. Miri houdt hier scherp toezicht op.
  • Type-invariant overtredingen: Rust controleert strikt op types, maar in unsafe-blokken kun je zelfs basis invarianten schenden, bijvoorbeeld een bool maken die niet 0 of 1 is, of een enum met een ongeldige discriminant. Miri vangt dit ook op.
  • Geheugenuitlijning problemen: Toegang tot geheugen met onvoldoende uitlijning kan leiden tot fouten op sommige architecturen. Miri controleert of al je pointers correct zijn uitgelijnd.
  • Data races en zwak geheugenmodel: Concurrency is moeilijk. Miri kan sommige zwakke geheugeneffecten emuleren en data races detecteren die extreem moeilijk te reproduceren zijn op echte hardware. Dit is vooral waardevol voor multithreaded applicaties.
  • Geheugenlekken: Aan het einde van de programma-uitvoering controleert Miri of er toegewezen geheugen is dat niet langer toegankelijk is. Dit is een geweldige manier om lekken te vinden die langzaam maar zeker de resources van je server kunnen "opeten".
  • Experimentele aliasing-controles: Miri bevat ook experimentele controles voor Stacked Borrows en Tree Borrows regels, die helpen bij het identificeren van subtielere aliasing-regelovertredingen voor referentietypes. Dit zijn geavanceerde onderzoeksinspanningen die al helpen om Rust nog veiliger te maken.

Hoe Miri het doet: een diepgaande blik op de interpreter

In tegenstelling tot statische analyzers scant Miri je code niet alleen. Het voert het uit in zijn eigen "sandbox" — als een interpreter voor Rust's intermediate representation (MIR). Dit stelt het in staat om programmagedrag in realtime te observeren en problemen te identificeren die zich alleen tijdens de uitvoering manifesteren.

Deterministische uitvoering: Standaard zorgt Miri voor volledig deterministische uitvoering. Dit betekent dat als je het programma met dezelfde invoergegevens uitvoert, het resultaat altijd identiek zal zijn. Hoe wordt dit bereikt? Miri vervangt veel systeem-API's (bijvoorbeeld random number generators, omgevingsvariabelen, klokken) door zijn eigen "valse" implementaties. Dit is cruciaal belangrijk voor het reproduceren en debuggen van bugs die anders willekeurig zouden kunnen optreden.

Cross-interpretatie: Interessant is dat Miri je code voor verschillende target platforms kan uitvoeren, zelfs als je host-systeem anders is. Je kunt bijvoorbeeld tests uitvoeren op Windows, maar Miri zal ze interpreteren alsof ze op Linux draaien (--target x86_64-unknown-linux-gnu). Dit is vooral nuttig voor het testen van platform-afhankelijke code, bijvoorbeeld om de correctheid van byte-manipulaties op little-endian en big-endian systemen te verifiëren.

Aan de slag met Miri: gemakkelijker dan het lijkt

Miri integreren in je workflow is vrij eenvoudig, vooral als je al rustup en cargo gebruikt.

Installatie

Om Miri te installeren heb je een nightly versie van Rust nodig. Voer simpelweg uit:

rustup +nightly component add miri

Tests en binaries uitvoeren

Na de installatie kun je je projecten door Miri uitvoeren met vertrouwde cargo-commando's:

  • Om alle projecttests door Miri uit te voeren: cargo miri test
  • Als je een binair project hebt, voer het dan door Miri uit: cargo miri run

Bij de eerste uitvoering zal Miri aanvullende setup uitvoeren en noodzakelijke afhankelijkheden installeren, waarbij om je bevestiging wordt gevraagd.

Trouwens, cargo miri run/test ondersteunt dezelfde flags als reguliere cargo run/test. Bijvoorbeeld, cargo miri test filter voert alleen tests uit die filter in de naam bevatten.

Geavanceerde functies met Miri flags

Je kunt speciale Miri flags doorgeven via de MIRIFLAGS omgevingsvariabele. Bijvoorbeeld, om isolatie van het host-systeem uit te schakelen (en toegang te krijgen tot echte systeem-API's, maar determinisme te verliezen):

RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test

Of, om verschillende niet-deterministische gedragsscenario's te testen (bijvoorbeeld verschillende thread-interleavings of allocatie-adressen):

MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test

Dit is een zeer krachtige functie voor het identificeren van moeilijk te vangen bugs die zich alleen onder bepaalde omstandigheden manifesteren.

CI/CD-integratie

Miri is geweldig voor geautomatiseerde controles in CI. Hier is een voorbeeld van hoe je een job voor GitHub Actions instelt:

  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

Tests negeren die incompatibel zijn met Miri

Soms gebruikt je code mogelijk API's die Miri nog niet ondersteunt (bijvoorbeeld netwerkoperaties). In zulke gevallen kun je deze tests simpelweg negeren voor Miri:

#[test]
#[cfg_attr(miri, ignore)]
fn does_not_work_on_miri() {
    // Ваш код, который Miri не поддерживает
}

Miri redt al de wereld (en je code)

Het meest indrukwekkende aan Miri is zijn bewezen effectiviteit. Het heeft al veel echte bugs ontdekt in de Rust standard library en populaire crates. Daaronder waren fouten gerelateerd aan toegang tot niet-geïnitialiseerd geheugen in VecDeque, incorrecte uitlijning in Vec, geheugenlekken in BTreeMap, en zelfs data races in std::mpsc-kanalen. Dit is niet zomaar een academische tool, maar een bewezen helper die actief wordt gebruikt om de betrouwbaarheid van het Rust-ecosysteem te verbeteren.

Conclusies: is Miri de moeite waard om te proberen?

Absoluut! Miri is geen wondermiddel, en het zal niet alle mogelijke Rust-specificatie-overtredingen vangen (omdat zo'n uniforme specificatie eigenlijk niet bestaat). Het is echter een onmisbaar hulpmiddel voor het verbeteren van de betrouwbaarheid van Rust-code, vooral in unsafe-secties en bij het werken met concurrency.

Als je de kwaliteit van je Rust-code serieus neemt, risico's gerelateerd aan ongedefinieerd gedrag wilt minimaliseren, en zeker wilt zijn van de stabiliteit van je applicaties, zou Miri in je arsenaal moeten zitten. Het helpt je die verraderlijke bugs te vinden en op te lossen die diep in je code verborgen kunnen zitten, voordat ze een probleem worden voor je gebruikers. Probeer Miri, en je zult verrast zijn hoeveel interessante dingen het kan vinden in je ogenschijnlijk perfecte code!

Gerelateerde projecten