flawopen.com/Incidents/rust-unsafe-smallvec-overflow-january-2021
CVE-2021-25900: Heap-Overflow in Rust-smallvec
SmallVec::insert_many vertraute der Untergrenze von size_hint; lieferte ein Iterator mehr, lief der Heap-Puffer über. Behoben in 0.6.14 und 1.6.1.
Ein Lager gilt als sicher, weil ein Roboter jedes Regal prüft, bevor etwas daraufkommt. Für ein paar Aufgaben wird der Roboter abgeschaltet, und ein Mensch arbeitet von Hand und verspricht, vorsichtig zu sein. Einer davon fragte den Fahrer: „Ungefähr wie viele Kisten bringen Sie?“, baute ein Regal dieser Größe und fing an zu stapeln. Die Schätzung war eine Vermutung, keine Garantie – und als mehr Kisten kamen als angekündigt, lief der Stapel über das Regalende hinaus.
Kernkonzepte & Begriffe
SmallVec- Eine Rust-Collection, die wenige Elemente inline hält und beim Wachsen auf einen Heap-Puffer umsteigt. Ihre Geschwindigkeit verdankt sie unsafe-Code, der diesen Puffer von Hand verwaltet.
size_hint- Eine Iterator-Methode, die geschätzte Unter- und Obergrenzen für die verbleibenden Elemente meldet. Es ist ein Hinweis: safe-Code darf jeden Wert liefern, auch einen falschen.
Unsafe Rust- Code in einem unsafe-Block darf Dinge tun, die der Compiler nicht prüft, etwa über Rohzeiger schreiben; der Autor muss die Speichersicherheit für jeden denkbaren safe-Aufrufer gewährleisten.
Soundness- Eine safe-API ist sound, wenn kein safe-Code, so ungewöhnlich er sei, über sie undefiniertes Verhalten auslösen kann. insert_many war es nicht, denn ein zulässiger size_hint konnte Speicher beschädigen.
Framework-Zuordnungen
Vorfall-Zeitstrahl
Yechan Bae und die Rust-Gruppe am SSLab der Georgia Tech melden den Fehler; RustSec veröffentlicht RUSTSEC-2021-0003, und smallvec 0.6.14 und 1.6.1 enthalten die Korrektur.
CVE-2021-25900 wird vergeben; cargo audit markiert betroffene Versionen fortan in Abhängigkeitsbäumen.
Schritt-für-Schritt Angriffsablauf
Ein Iterator gibt eine zu kleine Länge an
insert_many wird mit einem Iterator aufgerufen, dessen size_hint-Untergrenze kleiner ist als die Zahl der tatsächlich gelieferten Elemente. Das darf safe-Code.
Platz wird nach dem Hinweis reserviert
insert_many reserviert nur Platz für die Untergrenze und verschiebt vorhandene Elemente um genau diese Anzahl.
Alle Elemente werden trotzdem geschrieben
Innerhalb eines unsafe-Blocks schreibt die Schleife jedes Element über einen Rohzeiger – überzählige landen hinter dem Ende der Allokation.
Der Heap wird beschädigt
Der Überlauf überschreibt benachbarten Heap-Speicher. Programme, die insert_many Iteratoren übergeben, die von nicht vertrauenswürdigen Eingaben beeinflusst sind, sind betroffen.
Quellcode: Verwundbar vs. Sicher
// Auf Grundlage eines *Hinweises* reservieren...
let (lower, _) = iter.size_hint();
self.reserve(lower);
unsafe {
let ptr = self.as_mut_ptr().add(index);
for (i, item) in iter.enumerate() {
// ...aber so viele Elemente schreiben,
// wie tatsächlich eintreffen.
ptr::write(ptr.add(i), item);
}
}// Die Kapazität für jedes Element erneut prüfen.
// Ein Wert aus sicherem Code darf niemals
// eine Grenze bestimmen, die von unsafe-Code verwendet wird.
for (i, item) in iter.enumerate() {
self.reserve(1);
unsafe {
let ptr = self.as_mut_ptr()
.add(index + i);
ptr::write(ptr, item);
}
}
// Die echte Korrektur hat außerdem den
// unsafe-Codebereich deutlich verkleinert.Checkliste für Engineering & Systemsicherheit
- ✓Bemessen Sie einen von unsafe-Code genutzten Puffer nie anhand einer safe-Trait-Methode wie size_hint, len auf einem Trait-Objekt oder einem Vergleicher; prüfen Sie die Kapazität vor jedem Schreibvorgang erneut.
- ✓Schreiben Sie zu jedem unsafe-Block einen // SAFETY:-Kommentar mit der zugrunde liegenden Invariante und lehnen Sie Reviews ab, in denen diese von nicht vertrauenswürdigem safe-Code abhängt.
- ✓Lassen Sie Miri in der CI mit Tests laufen, die feindselige Implementierungen verwenden – etwa einen Iterator mit falschem size_hint, ein panisches Drop und einen Vergleicher ohne Totalordnung.
- ✓Halten Sie unsafe-Blöcke so klein wie möglich; die smallvec-Korrektur verringerte auch den unsafe-Code in insert_many, damit er überprüfbar wurde.
- ✓Führen Sie cargo audit (oder cargo deny) in der CI aus, damit Abhängigkeiten mit RustSec-Hinweisen wie smallvec vor 0.6.14 bzw. 1.6.1 den Build scheitern lassen.
Quellen
- RustSec — RUSTSEC-2021-0003: Pufferüberlauf in SmallVec::insert_many
- servo/rust-smallvec — Issue 252
- CVE-2021-25900 — CVE-Eintrag
- MITRE CWE-787
- Heartbleed: eine fehlende Grenzprüfung, das halbe Internet
- Baron Samedit: zehn Jahre Root, versteckt in einem Backslash
- Citrix Bleed: wie ein falsch gelesener Rückgabewert MFA aushebelte