Attackathon _ Fuel Network 32270 - [Smart Contract - Low] Inappropriate fuel dce on side affects
Submitted on Mon Jun 17 2024 08:24:54 GMT-0400 (Atlantic Standard Time) by @anatomist for Attackathon | Fuel Network
Report ID: #32270
Report type: Smart Contract
Report severity: Low
Target: https://github.com/FuelLabs/sway/tree/7b56ec734d4a4fda550313d448f7f20dba818b59
Impacts:
Incorrect sway optimization leading to unexpected bytecode
Description
Brief/Intro
The sway compiler may remove expressions that potentially has side affects during dce pass, and eventually change behavior of program.
Vulnerability Details
The has_side_effect
function is used to check whether an operation may have untrackable effects. Any operation with untrackable effect should never be removed by dce
optimization. However, the function doesn't has_side_effect
function doesn't correctly consider that all arithmetic opcodes may panic on illegal operands or overflows.
pub(crate) fn has_side_effect(&self) -> bool {
use VirtualOp::*;
(match self {
...
DIV(_, _, _) => self.def_registers().iter().any(|vreg| matches!(vreg, VirtualRegister::Constant(_))),
...
})
.into_iter()
.collect()
}
Thus if dce
considers the output of an arithmetic instruction to be "dead", it would remove the instruction regardless of whether a panic may happen due to calculation.
To show what this means, we look at the test case below. Since the result of let c = 1 / 0
is never used, dce
removed the div $r70 $one $zero
instruction, changing the behavior of the program.
Source Script
fn main() -> () {
let c = 1 / 0;
}
Abstract instructions before dce
.program:
.2 ; --- start of function: strange_panic ---
move $$locbase $sp ; save locals base register for strange_panic
cfei i0 ; allocate 0 bytes for locals and 0 slots for call arguments.
.5
div $r70 $one $zero
ret $zero ; returning unit as zero
Abstract instructions after dce
.program:
.2 ; --- start of function: strange_panic ---
move $$locbase $sp ; save locals base register for strange_panic
cfei i0 ; allocate 0 bytes for locals and 0 slots for call arguments.
.5
ret $zero ; returning unit as zero
Impact Details
This would potentially change the behavior of programs. The example provided is relatively harmless, but there could be cases where developers rely on a let c = a + b
panic to check whether a + b
overflows, but otherwise leaves c
unused. The incorrect elimination of such instructions would result in removal of such checks.
References
https://github.com/FuelLabs/sway/blob/7b56ec734d4a4fda550313d448f7f20dba818b59/sway-core/src/asm_lang/virtual_ops.rs#L328
https://github.com/FuelLabs/sway/blob/7b56ec734d4a4fda550313d448f7f20dba818b59/sway-core/src/asm_generation/fuel/optimizations.rs#L239
Proof of concept
Proof of Concept
#[test]
fn incorrect_neglect_of_side_effects() -> () {
let c = 1 / 0;
}
Last updated
Was this helpful?