Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: avoid integer overflows #2713

Merged
merged 73 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
c3c7ba5
error on integer overflow
guipublic Sep 15, 2023
a783b24
range constraint block box is not supported in brillig
guipublic Sep 15, 2023
0ae7b49
Merge branch 'master' into gd/integer_overflow
guipublic Sep 18, 2023
cddd13b
Merge branch 'master' into gd/integer_overflow
guipublic Sep 20, 2023
0511c9d
Merge branch 'master' into gd/integer_overflow
guipublic Sep 25, 2023
187f77a
Merge branch 'master' into gd/integer_overflow
guipublic Sep 29, 2023
8e611d3
note: fix signed arithmetic
vezenovm Oct 9, 2023
558a6b0
Merge branch 'master' into gd/integer_overflow
guipublic Oct 11, 2023
2ed45ac
check overflow for signed integers
guipublic Oct 12, 2023
c4c4f3c
code review
guipublic Oct 12, 2023
f9cd90d
Merge branch 'master' into gd/integer_overflow
guipublic Oct 12, 2023
e0f71d1
Merge branch 'master' into gd/integer_overflow
guipublic Oct 16, 2023
d08ca79
Fix issue with sha2_byte
guipublic Oct 16, 2023
458fde9
remove debug code
guipublic Oct 16, 2023
34292f5
Merge branch 'master' into gd/integer_overflow
guipublic Oct 16, 2023
fa2d1e4
Merge branch 'master' into gd/integer_overflow
guipublic Oct 18, 2023
f7333c4
Merge branch 'master' into gd/integer_overflow
guipublic Oct 24, 2023
f214e7d
chore: Update ACIR artifacts (#3263)
kevaundray Oct 24, 2023
3d50049
fix: recompile artefacts from a different noir version (#3248)
guipublic Oct 24, 2023
8528283
feat(debugger): Print limited source code context (#3217)
mverzilli Oct 24, 2023
7c9b414
chore(docs): Document `nargo fmt` (#3262)
Savio-Sou Oct 24, 2023
7c4444f
chore(docs): Rearrange NoirJS section (#3260)
Savio-Sou Oct 24, 2023
e1d5077
feat: noir-wasm takes dependency graph (#3213)
alexghr Oct 24, 2023
9e9dc5d
feat!: Switch to new pedersen implementation (#3151)
kevaundray Oct 24, 2023
84f408e
feat: replace boolean range constraints with arithmetic opcodes (#3234)
TomAFrench Oct 24, 2023
66a8453
feat: implement euclidean division and signed division in terms of `A…
TomAFrench Oct 24, 2023
3f4f2ee
chore: fix empty constructor formatting (#3265)
Oct 24, 2023
9ce4237
feat: add crate for pub modifier (#3271)
guipublic Oct 24, 2023
bc02046
chore: method specialization (#3268)
signorecello Oct 24, 2023
a2b078f
fix: Fix lexer error formatting (#3274)
jfecher Oct 24, 2023
5c3fb2d
chore: standardize workflow triggers (#3277)
TomAFrench Oct 24, 2023
7b08b21
chore: add constrain formatter (#3272)
Oct 24, 2023
6ae06ed
fix: Add size checks to integer literals (#3236)
jfecher Oct 24, 2023
12fc657
feat: implement `bound_constraint_with_offset` in terms of `AcirVar`s…
TomAFrench Oct 24, 2023
33c083e
chore: adding fix to master branch (#3279)
signorecello Oct 24, 2023
a456bdb
feat!: expose pedersen hash in acir and bb solver (#3269)
sirasistant Oct 25, 2023
0d07ad9
chore: turn off `aztec` flag by default (#3280)
TomAFrench Oct 25, 2023
7b7f861
chore: Release Noir(0.18.0) (#3242)
kevaundray Oct 25, 2023
2917c01
chore: pinning NoirJS guide versions to 0.17.0 and adding note on noi…
signorecello Oct 25, 2023
cd6c003
feat: Extract Brillig VM to allow step debugging (#3259)
ggiraldez Oct 25, 2023
e5eda81
feat: perform compile-time euclidean division on constants (#3231)
TomAFrench Oct 25, 2023
3f0bd3a
chore: update artefacts (#3287)
kevaundray Oct 25, 2023
7a534e9
chore: remove `default` empty features array (#3285)
alexghr Oct 25, 2023
c08fd00
chore(docs): Cut v0.17.0 docs (#3290)
Savio-Sou Oct 25, 2023
3758f19
chore: update relative paths for docs imports (#3291)
critesjosh Oct 25, 2023
2d3d6f6
chore(docs): Recommend `yarn` instead of for cutting doc versions (#3…
Savio-Sou Oct 25, 2023
593f3c1
feat: Allow a trait to be implemented multiple times for the same str…
jfecher Oct 25, 2023
a5245b2
chore: format submodule/contract (#3286)
Oct 25, 2023
e8a9274
chore(github): Update PR template per new documentation workflow (#3241)
Savio-Sou Oct 25, 2023
23bb4ce
feat: handle warnings in evaluator (#3205)
guipublic Oct 26, 2023
b9e5b5f
feat: `compute_note_hash_and_nullifier` check (#3216)
benesjan Oct 26, 2023
7950bc9
feat: Make generic impls callable (#3297)
jfecher Oct 26, 2023
ab982ef
chore: create publish-docs.yml (#3298)
signorecello Oct 26, 2023
219f862
chore(docs): Supplement descriptions for defaulting loop indices to b…
Savio-Sou Oct 26, 2023
881add7
chore: bump bb version to 0.12.0 (#3304)
TomAFrench Oct 26, 2023
07f4f93
chore: upload acir artifacts as a github artifact (#3288)
TomAFrench Oct 26, 2023
1c01fbd
feat: add exports of JS black box solvers to noirJS (#3295)
TomAFrench Oct 26, 2023
ffacd52
chore: add bn254 attribute when needed in the stdlib (#3208)
guipublic Oct 26, 2023
fc03aa4
chore: recrawl docs on merge (#3306)
signorecello Oct 26, 2023
bab2fca
Merge branch 'master' into gd/integer_overflow
guipublic Oct 30, 2023
04663f4
fix the merge
guipublic Oct 30, 2023
339ae8a
feat!: expose pedersen hash in acir and bb solver (#3269)
guipublic Oct 30, 2023
3af8912
Merge branch 'master' into gd/integer_overflow
guipublic Oct 30, 2023
2ec6998
Merge branch 'master' into gd/integer_overflow
guipublic Oct 30, 2023
76ffae7
Merge branch 'master' into gd/integer_overflow
guipublic Oct 30, 2023
5358c10
Merge branch 'master' into gd/integer_overflow
guipublic Oct 30, 2023
e74c98d
fix merge issues
guipublic Oct 30, 2023
6d259c1
fix test case
guipublic Oct 31, 2023
024a949
Merge branch 'master' into gd/integer_overflow
guipublic Oct 31, 2023
7623e97
Merge branch 'master' into gd/integer_overflow
kevaundray Oct 31, 2023
acdd2db
Code review
guipublic Oct 31, 2023
84131b8
Merge branch 'master' into gd/integer_overflow
guipublic Oct 31, 2023
f13b01c
Merge branch 'master' into gd/integer_overflow
guipublic Oct 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use acvm::brillig_vm::brillig::HeapVector;
use acvm::FieldElement;
use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet};
use iter_extended::vecmap;
use num_bigint::BigUint;

use super::brillig_black_box::convert_black_box_call;
use super::brillig_block_variables::BlockVariables;
Expand Down Expand Up @@ -554,6 +555,25 @@ impl<'block> BrilligBlock<'block> {
value_variable,
);
}
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
let left = self.convert_ssa_register_value(*value, dfg);
let max = BigUint::from(2_u128).pow(*max_bit_size);
let right = self.brillig_context.allocate_register();
self.brillig_context.const_instruction(
right,
FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(),
);

let brillig_binary_op = BrilligBinaryOp::Integer {
op: BinaryIntOp::LessThan,
bit_size: max_bit_size + 1,
};
let condition = self.brillig_context.allocate_register();
self.brillig_context.binary_instruction(left, right, condition, brillig_binary_op);
self.brillig_context.constrain_instruction(condition, assert_message.clone());
self.brillig_context.deallocate_register(condition);
self.brillig_context.deallocate_register(right);
}
_ => todo!("ICE: Instruction not supported {instruction:?}"),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,11 +597,17 @@ impl AcirContext {
&mut self,
variable: AcirVar,
numeric_type: &NumericType,
message: Option<String>,
) -> Result<AcirVar, RuntimeError> {
match numeric_type {
NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => {
let witness = self.var_to_witness(variable)?;
self.acir_ir.range_constraint(witness, *bit_size)?;
if let Some(message) = message {
self.acir_ir
.assert_messages
.insert(self.acir_ir.last_acir_opcode_location(), message);
}
}
NumericType::NativeField => {
// Range constraining a Field is a no-op
Expand Down
10 changes: 9 additions & 1 deletion compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ impl Context {
) -> Result<AcirVar, RuntimeError> {
let acir_var = self.acir_context.add_variable();
if matches!(numeric_type, NumericType::Signed { .. } | NumericType::Unsigned { .. }) {
self.acir_context.range_constrain_var(acir_var, numeric_type)?;
self.acir_context.range_constrain_var(acir_var, numeric_type, None)?;
}
Ok(acir_var)
}
Expand Down Expand Up @@ -529,6 +529,14 @@ impl Context {
Instruction::Load { .. } => {
unreachable!("Expected all load instructions to be removed before acir_gen")
}
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
let acir_var = self.convert_numeric_value(*value, dfg)?;
self.acir_context.range_constrain_var(
acir_var,
&NumericType::Unsigned { bit_size: *max_bit_size },
assert_message.clone(),
)?;
}
}
self.acir_context.set_call_stack(CallStack::new());
Ok(())
Expand Down
28 changes: 23 additions & 5 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ pub(crate) enum Instruction {
/// Constrains two values to be equal to one another.
Constrain(ValueId, ValueId, Option<String>),

/// Range constrain `value` to `max_bit_size`
RangeCheck { value: ValueId, max_bit_size: u32, assert_message: Option<String> },

/// Performs a function call with a list of its arguments.
Call { func: ValueId, arguments: Vec<ValueId> },

Expand Down Expand Up @@ -199,7 +202,8 @@ impl Instruction {
Instruction::ArraySet { array, .. } => InstructionResultType::Operand(*array),
Instruction::Constrain(..)
| Instruction::Store { .. }
| Instruction::EnableSideEffects { .. } => InstructionResultType::None,
| Instruction::EnableSideEffects { .. }
| Instruction::RangeCheck { .. } => InstructionResultType::None,
Instruction::Load { .. } | Instruction::ArrayGet { .. } | Instruction::Call { .. } => {
InstructionResultType::Unknown
}
Expand All @@ -226,9 +230,12 @@ impl Instruction {
Truncate { .. } => false,

// These either have side-effects or interact with memory
Constrain(..) | EnableSideEffects { .. } | Allocate | Load { .. } | Store { .. } => {
false
}
Constrain(..)
| EnableSideEffects { .. }
| Allocate
| Load { .. }
| Store { .. }
| RangeCheck { .. } => false,

Call { func, .. } => match dfg[*func] {
Value::Intrinsic(intrinsic) => !intrinsic.has_side_effects(),
Expand All @@ -250,7 +257,7 @@ impl Instruction {
| ArrayGet { .. }
| ArraySet { .. } => false,

Constrain(..) | Store { .. } | EnableSideEffects { .. } => true,
Constrain(..) | Store { .. } | EnableSideEffects { .. } | RangeCheck { .. } => true,

// Some `Intrinsic`s have side effects so we must check what kind of `Call` this is.
Call { func, .. } => match dfg[*func] {
Expand Down Expand Up @@ -307,6 +314,13 @@ impl Instruction {
Instruction::ArraySet { array, index, value } => {
Instruction::ArraySet { array: f(*array), index: f(*index), value: f(*value) }
}
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
Instruction::RangeCheck {
value: f(*value),
max_bit_size: *max_bit_size,
assert_message: assert_message.clone(),
}
}
}
}

Expand Down Expand Up @@ -351,6 +365,9 @@ impl Instruction {
Instruction::EnableSideEffects { condition } => {
f(*condition);
}
Instruction::RangeCheck { value, .. } => {
f(*value);
}
}
}

Expand Down Expand Up @@ -448,6 +465,7 @@ impl Instruction {
Instruction::Allocate { .. } => None,
Instruction::Load { .. } => None,
Instruction::Store { .. } => None,
Instruction::RangeCheck { .. } => None,
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,5 +172,8 @@ pub(crate) fn display_instruction(
show(*value)
)
}
Instruction::RangeCheck { value, max_bit_size, .. } => {
write!(f, "range {} to {} bits", show(*value), *max_bit_size,)
jfecher marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
36 changes: 25 additions & 11 deletions compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::ssa::function_builder::FunctionBuilder;
use crate::ssa::ir::dfg::DataFlowGraph;
use crate::ssa::ir::function::FunctionId as IrFunctionId;
use crate::ssa::ir::function::{Function, RuntimeType};
use crate::ssa::ir::instruction::{BinaryOp, Endian, Intrinsic};
use crate::ssa::ir::instruction::{BinaryOp, Endian, Instruction, Intrinsic};
use crate::ssa::ir::map::AtomicCounter;
use crate::ssa::ir::types::{NumericType, Type};
use crate::ssa::ir::value::ValueId;
Expand Down Expand Up @@ -313,21 +313,35 @@ impl<'a> FunctionContext<'a> {
}
};

if let Some(max_bit_size) = operator_result_max_bit_size_to_truncate(
// Check for integer overflow
if matches!(
operator,
lhs,
rhs,
&self.builder.current_function.dfg,
BinaryOpKind::Add
| BinaryOpKind::Subtract
| BinaryOpKind::Multiply
| BinaryOpKind::ShiftLeft
jfecher marked this conversation as resolved.
Show resolved Hide resolved
) {
let result_type = self.builder.current_function.dfg.type_of_value(result);
let bit_size = match result_type {
match result_type {
Type::Numeric(NumericType::Signed { bit_size })
| Type::Numeric(NumericType::Unsigned { bit_size }) => bit_size,
_ => {
unreachable!("ICE: Truncation attempted on non-integer");
| Type::Numeric(NumericType::Unsigned { bit_size }) => {
let op_name = match operator {
BinaryOpKind::Add => "add",
BinaryOpKind::Subtract => "subtract",
BinaryOpKind::Multiply => "multiply",
BinaryOpKind::ShiftLeft => "left shift",
_ => unreachable!("operator {} should not overflow", operator),
};
let message = format!("attempt to {} with overflow", op_name);
let range_constraint = Instruction::RangeCheck {
value: result,
max_bit_size: bit_size,
assert_message: Some(message),
};
self.builder.set_location(location).insert_instruction(range_constraint, None);
}
};
result = self.builder.insert_truncate(result, bit_size, max_bit_size);
_ => (),
}
}

if operator_requires_not(operator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ fn main() {
let x = -1;
assert(x == 1 - 2);

// TODO: subtraction for signed integers
// This will error with 'attempt to subtract with overflow'
let y: i32 = -1;
assert(x == 1 - 2);
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
x = "0"
x = "5"
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ fn main(mut x: i32, mut y: i32, mut z: i32) {
assert(x / y == z);

// -7/3 = -2
let minus_x = std::wrapping_sub(0,x);
let zero: i32 = 0;
let minus_x: i32 = std::wrapping_sub(zero,x);
let minus_z = std::wrapping_sub(0,z);
let minus_y = std::wrapping_sub(0,y);
// TODO: Fix addition and subtraction for signed integers
// We should not have to wrap to do 7 + (-7)
// let x_plus_minus_x = std::wrapping_add(x, minus_x);
// This will print `0` as we expect for normal signed addition
// std::println(x_plus_minus_x);
assert(x+minus_x == 0);
assert(z+minus_z == 0);
assert(minus_x / y == minus_z);
Expand Down
Loading