Add examples of good and bad safety documentation. There aren't many examples at the moment, as I hope to add more during discussions, since coming up with examples on my own is very difficult. Signed-off-by: Benno Lossin <benno.lossin@xxxxxxxxx> --- .../rust/safety-standard/examples.rst | 70 +++++++++++++++++++ Documentation/rust/safety-standard/index.rst | 23 ++++-- 2 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 Documentation/rust/safety-standard/examples.rst diff --git a/Documentation/rust/safety-standard/examples.rst b/Documentation/rust/safety-standard/examples.rst new file mode 100644 index 000000000000..d66ef3f8954a --- /dev/null +++ b/Documentation/rust/safety-standard/examples.rst @@ -0,0 +1,70 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. highlight:: rust + +Examples +======== + +Unsound APIs +------------ + +Simple Unsound Function +*********************** +:: + + struct Data { + a: usize, + } + + fn access_a(data: *mut Data) -> usize { + unsafe { (*data).a } + } + +One would normally call this function as follows, which does not trigger UB:: + + fn main() { + let mut d = Data { a: 42 }; + println!("{}", access_a(&mut d)); + } + +However, a caller could also call it like this, which triggers UB using only safe code:: + + fn main() { + println!("{}", access_a(core::ptr::null_mut())); + } + +And this would result in a dereference of a null pointer. + + +Sound ``unsafe`` Code +--------------------- + +The Importance of the API Boundary +********************************** + +Is the following API sound?:: + + fn foo(r: &mut u32) { + let ptr: *mut u32 = r; + let val; + unsafe { + val = *ptr; + *ptr = 0; + } + } + +It better be sound, but one could argue that it is unsound, since one could replace the ptr +initialization by ``ptr = core::ptr::null_mut()``:: + + fn foo(r: &mut u32) { + let ptr: *mut u32 = core::ptr::null_mut(); + let val; + unsafe { + val = *ptr; + *ptr = 0; + } + } + +But this modification is not allowed, since it goes beyond the API boundary of ``foo``. This way +any ``unsafe`` code that relies on surrounding safe code could be shown to be unsound. Instead one +should only consider safe code using the API, in this case, there is no way to make the code +incorrect, since a reference is always valid to dereference during its lifetime. diff --git a/Documentation/rust/safety-standard/index.rst b/Documentation/rust/safety-standard/index.rst index 1cbc8d3dea04..bebebda06831 100644 --- a/Documentation/rust/safety-standard/index.rst +++ b/Documentation/rust/safety-standard/index.rst @@ -92,21 +92,28 @@ And this would result in a dereference of a null pointer. In its essence, a sound API means that if someone only writes safe code, they can never encounter UB even if they call safe code that calls ``unsafe`` code behind the scenes. +For more examples of unsound code see examples.rst. + Because unsoundness issues have the potential for allowing safe code to experience UB, they are -treated similarly to actual bugs with UB. Their fixes should also be included in the stable tree. +treated similarly to real UB. Their fixes should also be included in the stable tree. Safety Documentation ==================== -After trying to minimize and remove as much ``unsafe`` code as possible, there still is some left. -This is because some things are just not possible in only safe code. This last part of ``unsafe`` -code must still be correct. Helping with that is the safety documentation: it meticulously documents -the various requirements and justifications for every line of ``unsafe`` code. That way it can be -ensured that all ``unsafe`` code is sound without anyone needing to know the whole kernel at once. +No matter how hard one tries to remove ``unsafe`` code, it is impossible to completely get rid of it +in the Kernel. There are things that are impossible for safe code. For example interacting with the +C side. So one can never be completely sure that there are no memory issues lurking somewhere. + +This is where safety documentation helps: it meticulously documents the various requirements and +justifications for every line of ``unsafe`` code. That way the risk of writing unsound ``unsafe`` +code is reduced drastically. + The gist of the idea is this: every ``unsafe`` operation documents its requirements and every location that uses an ``unsafe`` operation documents for every requirement a justification why they are fulfilled. If now all requirements and justifications are correct, then there can only be sound -``unsafe`` code. +``unsafe`` code. Reducing the global problem of correctness of the whole kernel to the correctness +of each and every ``unsafe`` code block makes it a local problem. Local problems are a lot easier to +handle, since each instance can be fixed/reviewed independently. The ``unsafe`` keywords has two different meanings depending on the context it is used in: @@ -238,6 +245,8 @@ Further Pages .. toctree:: :maxdepth: 1 + examples + .. only:: subproject and html Indices -- 2.45.1