-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Hey.
I was analyzing https://suiscan.xyz/mainnet/object/0x3d37357bac2e9644920b5c51d403938c32ea76e9ec848b1a526b208e76da1707/contracts and it turned out it is decompiled incorrectly.
In decompiled code you have
let v4 = 0x2::random::generate_u64_in_range(arg2, 1, 10001 + arg1.house_edge);
if (v4 > 10000 && false || arg4 == 0 && v4 >= arg6 && v4 <= arg5 || v4 <= arg6 || v4 >= arg5) {
v1 = v2 + arg3;
};
while it should be more like this:
let v5 = if (v4 > 10000) {
false
} else {
if (arg4 == 0) {
v4 >= arg6 && v4 <= arg5 // Inside bet: win if within range
} else {
v4 <= arg6 || v4 >= arg5 // Outside bet: win if outside range
}
};
if (v5) {
v1 = v2 + arg3;
};
Bellow are more details (generated by claude code based on my findings):
Incorrect Decompilation of Complex Boolean Expression in Move Bytecode v6
Description
The decompiler incorrectly decompiles a complex boolean expression in the one_game function, resulting in code that appears to have a critical security vulnerability when the
actual bytecode implements the logic correctly.
Environment
- Move bytecode version: v6
- Module: 3d37357bac2e9644920b5c51d403938c32ea76e9ec848b1a526b208e76da1707::ufo_range
Expected Behavior
Based on the bytecode analysis, the win condition should be decompiled as:
let v5 = if (v4 > 10000) {
false
} else {
if (arg4 == 0) {
v4 >= arg6 && v4 <= arg5 // Inside bet: win if within range
} else {
v4 <= arg6 || v4 >= arg5 // Outside bet: win if outside range
}
};
if (v5) {
v1 = v2 + arg3;
};
Actual Behavior
The decompiler produced:
if (v4 > 10000 && false || arg4 == 0 && v4 >= arg6 && v4 <= arg5 || v4 <= arg6 || v4 >= arg5) {
v1 = v2 + arg3;
};
This decompiled code creates a logical tautology where (v4 <= arg6 || v4 >= arg5) covers all possible values, making it appear that inside bets (arg4 == 0) always win.
Bytecode Analysis
The relevant bytecode section (simplified):
B22: 137: LdFalse
138: StLoc[13](loc5: bool)
139: Branch(173)
B23: 140: CopyLoc[4](Arg4: u64)
141: LdConst[0](u64: 0)
142: Eq
143: BrFalse(158)
B24: 144-151: // Check if v4 >= arg6 && v4 <= arg5
152: Branch(155)
B28: 158-168: // Check if v4 <= arg6 || v4 >= arg5
B32: 171: MoveLoc[12](loc4: bool)
172: StLoc[13](loc5: bool)
B33: 173: MoveLoc[13](loc5: bool)
174: BrFalse(179)
B34: 175-178: // Set payout if loc5 is true
The bytecode clearly shows:
- Different logic paths for arg4 == 0 (inside bet) vs arg4 == 1 (outside bet)
- Proper boolean evaluation with intermediate results stored in locals
- No tautology - the conditions are correctly scoped
Impact
This decompilation error led to:
- False security vulnerability report: The incorrect decompilation made it appear there was a critical exploit allowing guaranteed wins
- Misunderstanding of contract behavior: Multiple security analyses were performed based on the incorrect decompiled code
- Wasted resources: Time spent analyzing and attempting to exploit a non-existent vulnerability
Reproduction Steps
- Decompile the bytecode for function one_game in the specified module
- Observe the boolean expression for the win condition around bytecode positions 133-174
- Compare with manual bytecode analysis to see the discrepancy
Suggested Fix
The decompiler should:
- Better handle nested conditional logic with multiple branches
- Preserve the semantic structure of if-else chains rather than flattening to a single boolean expression
- Consider the scope of boolean operators when reconstructing complex conditions
Additional Context
This issue was discovered when investigating what appeared to be a critical security vulnerability in a gaming smart contract. The decompiled code suggested that players could
achieve ~99% win rates, but actual testing showed normal loss rates. Manual bytecode analysis revealed the decompiler had incorrectly reconstructed the boolean logic.