-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #312 from CoinFabrik/migrate-avoid-unsafe-block-do…
…cumentation New avoid-unsafe-block documentation
- Loading branch information
Showing
1 changed file
with
51 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,74 @@ | ||
# Avoid unsafe block | ||
|
||
### What it does | ||
## Description | ||
|
||
Checks for usage of `unsafe` blocks. | ||
- Category: `Validations and error handling` | ||
- Severity: `Critical` | ||
- Detector: [`avoid-unsafe-block`](https://github.com/CoinFabrik/scout-soroban/tree/main/detectors/avoid-unsafe-block) | ||
- Test Cases: [`avoid-unsafe-block-1`](https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/avoid-unsafe-block/avoid-unsafe-block-1) | ||
|
||
The use of unsafe blocks in Rust is generally discouraged due to the potential risks it poses to the safety and reliability of the code. Rust's primary appeal lies in its ability to provide memory safety guarantees, which are largely enforced through its ownership and type systems. When you enter an unsafe block, you're effectively bypassing these safety checks. These blocks require the programmer to manually ensure that memory is correctly managed and accessed, which is prone to human error and can be challenging even for experienced developers. Therefore, unsafe blocks should only be used when absolutely necessary and when the safety of the operations within can be assured. | ||
|
||
### Why is this bad? | ||
## Why is this bad? | ||
|
||
`unsafe` blocks should not be used unless absolutely necessary. The use of unsafe blocks in Rust is discouraged because they bypass Rust's memory safety checks, potentially leading to issues like undefined behavior and security vulnerabilities. | ||
|
||
### Example | ||
## Issue example | ||
|
||
```rust | ||
pub fn unsafe_function(n: u64) -> u64 { | ||
unsafe { | ||
let mut i = n as f64; | ||
let mut y = i.to_bits(); | ||
y = 0x5fe6ec85e7de30da - (y >> 1); | ||
i = f64::from_bits(y); | ||
i *= 1.5 - 0.5 * n as f64 * i * i; | ||
i *= 1.5 - 0.5 * n as f64 * i * i; | ||
Consider the following `Soroban` contract: | ||
|
||
let result_ptr: *mut f64 = &mut i; | ||
let result = *result_ptr; | ||
```rust | ||
#[contractimpl] | ||
impl AvoidUnsafeBlock { | ||
pub fn unsafe_function(n: u64) -> u64 { | ||
unsafe { | ||
let mut i = n as f64; | ||
let mut y = i.to_bits(); | ||
y = 0x5fe6ec85e7de30da - (y >> 1); | ||
i = f64::from_bits(y); | ||
i *= 1.5 - 0.5 * n as f64 * i * i; | ||
i *= 1.5 - 0.5 * n as f64 * i * i; | ||
|
||
result.to_bits() | ||
} | ||
let result_ptr: *mut f64 = &mut i; | ||
|
||
(*result_ptr).to_bits() | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Use instead: | ||
In this example we can see that it creates a raw pointer named `result_ptr`. Then `(*result_ptr).to_bits()` dereferences the raw pointer. This directly accesses the memory location and calls the `to_bits` method on the value stored at that location. | ||
|
||
Raw pointers bypass Rust's type safety system and memory management features. If something goes wrong with the calculations or the value of n, dereferencing the pointer could lead to a memory access violations or undefined behavior. | ||
|
||
```rust | ||
pub fn unsafe_function(n: u64) -> u64 { | ||
The code example can be found [here](https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/vulnerable-example). | ||
|
||
|
||
## Remediated example | ||
|
||
By removing the raw pointer, the following version eliminates the issue associated with dereferencing memory in an unsafe way. Rust's type safety checks ensure memory is accessed correctly, preventing the potential issues mentioned earlier. | ||
|
||
```rust | ||
#[contractimpl] | ||
impl AvoidUnsafeBlock { | ||
pub fn unsafe_function(n: u64) -> u64 { | ||
let mut i = n as f64; | ||
let mut y = i.to_bits(); | ||
y = 0x5fe6ec85e7de30da - (y >> 1); | ||
i = f64::from_bits(y); | ||
i *= 1.5 - 0.5 * n as f64 * i * i; | ||
i *= 1.5 - 0.5 * n as f64 * i * i; | ||
result.to_bits() | ||
} | ||
i.to_bits() | ||
} | ||
} | ||
``` | ||
|
||
### Implementation | ||
The remediated code example can be found [here](https://github.com/CoinFabrik/scout-soroban/tree/main/test-cases/avoid-unsafe-block/avoid-unsafe-block-1/remediated-example). | ||
|
||
## How is it detected? | ||
|
||
Checks for usage of `unsafe` blocks. | ||
|
||
|
||
|
||
The detector's implementation can be found at [this link](https://github.com/CoinFabrik/scout-soroban/tree/main/detectors/avoid-unsafe-block). | ||