-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
142 additions
and
0 deletions.
There are no files selected for viewing
109 changes: 109 additions & 0 deletions
109
external-crates/move/crates/move-compiler/src/linters/shift_overflow.rs
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 |
---|---|---|
@@ -0,0 +1,109 @@ | ||
//! Detect potential overflow scenarios where the number of bits being shifted exceeds the bit width of | ||
//! the variable being shifted, which could lead to unintended behavior or loss of data. If such a | ||
//! potential overflow is detected, a warning is generated to alert the developer. | ||
use crate::{ | ||
diag, | ||
diagnostics::{ | ||
codes::{custom, DiagnosticInfo, Severity}, | ||
WarningFilters, | ||
}, | ||
expansion::ast::Value_, | ||
naming::ast::{BuiltinTypeName_, TypeName_, Type_}, | ||
parser::ast::BinOp_, | ||
shared::{program_info::TypingProgramInfo, CompilationEnv}, | ||
typing::{ | ||
ast::{self as T, UnannotatedExp_}, | ||
visitor::{TypingVisitorConstructor, TypingVisitorContext}, | ||
}, | ||
}; | ||
use move_ir_types::location::Loc; | ||
use std::str::FromStr; | ||
|
||
use super::{LinterDiagCategory, LINTER_DEFAULT_DIAG_CODE, LINT_WARNING_PREFIX}; | ||
|
||
const SHIFT_OPERATION_OVERFLOW_DIAG: DiagnosticInfo = custom( | ||
LINT_WARNING_PREFIX, | ||
Severity::Warning, | ||
LinterDiagCategory::ShiftOperationOverflow as u8, | ||
LINTER_DEFAULT_DIAG_CODE, | ||
"Potential overflow detected. The number of bits being shifted exceeds the bit width of the variable being shifted.", | ||
); | ||
|
||
pub struct ShiftOperationOverflow; | ||
|
||
pub struct Context<'a> { | ||
env: &'a mut CompilationEnv, | ||
} | ||
|
||
impl TypingVisitorConstructor for ShiftOperationOverflow { | ||
type Context<'a> = Context<'a>; | ||
|
||
fn context<'a>( | ||
env: &'a mut CompilationEnv, | ||
_program_info: &'a TypingProgramInfo, | ||
_program: &T::Program_, | ||
) -> Self::Context<'a> { | ||
Context { env } | ||
} | ||
} | ||
|
||
impl TypingVisitorContext for Context<'_> { | ||
fn visit_exp_custom(&mut self, exp: &mut T::Exp) -> bool { | ||
// Check if the expression is a binary operation and if it is a shift operation. | ||
if let UnannotatedExp_::BinopExp(lhs, op, _, rhs) = &exp.exp.value { | ||
// can't do let UnannotatedExp_::BinopExp(lhs, BinOp_::Shl | BinOp_::Shr, _, rhs) = &exp.exp.value else { return }; | ||
// because the op is Spanned<BinOp_> and not BinOp_ | ||
if matches!(op.value, BinOp_::Shl | BinOp_::Shr) { | ||
match ( | ||
get_bit_width(&lhs.ty.value), | ||
get_shift_amount(&rhs.exp.value), | ||
) { | ||
(Some(bit_width), Some(shift_amount)) if shift_amount >= bit_width => { | ||
report_overflow(self.env, shift_amount, bit_width, op.loc); | ||
} | ||
_ => (), | ||
} | ||
} | ||
} | ||
false | ||
} | ||
fn add_warning_filter_scope(&mut self, filter: WarningFilters) { | ||
self.env.add_warning_filter_scope(filter) | ||
} | ||
|
||
fn pop_warning_filter_scope(&mut self) { | ||
self.env.pop_warning_filter_scope() | ||
} | ||
} | ||
|
||
fn get_bit_width(ty: &Type_) -> Option<u128> { | ||
ty.builtin_name().and_then(|typ| match typ.value { | ||
BuiltinTypeName_::U8 => Some(8), | ||
BuiltinTypeName_::U16 => Some(16), | ||
BuiltinTypeName_::U32 => Some(32), | ||
BuiltinTypeName_::U64 => Some(64), | ||
BuiltinTypeName_::U128 => Some(128), | ||
BuiltinTypeName_::U256 => Some(256), | ||
_ => None, | ||
}) | ||
} | ||
|
||
fn get_shift_amount(value: &UnannotatedExp_) -> Option<u128> { | ||
if let UnannotatedExp_::Value(v) = value { | ||
match &v.value { | ||
Value_::U8(v) => Some(*v as u128), | ||
_ => None, | ||
} | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
fn report_overflow(env: &mut CompilationEnv, shift_amount: u128, bit_width: u128, loc: Loc) { | ||
let msg = format!( | ||
"The {} of bits being shifted exceeds the {} bit width of the variable being shifted.", | ||
shift_amount, bit_width | ||
); | ||
let diag = diag!(SHIFT_OPERATION_OVERFLOW_DIAG, (loc, msg)); | ||
env.add_diag(diag); | ||
} |
24 changes: 24 additions & 0 deletions
24
external-crates/move/crates/move-compiler/tests/custom_rules/shift_overflow.exp
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 |
---|---|---|
@@ -0,0 +1,24 @@ | ||
warning[Lint W00001]: Potential overflow detected. The number of bits being shifted exceeds the bit width of the variable being shifted. | ||
┌─ tests/custom_rules/shift_overflow.move:5:20 | ||
│ | ||
5 │ let _b = x << 64; // Should not raise an issue | ||
│ ^^ The 64 of bits being shifted exceeds the 64 bit width of the variable being shifted. | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(shift_overflow))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
||
warning[Lint W00001]: Potential overflow detected. The number of bits being shifted exceeds the bit width of the variable being shifted. | ||
┌─ tests/custom_rules/shift_overflow.move:6:20 | ||
│ | ||
6 │ let _b = x << 65; // Should raise an issue | ||
│ ^^ The 65 of bits being shifted exceeds the 64 bit width of the variable being shifted. | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(shift_overflow))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
||
warning[Lint W00001]: Potential overflow detected. The number of bits being shifted exceeds the bit width of the variable being shifted. | ||
┌─ tests/custom_rules/shift_overflow.move:7:20 | ||
│ | ||
7 │ let _b = x >> 66; // Should raise an issue | ||
│ ^^ The 66 of bits being shifted exceeds the 64 bit width of the variable being shifted. | ||
│ | ||
= This warning can be suppressed with '#[allow(lint(shift_overflow))]' applied to the 'module' or module member ('const', 'fun', or 'struct') | ||
|
9 changes: 9 additions & 0 deletions
9
external-crates/move/crates/move-compiler/tests/custom_rules/shift_overflow.move
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 |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module 0x42::M { | ||
|
||
fun func1(x: u64) { | ||
let _b = x << 24; | ||
let _b = x << 64; // Should raise an issue | ||
let _b = x << 65; // Should raise an issue | ||
let _b = x >> 66; // Should raise an issue | ||
} | ||
} |