@@ -49,46 +49,109 @@ descriptions of safety properties or safety requirements that must be satisfied
4949when using an unsafe API. This is the current form of safety descriptions used in Rust.
5050
5151In contrast, ** safety tags** represent safety properties using a formal language, i.e., a
52- [ tool attribute] written in the form ` #[safety { Prop: "reason" }] ` where
53- - ` safety ` is proc-macro,
54- - ` type ` is one of ` {precond, hazard, option} ` ,
55- - precond denotes a safety requirement that must be satisfied before invoking an unsafe API.
56- Most unsafe APIs carry at least one precondition.
57- - hazard denotes invoking the unsafe API may temporarily leave the program in a vulnerable
58- state; e.g. [ ` String::as_bytes_mut ` ] .
59- - option denotes an optional precondition for an unsafe API—conditions that are sufficient but
60- not necessary to uphold the safety invariant.
61- - ` Prop ` is a safety property (SP) instance. For safety propeties in libcore and libstd,
52+ [ tool attribute] namespace ` safety ` with sub-attributes for each contract category:
53+
54+ - ` #[safety::requires(Prop1, Prop2, ...)] ` — ** preconditions** : safety requirements that must be
55+ satisfied before invoking an unsafe API. Most unsafe APIs carry at least one precondition.
56+ By default, every property in ` requires ` is a precondition. A property tagged with
57+ ` kind = "hazard" ` denotes that invoking the unsafe API may temporarily leave the program
58+ in a vulnerable state with respect to Rust's safety invariants (e.g. [ ` String::as_bytes_mut ` ] ).
59+ - ` #[safety::invariant(Prop)] ` — ** struct invariants** : properties that must hold for every instance
60+ of a struct at all observable points.
61+ - ` #[safety::verify] ` — marks a function as a verification entry point.
62+ - ` #[safety::ensures(Prop)] ` — ** postconditions** : properties guaranteed after the function returns
63+ (reserved for future use).
64+ - A property group can carry an optional ` kind = "..." ` tag for fine-grained categorization:
65+ ``` rust
66+ #[safety:: requires(
67+ (ValidPtr (ptr, T , 1), Align (ptr, T ), kind = " precond" ),
68+ (Alias (ptr, ret), kind = " hazard" ),
69+ )]
70+ ```
71+ When ` kind ` is omitted, ` "precond" ` is the default for ` requires ` , ` "hazard" ` for ` hazard ` .
72+ - ` / *Prop */ ` — ** inline discharge** on a callsite, discharging specific safety properties of the
73+ callee (see [ Discharge Safety Properties] ( #discharge-safety-properties ) ).
74+ - When multiple properties share the same kind, they are grouped with commas inside a single
75+ attribute rather than repeated:
76+ ``` rust
77+ // Preferred (merged)
78+ #[safety:: requires(ValidPtr (ptr, T , 1), Align (ptr, T ), Init (ptr, T , 1))]
79+ // Equivalent but verbose
80+ // #[safety::requires(ValidPtr(ptr, T, 1))]
81+ // #[safety::requires(Align(ptr, T))]
82+ // #[safety::requires(Init(ptr, T, 1))]
83+ ```
84+ - ` Prop ` is a safety property (SP) instance. For safety properties in libcore and libstd,
6285 refer to [ this document] [ primitive-sp ] and our ongoing [ paper] .
63- - multiple SPs can be grouped together by separating them with commas, such as ` SP1, SP2 ` .
64- - ` : "reason" ` is an * optional* string to clarify what SP means in the context.
65- - when a reason string appears, use ` ; ` to separate props like ` SP1: ""; Sp2: "" ` .
6686
6787Here are some basic syntax examples:
6888
6989``` rust
70- #[safety { SP }]
71- #[safety { SP1 , SP2 }]
90+ #[safety:: requires(SP )]
91+ #[safety:: requires(SP1 , SP2 )]
92+ #[safety:: requires((SP1 , kind = " precond" ), (SP2 , kind = " hazard" ))]
93+ #[safety:: invariant(SP1 , SP2 )]
94+ #[safety:: invariant(any(Null (p), (ValidPtr (p, T , 1), Align (p, T ))))]
95+ #[safety:: verify]
96+ #[safety:: ensures(SP )]
97+ ```
98+
99+ ### The ` any(...) ` Combinator
100+
101+ For properties that admit alternative states, the ` any(...) ` combinator expresses a logical OR
102+ between disjuncts, where commas inside each parenthesized disjunct mean logical AND:
103+
104+ ``` text
105+ any(D1, D2)
106+ any(Null(p), (P1(p, ...), P2(p, ...)))
107+ ```
72108
73- #[safety { SP1 : " reason" }]
74- #[safety { SP1 : " reason" ; SP2 : " reason" }]
109+ The primary use case is a ** null guard** : when a pointer may legally be null, ` any(Null(p), ...) `
110+ declares that the conjunct properties only need to hold when ` p ` is non-null. This is the
111+ raw-pointer counterpart of ` Option ` invariants:
75112
76- #[safety { SP1 , SP2 : " shared reason for the two SPs" }]
77- #[safety { SP1 , SP2 : " shared reason for the two SPs" ; SP3 }]
78- #[safety { SP3 ; SP1 , SP2 : " shared reason for the two SPs" }]
113+ ``` rust
114+ // A linked list node whose `next` field may be null
115+ #[safety:: invariant(any(
116+ Null (self. next),
117+ (ValidPtr (self. next, Node , 1), Align (self. next, Node ))
118+ ))]
119+ pub struct Node {
120+ value : u32 ,
121+ next : * mut Node ,
122+ }
123+ ```
124+
125+ In the ` ptr::read ` example, the ` Owning ` precondition and ` Trait(T, Copy) ` advisory can be grouped
126+ under ` any ` because either ` T: Copy ` or ` Owning(src) ` must hold — non-Copy types require ownership
127+ transfer:
128+
129+ ``` rust
130+ #[safety:: requires(
131+ ValidPtr (src, T , 1),
132+ Aligned (src, T ),
133+ Init (src, T , 1),
134+ Alias (src, ret),
135+ any(Owning (src), Trait (T , Copy )),
136+ )]
137+ pub const unsafe fn read <T >(src : * const T ) -> T { ... }
79138```
80139
81140We can define the annotation language with context-free grammar as follows:
82141
83142``` text
84- SafetyAnnotation => '#' '[' 'safety' '{' SPUnits '}' ']'
85- SPUnits => SPUnit (';' SPUnit)*
86- SPUnit => SPItem (',' SPItem)* (':' Reasons)?
87- SPItem => ID (Args)?
88- ID => ([a-z][A-Z])+
89- Args => '(' Arg (, Arg)* ')'
90- Arg => expression
91- Reasons => '"' Text '"'
143+ SafetyAnnotation => '#' '[' 'safety' '::' attr '(' SPGroups ')' ']'
144+ attr => 'requires' | 'invariant' | 'ensures' | 'verify'
145+ SPGroups => SPGroup (',' SPGroup)*
146+ SPGroup => '(' SPList (',' 'kind' '=' STRING)? ')'
147+ | SPList
148+ SPList => SPItem (',' SPItem)*
149+ SPItem => ID ('(' Arg (',' Arg)* ')')?
150+ | 'any' '(' Disjunct (',' Disjunct)* ')'
151+ Disjunct => SPItem
152+ | '(' SPList ')'
153+ ID => [A-Z][A-Za-z]*
154+ Arg => expression | type
92155```
93156
94157See the following usage of ` ptr::read ` as a full example.
@@ -128,14 +191,13 @@ pub const unsafe fn read<T>(src: *const T) -> T { ... }
128191
129192We can extract safety requirements above into propeties below:
130193
131- | Type | Property | Arguments | Description |
132- | ---------| ----------| -----------| -----------------------------------------------------------------------------------------------------------------------------------------------------|
133- | Precond | ValidPtr | src, T, 1 | ` src ` must be [ valid] for reads (for 1 * sizeof(T) bytes). |
134- | Precond | Aligned | src, T | ` src ` must be properly aligned (with T). |
135- | Precond | Init | src, T, 1 | ` src ` must point to a properly initialized value of type ` T ` . |
136- | Option | Trait | T, Copy | If ` T ` is not [ ` Copy ` ] , using both the returned value and the value at ` *src ` can violate memory safety. |
137- | Precond | Owning | src | Further clarification: The memory pointed by src must not be owned if T is not copy, or the object hold by * src should not be automatically dropped |
138- | Hazard | Alias | src, ret | Further clarification: The return value may incur aliases between src and the return value |
194+ | Category | Property | Arguments | Description |
195+ | ---------------| ----------| -------------| -----------------------------------------------------------------------------------------------------------------------------------------------------|
196+ | ` requires ` | ValidPtr | src, T, 1 | ` src ` must be [ valid] for reads (for 1 * sizeof(T) bytes). |
197+ | ` requires ` | Aligned | src, T | ` src ` must be properly aligned (with T). |
198+ | ` requires ` | Init | src, T, 1 | ` src ` must point to a properly initialized value of type ` T ` . |
199+ | ` requires ` | Alias | src, ret | The return value may incur aliases between src and the return value (informational). |
200+ | ` requires ` | any | Owning, Trait(T, Copy) | Either ` src ` must be uniquely owned (non-Copy types), or ` T: Copy ` must hold. |
139201
140202[ valid ] : https://doc.rust-lang.org/std/ptr/index.html#safety
141203[ alignment ] : https://doc.rust-lang.org/std/ptr/index.html#alignment
@@ -144,10 +206,13 @@ We can extract safety requirements above into propeties below:
144206We can represent these safety requirements using safety tags as shown below.
145207
146208``` rust
147- #[safety {
148- ValidPtr , Aligned , Init , Alias ,
149- any { Owning , Trait (T , Copy ) },
150- }]
209+ #[safety:: requires(
210+ ValidPtr (src, T , 1),
211+ Aligned (src, T ),
212+ Init (src, T , 1),
213+ Alias (src, ret),
214+ any(Owning (src), Trait (T , Copy )),
215+ )]
151216pub const unsafe fn read <T >(src : * const T ) -> T { ... }
152217```
153218
@@ -178,7 +243,7 @@ url = "https://doc.rust-lang.org/nightly/std/ptr/index.html#alignment"
178243We defined a property called ` Aligned ` , which includes two arguments, a dynamic description derived
179244from user input and some other fields. All fields are optional.
180245
181- When ` #[safety { Aligned(src, T) } ] ` is used, a corresponding doc comment is generated:
246+ When ` #[safety::requires( Aligned(src, T)) ] ` is used, a corresponding doc comment is generated:
182247
183248``` rust
184249#[doc = " pointer `src` must be properly aligned for type `T`" ]
@@ -195,10 +260,13 @@ For detailed usage and examples, refer to [tag-std#35].
195260We also support SPs with arguments, which are required in verification scenarios.
196261
197262``` rust
198- #[safety {
199- ValidPtr (src, T , 1), Aligned (src, T ), Init (src, T , 1), Alias (src, ret),
200- any{ Owning (src), Trait (T , Copy ) }
201- }]
263+ #[safety:: requires(
264+ ValidPtr (src, T , 1),
265+ Aligned (src, T ),
266+ Init (src, T , 1),
267+ Alias (src, ret),
268+ any(Owning (src), Trait (T , Copy )),
269+ )]
202270pub const unsafe fn read <T >(src : * const T ) -> T { ... }
203271```
204272
@@ -275,7 +343,8 @@ depends on the behavior of Guard's Drop implementation. If `try_fold::Guard::dro
275343developers must check whether the associated safety comments still hold. (This RFC does not address
276344this problem, but see [ Entity Reference System] ( #reference-entity ) for our thought.)
277345
278- To address the first issue, we propose a solution based on annotating ` #[safety {}] ` on callsites.
346+ To address the first issue, we propose a solution based on annotating safety tags on callsites using
347+ inline ` /* property */ ` discharge comments or ` #[safety::verify] ` for automated verification.
279348
280349``` rust
281350fn try_fold <B , F , R >(& mut self , mut init : B , mut f : F ) -> R {
@@ -284,11 +353,9 @@ fn try_fold<B, F, R>(&mut self, mut init: B, mut f: F) -> R {
284353 init = head . iter (). map (| elem | {
285354 guard . consumed += 1 ;
286355
287- #[safety {
288- ValidPtr , Aligned , Init , Alias ,
289- Owning : " Because we incremented `guard.consumed`, the deque \
290- effectively forgot the element, so we can take ownership."
291- }]
356+ // SAFETY: Because we incremented `guard.consumed`, the deque
357+ // effectively forgot the element, so we can take ownership.
358+ /* ValidPtr(elem, T, 1), Aligned(elem, T), Init(elem, T, 1), Owning(elem) */
292359 unsafe { ptr :: read (elem ) }
293360 })
294361 . try_fold (init , & mut f )? ;
@@ -319,62 +386,52 @@ LLL | unsafe { ptr::read(elem) }
319386Since this RFC does not require significant changes to the Rust compiler or language, the
320387implementation details discussed in this section are tool-specific and primarily focus on syntax.
321388
322- Take one of safety tag on ` ptr::read ` as an example:
389+ Take one safety tag on ` ptr::read ` as an example:
323390
324391``` rust
325- use safety :: safety;
326-
327- #[safety { ValidPtr }]
392+ // The `safety` tool attribute is registered via
393+ // #![feature(register_tool)]
394+ // #![register_tool(safety)]
395+ //
396+ // or injected through RUSTFLAGS:
397+ // RUSTFLAGS="--cfg=safety -Zcrate-attr=feature(register_tool) -Zcrate-attr=register_tool(safety)"
398+
399+ #[safety:: requires(ValidPtr (src, T , 1), Aligned (src, T ), Init (src, T , 1))]
328400```
329401
330- #[ safety] is a procedural macro imported into scope by a crate named ` safety-macro ` .
331-
332- Since we don’t have permissions to the ` safety ` crate, users can rename our crate in their
333- Cargo.toml file as follows:
334-
335- ``` toml
336- # This renames the dependency `safety-macro` as `safety` within your crate.
337- safety = { version = " 0.3.0" , package = " safety-macro" }
338- ```
339-
340- Proc-macros can be directly used in ` no_std ` projects and even in non-Cargo environments, such as
341- Rust for Linux, by passing the compiled ` libsafety_macro.so ` as a direct dependency.
342-
343- The proc macro expands to three attributes:
402+ The attribute can expand to multiple downstream annotations for different tools. For example,
403+ ` #[safety::requires(ValidPtr(src, T, 1), Aligned(src, T), Init(src, T, 1))] ` on ` ptr::read ` could
404+ generate:
344405
345406``` rust
346407#[doc = " `src` must be [valid] for reads.\ n\ n [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety" ]
347- #[safety_tool :: ... ]
408+ #[rapx :: requires( ValidPtr (src, T , 1), Aligned (src, T ), Init (src, T , 1)) ]
348409#[kani:: requires(kani:: mem:: can_dereference(src))]
349410```
350411
351- * ` #[doc] ` is a safety comment, possibly with extra argument infomation interpolated into the text.
352- * ` #[kani] ` is a [ contract] . If the safety property has a countepart of external verification macro
353- such as kani, we hope to support this feature in the future.
354- * ` #[safety_tool] ` is a [ tool attribute] registered by our linter. ` register_tool ` feature needs to
355- be stabilized, so developers must enable the following features in the root module:
412+ * ` #[doc] ` is a safety comment, possibly with extra argument information interpolated into the text.
413+ * ` #[kani] ` is a [ contract] . If the safety property has a counterpart in an external verification
414+ tool such as kani, we hope to support this feature in the future.
415+ * ` #[rapx::requires(...)] ` is a [ tool attribute] processed by the RAPx verifier. The
416+ ` register_tool ` feature is needed, which can be provided via source annotation or compiler
417+ flags:
356418
357419[ contract ] : https://model-checking.github.io/kani/reference/experimental/contracts.html
358420[ register_tool ] : https://github.com/rust-lang/rfcs/pull/3808
359421
360422``` rust
361423#![feature(register_tool)]
362- #![register_tool(safety_tool )]
424+ #![register_tool(safety )]
363425```
364426
365427or add them to [ ` --crate-attr ` ] ( https://github.com/rust-lang/rfcs/pull/3791 ) compiler flag:
366428
367429``` bash
368- rustc --crate-attr=" feature(register_tool)" --crate=" register_tool(safety_tool )"
430+ rustc --crate-attr=" feature(register_tool)" --crate-attr =" register_tool(safety )"
369431```
370432
371- To support ` #[discharges] ` , additional unstable features are required to allow attributes on
372- statements and expressions:
373-
374- ``` rust
375- #![feature(proc_macro_hygiene)]
376- #![feature(stmt_expr_attributes)]
377- ```
433+ To support inline ` /* property */ ` discharge comments, the RAPx driver parses comments
434+ adjacent to unsafe call sites without requiring additional unstable features.
378435
379436Details of implementation on reference entity system belongs to the linter tool.
380437
@@ -539,11 +596,9 @@ fn try_fold<B, F, R>(&mut self, mut init: B, mut f: F) -> R
539596 guard . consumed += 1 ;
540597
541598 #[ref (try_fold)] // 💡
542- #[safety {
543- ValidPtr , Aligned , Init , Alias ,
544- Owning : " Because we incremented `guard.consumed`, the deque \
545- effectively forgot the element, so we can take ownership."
546- }]
599+ // SAFETY: Because we incremented `guard.consumed`, the deque
600+ // effectively forgot the element, so we can take ownership.
601+ /* ValidPtr(elem, T, 1), Aligned(elem, T), Init(elem, T, 1), Owning(elem) */
547602 unsafe { ptr :: read (elem ) }
548603 })
549604 . try_fold (init , & mut f )? ;
@@ -592,18 +647,16 @@ Arguments in a property can be any expression, and sometimes the type of argumen
592647analysis and doc comments:
593648
594649``` rust
595- // Syntax1: we don't need to query type if user is asked to provide it.
596- // But we're responsible to check the given type is valid!
597- // So this means we have to reach type systems anyway.
598- #[safety:: precond:: Aligned (p, T )]
599- // Syntax2: we must get type info from rustc.
600- #[safety:: precond:: Aligned (p)]
650+ // Syntax1: type provided explicitly — verifier can check the given type is valid
651+ #[safety:: requires(Aligned (p, T ))]
652+ // Syntax2: type inferred from the function signature — simpler for the annotator
653+ #[safety:: requires(Aligned (p))]
601654unsafe fn read <T >(src : * const T ) {}
602655```
603656
604657The generic type ` T ` will be rendered in ` #[doc] ` , so it'd be tricky if the type needs
605658[ normalization] or trait bounds analysis. It happens to be the case that ` ptr::read ` has a safety
606- property ` #[option:: Trait(T, Copy)] ` .
659+ property ` Trait(T, Copy) ` (informational, not a hard precondition) .
607660
608661[ normalization ] : https://rustc-dev-guide.rust-lang.org/normalization.html
609662
0 commit comments