Update: #[stub(...)]
seems to be more popular, such as
#[stub(warning="This always returns 4, determined by fair dice roll"]
#[stub(fail="This function always panic and should not be called")]
#[stub(info="This function process privacy input. Please be careful")]
And#[allow(stub...)]
or similar stuffs still need.
Hi all,
I would like to propose a new feature gate always_panic
as well as a lint rule to check calls to unsupported
/unimplemented
or similar functions.
- Feature Name:
always_panic
- Start Date: 2019-04-08
- RFC PR: (left empty)
- Rust Issue: (left empty)
Summary
Add a new feature gate always_panic
works on unimplemented/unsupported functions to trigger compile-time warnings.
Motivation
Compile-time warnings are always better than runtime failures. However, with the current design of libstd, we may encounter inevitable runtime panics without compile-time warnings. For example, on target wasm32-unknown-unknown
, we can compile the following code without warnings:
#[wasm_bindgen]
pub fn greet() {
let _ = std::fs::File::open("foo.txt").unwrap();
alert("Hello, wasm-game-of-life!");
}
It always compiles perfectly, but turns out to panic due to fs
is unsupported in wasm32-unknown-unknown
. Similar panics would happen on platforms have unsupported features, such as sgx does not support process
, wasi does not support net
, etc.
With the proposed new feature, rustc would generate compile-time warnings on invoking such always_panic
functions, similar to the feature gate deprecated
. This will make Rust one step closer to if it compiles, it works.
Guide-level explanation
The proposed feature gate is pretty straight-forward. The one who designs platform-specific codes (such as codes under libstd/sys
) may add this feature to unsupported functions or not yet implemented ones. And others could help on tagging existing codes as well. One concrete example of libstd/sys/wasm/thread.rs
is as follows:
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
#[always_panic]
pub unsafe fn new(_stack: usize, _p: Box<dyn FnBox()>)
-> io::Result<Thread>
{
unsupported()
}
}
Then if rustc meets the following code:
#[wasm_bindgen]
pub fn greet() {
let _ = std::thread::Thread:new(......);
alert("Hello, wasm-game-of-life!");
}
rustc would generate a warning like:
warning: use of always_panic item 'std::thread::Thread::new'. Please check if it is needed.
--> src/lib.rs:22:5
|
22 | let _ = std::thread::Thread:new(......);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(always_panic)] on by default
This change may cause many compile-time warnings during the porting of a crate. In the past, the ported crate would compile smoothly. With the proposed feature gate and properly tagged functions, when porting a crate with #[deny(warnings)]
attribute, the compilation would fail. This would alert the developer about the incompatibility of this crate and the target platform.
Reference-level explanation
The proposed solution may need the following changes of Rust:
- A new feature gate in libsyntax. Its template may be
deprecated
:
552 // Allows `#[deprecated]` attribute.
553 (accepted, deprecated, "1.9.0", Some(29935), None),
One implementation could be:
// Allows `#[always_panic]` attribute.
(accepted, always_panic, "1.xx.0", Some(_), None),
- A default lint rule in librustc.
Since this feature works like a built-in lint rule, we should add it to librustc. In librustc/lint/builtin.rs
:
declare_lint! {
pub ALWAYS_PANIC,
Warn,
"detects use of items which always panics (unsupported or unimplemented)",
report_in_external_macro: true
}
report_in_external_macro: true
here is the same as feature deprecated
, intended to warn the developers about more potential incompatibility.
Whether to keep the implementation inside librustc, or put it in librustc_lint remains a question. I cannot fit it into any lint groups of "nonstandard_style", "unused", "rust_2018_idioms", "rustdoc" defined in librustc_lint. So I intend to put it in librustc, if possible.
Basically, it works similar to the deprecated
tag, but irrelevant to stability issues.
always_panic
should be orthogonal to existing feature gates because it adds additional semantics to the compiler.
Drawbacks
Potential drawbacks would be
- Increasing the number of compile-time warnings/failures on some platforms with limited sys support.
Rationale and alternatives
Why is this design the best in the space of possible designs?
Basically, it is a step we can take to get closer to 'if it compiles, it works'.
Rust is being used in more and more platforms which provide limited or no support to net/process/thread etc. Basically, there are ways to solve this:
(1) Re-factor libstd to make it's mods optional. This seems not friendly to existing Rust ecosystems.
(2) 'std-awared' cargo would solve this problem in another way, but left stealthy in-compatibility on upstream targets.
(3) The proposed always_panic
feature gate, which would solve this gently and neatly.
From my experience of security code auditing (~5 years), I think this feature and its checker is similar to MSVC's checker on dangerous functions (such as strcpy), and also a bunch of security tools provides rules against CWE-676 Use of Potentially Dangerous Function. Though panic is not dangerous, it still hide the incompatibility between crates and target platforms which would lead to unexpected behavior. As a compiler, rustc has the ability to prevent this and this approach would increase the trustworthiness of Rust much.
What is the impact of not doing this?
Without the additional semantics of always_panic
, we could never know if a crate is compatible to a platform -- until it triggers a runtime panic, maybe once in a week and we don't know what happened. It's better to get aware of such problem from rustc during compilation.
Prior art
Discuss prior art, both the good and the bad, in relation to this proposal. A few examples of what this can include are:
For language, library, cargo, tools, and compiler proposals: Does this feature exist in other programming languages and what experience have their community had?
- Microsoft's Security features in the CRT
- Coverity's checker on "CWE-676". I cannot find it's document which is open to all.
- sgx_tstd of rust-sgx-sdk, and Parity's pwasm-std . To eliminate such uncertainty of calling unsupported functions, these projects removed unsupported functions from their standard libraries to prevent unpredictable runtime panics.
For community proposals: Is this done by some other community and what were their experiences with it?
Don't have an answer yet.
For other teams: What lessons can we learn from what other communities have done here?
Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background.
- Rust SGX SDK: Towards Memory Safety in Intel SGX Enclave
This paper is accepted as a poster on the SIGSAC's Computer and Communications Security conference, which is a well-known 1st tier system security conference. To eliminate such stealthy incompatibility, the standard library
sgx_tstd
removed the unsupported mod to generate compile-time failures.
Unresolved questions
What parts of the design do you expect to resolve through the RFC process before this gets merged? What parts of the design do you expect to resolve through the implementation of this feature before stabilization? What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC?
Future possibilities
Another dimension of trustworthiness/certainty
Rust seems to be the best language for production-level trusted computing. To accommodate trusted computing with Rust, we need compile-time trustworthiness/certainty semantics. always_panic
is a start which alerts between incompatible codes and target platforms. In future, we may have untrusted_function
to annotate functions brings uncertainty/untrusted inputs to the environment, such as timing in Intel SGX, more specifically, the future mesatee-sgx and mesatee-optee target (soon available maybe next months) which aims at providing hardware-assisted trusted execution environment with Rust's help. Additional features would help us achieve higher-level of trustworthiness and get away from uncertainty much more effectively.
With Trusted Computing, the computer will consistently behave in expected ways, and those behaviors will be enforced by computer hardware and software.
(Chris Mitchell (2005). Trusted Computing. IET. ISBN 978-0-86341-525-8.)