2017-09-06 1 views
7

Warum ist diese Zeigerarithmetik (ohne dass Daten hinter diesen Zeigern gelesen oder geschrieben werden) die Ursache eines Segmentfehlers?Warum ist Zeigerarithmetik die Ursache eines segfault?

#![allow(dead_code,unused_variables)] 
use std::cell::Cell; 

struct Bar<T: ?Sized> { 
    a: Cell<usize>, 
    value: T, 
} 

unsafe fn foo<T: ?Sized>(v: &T) { 
    let fake: &Bar<T> = std::mem::zeroed(); 

    // segfault on this line 
    // we are not reading or writing uninitialized data behind the reference, 
    // but only doing pointer arithmetic. We are not reading or writing 
    // uninitialized vtable, but only copy the vtable pointer. 
    let fake_val = &fake.value; 
} 


fn main() { 
    use std::any::Any; 

    let some_ref: &Any = &42 as &Any; 
    unsafe { foo(some_ref) }; 
} 

(On Playground)

Ausgang: Segmentation fault

Antwort

10

In Rust, merely creating a dangling reference is undefined behavior! Dies ermöglicht es dem Compiler, einige aggressive Optimierungen um Referenzen durchzuführen, die andernfalls nicht möglich wären. In diesem speziellen Fall generiert der Compiler code which calculates the offset for the field by using the align value in the vtable. Es versucht also, das vptr zu dereferenzieren, das den segfault verursacht.


baumelt Zeiger haben, können Sie keine Referenz, aber eine raw pointer verwenden sollten. Sie können rohe Zeiger ohne Probleme baumeln lassen!

let fake: *const Bar<T> = std::ptr::null(); 
+0

Wenn es sicher ist, einen ungeordneten Zeiger mit einem rohen Zeiger zu haben, warum zeigt dies das gleiche Verhalten wie der OP-Code (z. B. schlägt in Debug "funktioniert" in Release) https: //play.rust-lang. org /? gist = 23baa08f2c83609e002f33b3e2852543 & version = stabil – Timidger

+1

@Timidger Da du den Zeiger in der zweiten Zeile dereferenzierst. Die Dereferenzierung eines baumelnden Zeigers ist natürlich nicht erlaubt. Sie können also den Zeiger auf das Feld nicht mit dem Operator '. Um ehrlich zu sein, ich weiß nicht, was der beste Weg ist, einen solchen Zeiger auf ein Feld zu erhalten. Besser eine andere Frage stellen! :) –

1

Sie dereferenzieren einen Nullzeiger auf der von Ihnen angegebenen Zeile. Sie haben Recht, dass Sie einen Bezug auf diesen Wert nehmen, aber der Code, den rustc für LLVM ausgibt, ist im Debug-Modus sehr dumm. Versuchen Sie, dies im Freigabemodus auszuführen, und Sie werden sehen, dass wahrscheinlich der Optimierer wird nett zu Ihnen sein, und dies wird nicht mehr segfault.

Da dies UB ist, hängen Sie bitte nicht davon für echten Code ab.

+4

Wohl, wenn das im Debug-Modus kompilierte Segfaults bei undefiniertem Verhalten, das macht es bestimmt nicht so blöd. : P In Rust ist es genauso illegal, einen Verweis auf ungültige Inhalte zu erhalten, als direkt auf diesen Inhalt zuzugreifen. Daher ist ein Segfault eine der "besten" Konsequenzen, die wir uns wünschen könnten. –