How is each dynamically verified aspect of Rust implemented?

Rust uses dynamic validation methods to validate many things. One such example is checking the boundaries of arrays.

Take this code for example

fn test_dynamic_checking() -> i32 { let x = [1, 2, 3, 4]; x[1] } 

As a result of LLVM IR:

 ; Function Attrs: uwtable define internal i32 @_ZN10dynamic_ck21test_dynamic_checking17hcef32a1e8c339e2aE() unnamed_addr #0 { entry-block: %x = alloca [5 x i32] %0 = bitcast [5 x i32]* %x to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* bitcast ([5 x i32]* @const7091 to i8*), i64 20, i32 4, i1 false) %1 = getelementptr inbounds [5 x i32], [5 x i32]* %x, i32 0, i32 0 %2 = call i1 @llvm.expect.i1(i1 false, i1 false) br i1 %2, label %cond, label %next next: ; preds = %entry-block %3 = getelementptr inbounds i32, i32* %1, i64 1 %4 = load i32, i32* %3 ret i32 %4 cond: ; preds = %entry-block call void @_ZN4core9panicking18panic_bounds_check17hcc71f10000bd8e6fE({ %str_slice, i32 }* noalias readonly dereferenceable(24) @panic_bounds_check_loc7095, i64 1, i64 5) unreachable } 

A branch is inserted to determine if the index is out of bounds or not, which does not exist in the CLL-compiled LLVM IR.

Here are my questions :

  • In what situations does Rust perform a dynamic check?
  • How does Rust implement dynamic validation in different situations?
  • Is there a way to turn off dynamic validation?
+5
source share
2 answers

Conceptually, Rust performs an array binding check for each access to each array. However, the compiler is very good at optimizing checks when it can prove that it is safe.

LLVM's staging output is misleading because it is still being optimized using the LLVM optimization machine prior to assembly. The best way to verify the assembly is to create the final assembly using a call such as rustc -O --emit asm --crate-type=lib . The build result for your function :

  push rbp mov rbp, rsp mov eax, 2 pop rbp ret 

There is not only a binding check, but also no array, the compiler optimized the entire function until return 2i32 ! To force a check to be bound, the function must be written so that Rust cannot prove that it can be removed:

 pub fn test_dynamic_checking(ind: usize) -> i32 () { let x = [1, 2, 3, 4]; x[ind] } 

This leads to a larger assembly where the binding check is performed as the following two instructions:

  cmp rax, 3 ; compare index with 3 ja .LBB0_2 ; if greater, jump to panic code 

It is as effective as it gets. Turning off snap checking is rarely a good idea, because it can lead to a program crash. This can be done, but explicitly and only within the block or function unsafe :

 pub unsafe fn test_dynamic_checking(ind: usize) -> i32 () { let x = [1, 2, 3, 4]; *x.get_unchecked(ind) } 

The generated assembly shows that the error is completely eliminated:

  push rbp mov rbp, rsp lea rax, [rip + const3141] mov eax, dword ptr [rax + 4*rdi] pop rbp ret const3141: .long 1 .long 2 .long 3 .long 4 
+8
source

In what situations does Rust implement dynamic validation?

It's a bit of a cop ... but Rust, the language, doesn't do any dynamic checking. Rust libraries, however, start with the core and std libraries.

The library needs to use runtime checking if this cannot lead to a memory security issue. Examples include:

  • ensuring that the index is within boundaries, for example, when implementing Index
  • ensures that no other object reference exists, for example when implementing RefCell
  • ...

In general, Rust's philosophy is to do as many checks as possible at compile time, but some checks require a delay until the execution time, because they depend on dynamic behavior / values.

How does Rust implement dynamic validation in different situations?

As efficient as possible.

When dynamic verification is required, Rust code will be generated as efficiently as possible. This can be a little tricky, because it involves trying to make the optimizer easier and fit the patterns that the optimizer recognizes (or captures), but we have a chance that some developers are obsessed with performance (e.g. @bluss).

Not all checks can be undone, of course, and those that remain will usually be just a branch.

Is there a way to turn off dynamic validation?

If dynamic verification is required to ensure code security, it cannot be disabled in secure code.

However, in some situations, an unsafe block or function may skip checking (for example, get_unchecked for indexing).

This is not recommended at all and should be the last behavior:

  • In most cases, runtime checks have virtually no effect on performance; Predicting the processor is just as amazing.
  • Even if they have some effect, if they do not sit in a very hot cycle, it may not be worth optimizing them.
  • Finally, if it is NOT optimizing, you should try to understand why: perhaps this is not possible or the optimizer may not be smart enough ... in the latter case, reporting a problem is the best way to make someone dig into the optimizer and fix it (if you you cannot or do not want to do it yourself).
+1
source

Source: https://habr.com/ru/post/1259382/


All Articles