You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -38,19 +39,12 @@ Moreover, there is a per-call-frame `CallId` as well as some global tracking sta
38
39
// `nat` is the type of mathematical natural numbers, meaning we don't want to think about overflow.
39
40
// NOTE: Miri just uses `NonZeroU64` which, realistically, will not overflow because we only ever increment by 1.
40
41
typePtrId=nat;
42
+
/// For historic reasons we often call pointer IDs "tags".
43
+
typeTag=PtrId;
41
44
42
45
/// CallId: uniquely identifying a stack frame.
43
46
typeCallId=nat;
44
47
45
-
/// Extra per-pointer state (the "tag"), tracking how this pointer was computed
46
-
/// ("pointer provenance").
47
-
pubenumTag {
48
-
/// References are tagged: we know where they got created.
49
-
Tag(PtrId),
50
-
/// Raw pointers are not tracked.
51
-
Untagged,
52
-
}
53
-
54
48
/// Indicates which permission is granted (by this item to some pointers)
55
49
pubenumPermission {
56
50
/// Grants unique mutable access.
@@ -78,7 +72,7 @@ pub struct Stack {
78
72
/// Used *mostly* as a stack; never empty.
79
73
/// Invariants:
80
74
/// * Above a `SharedReadOnly` there can only be more `SharedReadOnly`.
81
-
/// * Except for `Untagged`, no tag occurs in the stack more than once.
75
+
/// * No tag occurs in the stack more than once.
82
76
borrows:Vec<Item>,
83
77
}
84
78
@@ -167,7 +161,7 @@ In other words, the per-stack-frame `CallId` is initialized by `Tracking::new_ca
167
161
We say that a `CallId` is *active* if the call stack contains a stack frame with that ID.
168
162
In the following, we pretend there exists a function `call_is_active(id)` that can check this.
169
163
170
-
**Note**: Miri uses a slightly more complex system with a `HashSet<CallId>` tracking the set of active `CallId`; that is just an optimization to avoid having to scan the call stack all the time.
164
+
**Note**: Miri uses a slightly more complex system to track the set of active `CallId`; that is just an optimization to avoid having to scan the call stack all the time.
171
165
172
166
### Preliminaries for items
173
167
@@ -217,13 +211,13 @@ Using any item within a block is equivalent to using any other item in that same
217
211
218
212
When allocating memory, we have to initialize the `Stack` associated with the new locations, and we have to choose a `Tag` for the initial pointer to this memory.
219
213
220
-
- For heap allocations, the stack of each freshly allocated memory location is `Stack { borrows: vec![(Untagged: SharedReadWrite)] }`, and the initial pointer to that memory has tag `Untagged`.
221
-
- For global allocations (`static`, environment and program argument data, ...), we pick a fresh `id` associated with the global, and each time a pointer to the global is created, it gets tagged `Tag(id)`.
222
-
The stacks in that memory are initialized with `Stack { borrows: vec![(Tag(id): SharedReadWrite)] }`.
223
214
- Stack memory is handled by an environment (which is part of the information carried in a stack frame of the Rust abstract machine) that maps each local variable to a place.
224
215
A place is a pointer together with some other data that is not relevant here -- the key point is that a place, just like every other pointer, carries a tag.
225
-
When the local variable becomes live and its backing memory gets allocated, we generate a new pointer ID `id` by calling `Tracking::new_ptr` and use `Tag(id)` as tag for the place of this local variable.
226
-
We also initialize the stack of all the memory locations in this new memory allocation with `Stack { borrows: vec![(Tag(id): Unique)] }`.
216
+
When the local variable becomes live and its backing memory gets allocated, we generate a new pointer ID `id` by calling `Tracking::new_ptr` and use `id` as tag for the place of this local variable.
217
+
We also initialize the stack of all the memory locations in this new memory allocation with `Stack { borrows: vec![(id: Unique)] }`.
218
+
- For heap allocations, we pick a fresh `id` at allocation time. The stack of each freshly allocated memory location is `Stack { borrows: vec![(id: SharedReadWrite)] }`, and the initial pointer to that memory has tag `id`.
219
+
- For global allocations (`static`, environment and program argument data, ...), we pick a fresh `id` associated with the global, and each time a pointer to the global is created, it gets tagged `id`.
220
+
The stacks in that memory are initialized with `Stack { borrows: vec![(id: SharedReadWrite)] }`.
227
221
228
222
### Accessing memory
229
223
@@ -333,13 +327,16 @@ location.stack.grant(
333
327
When executing `Retag(kind, place)`, we check if `place` holds a reference (`&[mut] _`) or box (`Box<_>`), and if `kind == Raw` we also check each raw pointer (`*[const,mut] _`).
334
328
For those we perform the following steps:
335
329
336
-
1. We compute a fresh tag: `Untagged` for raw pointers, `Tag(Tracking::new_ptr_id())` for everything else.
330
+
1. We compute a fresh tag: `Tracking::new_ptr_id()`.
337
331
2. We determine if we will want to protect the items we are going to generate:
338
332
This is the case only if `kind == FnEntry` and the type of this pointer is a reference (not a box).
339
333
3. We perform reborrowing of the memory this pointer points to with the new tag and indicating whether we want protection, treating boxes as `RefKind::Unique { two_phase: false }`.
340
334
341
335
We do not recurse into fields of structs or other compound types, only "bare" references/... get retagged.
342
336
337
+
**Note**: Miri offers a flag, `-Zmiri-retag-fields`, that changes this behavior to also recurse into compound types to search for references to retag.
338
+
We never recurse through a pointer indirection.
339
+
343
340
### Deallocating memory
344
341
345
342
Memory deallocation first acts like a write access through the pointer used for deallocation.
0 commit comments