Skip to content

Commit da99284

Browse files
committed
introduce memory model validity
1 parent da8897c commit da99284

4 files changed

Lines changed: 275 additions & 72 deletions

File tree

examples/src/ffi.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,25 @@ int use_xtra()
5252
xtra_with(cb);
5353
}
5454
/////////////////////// ANCHOR_END: free_intern
55+
56+
/////////////////////// ANCHOR: output_pointer
57+
void inc_wrap(uint8_t *a)
58+
{
59+
if (*a == 255)
60+
{
61+
*a = 0;
62+
}
63+
else
64+
{
65+
*a = *a + 1;
66+
}
67+
}
68+
/////////////////////// ANCHOR_END: output_pointer
69+
70+
/////////////////////// ANCHOR: input_pointer_alias
71+
void use_swap()
72+
{
73+
uint8_t a = 42;
74+
swap(&a, &a);
75+
}
76+
/////////////////////// ANCHOR_END: input_pointer_alias

examples/src/ffi.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,31 @@ pub unsafe extern "C" fn no_panic() -> i32 {
265265
}
266266
}
267267
/////////////////////// ANCHOR_END: panic
268+
269+
/////////////////////// ANCHOR: output_pointer
270+
unsafe extern "C" {
271+
fn inc_wrap(a: *mut u8);
272+
}
273+
274+
fn run_inc() {
275+
let val: u8 = 6;
276+
let p = (&val as *const u8) as *mut u8;
277+
unsafe { inc_wrap(p) };
278+
}
279+
/////////////////////// ANCHOR_END: output_pointer
280+
281+
/////////////////////// ANCHOR: output_pointer_good
282+
fn safe_inc_wrap(a: &mut u8) {
283+
unsafe {
284+
inc_wrap(a);
285+
}
286+
}
287+
/////////////////////// ANCHOR_END: output_pointer_good
288+
289+
/////////////////////// ANCHOR: input_pointer_alias
290+
extern "C" fn swap(a: *mut u8, b: *mut u8) {
291+
let a = unsafe { a.as_mut() }.unwrap();
292+
let b = unsafe { b.as_mut() }.unwrap();
293+
std::mem::swap(a, b);
294+
}
295+
/////////////////////// ANCHOR_END: input_pointer_alias

src/en/unsafe/ffi.md

