Attackathon _ Fuel Network 33191 - [Smart Contract - Insight] Sway Formatting Behaves Differently Ba
Submitted on Sat Jul 13 2024 19:27:00 GMT-0400 (Atlantic Standard Time) by @savi0ur for Attackathon | Fuel Network
Report ID: #33191
Report type: Smart Contract
Report severity: Insight
Target: https://github.com/FuelLabs/sway/tree/v0.61.2
Impacts:
Compiler bug
Description
Inside sway module, we have swayfmt module which does a job of formatting sway language code, similar to what rustfmt do for rust lang.
https://github.com/FuelLabs/sway/blob/e1b1c2bee73e0ba825e07736cefa6c0abd079595/swayfmt/src/utils/map/newline.rs#L144-L150
fn calculate_offset(base: usize, offset: i64) -> usize {
offset
.checked_add(base as i64)
.unwrap_or(base as i64)
.try_into()//@audit-issue conversion discrepancies on 32/64 bit machine
.unwrap_or(base)
}It have a calculate_offset function which is behaving differently based on which platform it runs.
For example, When run on 64 bit machine, where usize is 64 bit wide, if offset is > u32::MAX, it will return new offset as offset + base by converting this result to usize. Since usize is 64 bit wide, it will be able to store this result.
But when run on 32-bit machine, where usize is 32 bit wide, if offset is > u32::MAX, and base is non-zero, calculate_offset function will return base instead of offset + base.
As we have seen, based on the platform architecture, this function returning different offset.
Impact
Sway formatting behaves differently based on architecture of the system.
Recommendation
Avoid conversions that may behave differently across architectures. Use architecture independent data types for performing required operations.
References
https://github.com/FuelLabs/sway/blob/e1b1c2bee73e0ba825e07736cefa6c0abd079595/swayfmt/src/utils/map/newline.rs#L144-L150
Proof of concept
Proof Of Concept
Steps to Run using Foundry:
Change directory to
swaydirectory i.e.,cd swayCopy following test case in
swayfmt/tests/mod.rsOpen terminal and run
cargo test --package swayfmt --test mod -- test_calculate_offset_bug_diff_arch --exact --show-output
#[test]
fn test_calculate_offset_bug_diff_arch() {
fn calculate_offset(base: usize, offset: i64) -> usize {
offset
.checked_add(base as i64)
.unwrap_or(base as i64)
.try_into()
.unwrap_or(base)
}
let base: usize = 10000;
let offset: i64 = u32::MAX as i64 + 10;
let res = calculate_offset(base, offset);
if cfg!(target_pointer_width = "64") {
assert!(res != base);
} else {
assert!(res == base);
}
}Console Output:
On 64-bit machine, this test case will execute successfully with
res != base.On 32-bit machine, this test case will execute successfully with
res == base.
Last updated
Was this helpful?