Lines changed: 103 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ references:
88
title: Extern types
99
url: https://rust-lang.github.io/rfcs/1861-extern-types.html
1010
id: RFC-1861
11+
- type: web
12+
title: Memory model
13+
url: https://doc.rust-lang.org/core/ptr/index.html
14+
id: memory-model
1115
- type: web
1216
title: Unwinding
1317
url: https://doc.rust-lang.org/stable/reference/items/functions.html#unwinding
@@ -235,7 +239,7 @@ the specific target platform.
235239
236240
[libc]: https://crates.io/crates/libc
237241
238-
### Non-robust types: references, function pointers, enums
242+
### Non-robust types: references, function pointers, enums {#robustness}
239243
240244
A *trap representation* of a particular type is a representation (pattern of
241245
bits) that respects the type's representation constraints (such as size and
@@ -311,12 +315,9 @@ LLVM's `-fsanitize=bool` may be used.
311315
312316
#### References and pointers
313317
314-
Although they are allowed by the Rust compiler, the use of Rust references in
315-
FFI may break Rust memory safety. Because their “unsafety” is more explicit,
316-
pointers are preferred over Rust references when binding to another language.
317-
318-
On the one hand, reference types are very non-robust: they allow only pointers
319-
to valid memory objects. Any deviation leads to undefined behavior.
318+
The types &mut T and &T are subject to the same validity requirements as the
319+
type T, with the additional constraint that they must refer to a properly
320+
allocated value. Any deviation leads to undefined behavior
320321
321322
When binding to and from C, the problem is particularly severe because C has
322323
no references (in the sense of valid pointers) and the compiler does not offer
@@ -331,17 +332,32 @@ Rust references may be used reasonably with other C-compatible languages
331332
including C variants allowing for non-null type checking, e.g. Microsoft SAL
332333
annotated code.
333334
334-
On the other hand, Rust *pointer types* may also lead to undefined behaviors
335-
but are more verifiable, mostly against `std/core::ptr::null()` (C's `(void*)0`)
335+
By contrast, Rust’s raw pointer types (`*const T` and `*mut T`) defer validity
336+
checks from the point of binding to the point of use. Furthermore, they allow
337+
validity to be assessed primarily against `std/core::ptr::null()` (C's `(void*)0`)
336338
but also in some contexts against a known valid memory range (particularly in
337-
embedded systems or kernel-level programming). Another advantage of using Rust
339+
embedded systems or kernel-level programming) or against alignment requirements.
340+
341+
Another advantage of using Rust
338342
pointers in FFI is that any load of the pointed value is clearly marked inside
339343
an `unsafe` block or function.
340344
341-
<div class="reco" id="FFI-NOREF" type="Rule" title="Do not use reference types in a FFI but pointer types">
345+
<div class="reco" id="FFI-CK-PTR-VALID" type="Rule" title="Check foreign pointers">
346+
347+
In a secure Rust development, any Rust code that dereferences a foreign
348+
pointer MUST check their validity beforehand.
349+
In particular, pointers MUST be checked to be non-null before any use.
342350
343-
In a secure Rust development, the Rust *low-level* code (the `*-sys` crate) in a FFI MUST NOT use reference types
344-
but pointer types.
351+
Stronger approaches are advisable when possible. They include checking
352+
pointers against known valid memory range, tagging (or signing) pointers
353+
(particularly applicable if the pointed value is only manipulated from Rust)
354+
or checking data alignment.
355+
356+
</div>
357+
358+
<div class="reco" id="FFI-INPUT-PTR" type="Recommendation" title="Use raw pointer to encode pointers coming from the external language">
359+
360+
In a secure Rust development, pointers received from the external language SHOULD be encoded by Rust *raw pointers*.
345361
346362
Exceptions include:
347363
@@ -354,9 +370,10 @@ Exceptions include:
354370
355371
</div>
356372
357-
<!-- -->
373+
Where the previous recommendation cannot be applied, pointer validity checks must
374+
be carried out by the language from which the pointer originates (that is, the external language).
358375
359-
<div class="reco" id="FFI-CKREF" type="Rule" title="Do not use unchecked foreign references">
376+
<div class="reco" id="FFI-CK-INPUT-REF-VALID" type="Rule" title="Do not use unchecked foreign references">
360377
361378
In a secure Rust development, every foreign reference that is transmitted to
362379
Rust through FFI MUST be **checked on the foreign side** either automatically
@@ -370,18 +387,6 @@ and manipulated only from the Rust side and `Option`-wrapped references
370387
371388
<!-- -->
372389
373-
<div class="reco" id="FFI-CKPTR" type="Rule" title="Check foreign pointers">
374-
375-
In a secure Rust development, any Rust code that dereferences a foreign
376-
pointer MUST check their validity beforehand.
377-
In particular, pointers MUST be checked to be non-null before any use.
378-
379-
Stronger approaches are advisable when possible. They include checking
380-
pointers against known valid memory range or tagging (or signing) pointers
381-
(particularly applicable if the pointed value is only manipulated from Rust).
382-
383-
</div>
384-
385390
The following code a simple example of foreign pointer use in an exported Rust
386391
function:
387392
@@ -523,6 +528,77 @@ Example of opaque Rust type:
523528
{{#include ../../../examples/src/ffi.rs:opaque_internal}}
524529
```
525530

531+
### Preserving Rust memory model {#ffi-memory-model}
532+
533+
The Rust compiler uses information from the *borrow checker* to optimize code execution.
534+
In doing so, it assumes that the conditions verified by the borrow checker,
535+
in particular the [aliasing](https://doc.rust-lang.org/nomicon/aliasing.html) rules,
536+
are valid, and it bases its optimizations on these assumptions. More details about this
537+
memory model can be found in the [`core` API documentation @memory-model].
538+
539+
Violating these assumptions can therefore lead to the occurrence of UB.
540+
541+
<div class="reco" id="FFI-CK-REF-MODEL" type="Rule" title="Preservation of Rust’s memory model when transferring indirections at its boundary">
542+
543+
In secure development in Rust, the use of indirection (pointers, references)
544+
crossing the boundary MUST preserve Rust’s memory model.
545+
546+
</div>
547+
548+
<div class="example">
549+
550+
In the following example, the Rust code manipulates pointers originating from C,
551+
showing the importance of respecting Rust’s invariants from the C side.
552+
553+
The `swap` function defined below produces undefined behaviour (UB) if it is
554+
called from the following C code, because Rust’s memory model forbids the *aliasing* of mutable references.
555+
556+
557+
```rust,noplaypen
558+
{{#include ../../../examples/src/ffi.rs:input_pointer_alias}}
559+
```
560+
561+
```c ub
562+
{{#include ../../../examples/src/ffi.c:input_pointer_alias}}
563+
```
564+
565+
</div>
566+
567+
<div class="example">
568+
569+
In the following example, the Rust code passes a pointer to C code,
570+
illustrating the importance of reflecting, in Rust’s type system,
571+
the operations performed on the C side.
572+
573+
The imported function `inc_wrap` increments the variable `a`.
574+
575+
```c
576+
{{#include ../../../examples/src/ffi.c:output_pointer}}
577+
```
578+
The Rust code passes a pointer to the immutable variable `val`
579+
to the C function, thereby violating Rust’s memory model and resulting in *UB*.
580+
581+
582+
```rust,noplaypen
583+
{{#include ../../../examples/src/ffi.rs:output_pointer}}
584+
```
585+
586+
In this example, the error could have been detected by placing stronger constraints
587+
on the binding to the C library API, either by modifying its signature directly,
588+
or by wrapping the `inc_wrap` function, as in the following example.
589+
590+
```rust good
591+
{{#include ../../../examples/src/ffi.rs:output_pointer_good}}
592+
```
593+
594+
The `safe_inc_wrap` function is indeed *safe* because:
595+
596+
* any memory value can be interpreted as a value of type `u8` (the `u8` type is [robust](#robustness)),
597+
* the type of the `safe_inc_wrap` function enforces the *non-aliasing* and *mutability* constraints,
598+
* the C code neither frees the pointer nor “retains” it (via a `static` variable, for example) beyond the call to the `inc_wrap` function.
599+
600+
</div>
601+
526602
## Memory and resource management
527603

528604
Programming languages handle memory in various ways. As a result, it is

0 commit comments

Comments
 (0)