---
# System prepended metadata

title: "\U0001F512 NOIR COMPILER SSA OPTIMIZATION BUGS"

---

# 🔒 NOIR COMPILER SSA OPTIMIZATION BUGS
## Security Audit Report - 3 Critical Issues

**Date:** October 13, 2025  
**Scope:** SSA optimization passes in `compiler/noirc_evaluator/src/ssa/opt/`

---

## BUG #1: Array Set Multi-Block Unsoundness ⚠️ HIGH

**File:** `array_set.rs` (Lines 66-72, 133-135, 202-211)

**Issue:** Optimization allows entry-point functions with multiple blocks but uses single-block analysis, causing incorrect mutable markings.

**Root Cause:**
```rust
// Precondition allows multi-block entry points
if !func.runtime().is_entry_point() {
    assert_eq!(reachable_blocks.len(), 1, /*...*/);
}

// But analysis is per-block without inter-block dataflow
for block in self.reachable_blocks() {
    context.analyze_last_uses(block);  // ← Independent analysis
}
```

**Counter-Example:**
```rust
acir(inline) entry_point fn main f0 {
  b0():
    v1 = make_array [Field 0]
    v2 = array_set v1, index u32 0, value Field 1  // OUTPUT: v2
    jmp b1
  b1():
    v3 = array_get v2, index u32 0  // USES v2 from b0
    return v3
}
// Result: ArraySet incorrectly marked mutable (checks INPUT v1, not OUTPUT v2)
```

**Impact:** Array aliasing violations, potential memory corruption

**Fix:** Either assert entry points have 1 block, or skip multi-block entry points

---

## BUG #2: Constrain Not-Equal Operand Order 🟡 LOW

**File:** `make_constrain_not_equal.rs` (Lines 79-81)

**Issue:** Only handles `constrain v2 == u1 0`, not reversed `constrain u1 0 == v2`

**Code:**
```rust
if !context.dfg.get_numeric_constant(*rhs).is_some_and(|c| c.is_zero()) {
    return;  // ← Only checks RHS is zero
}
```

**Impact:** LOW - Comments (line 67) indicate frontend guarantees order, but makes pass brittle

**Fix:** Handle both operand orders:
```rust
let (eq_value, _) = if dfg.get_numeric_constant(*rhs).is_some_and(|c| c.is_zero()) {
    (*lhs, *rhs)
} else if dfg.get_numeric_constant(*lhs).is_some_and(|c| c.is_zero()) {
    (*rhs, *lhs)
} else {
    return;
};
```

---

## BUG #3: Bit Shift Overflow Check Wrong Type 🔴 CRITICAL

**File:** `remove_bit_shifts.rs` (Lines 372-386, call site 119)

**Issue:** Checks `rhs < bit_size_of(rhs_type)` instead of `rhs < bit_size_of(lhs_type)`

**Code:**
```rust
fn enforce_bitshift_rhs_lt_bit_size(&mut self, rhs: ValueId) {
    let rhs_type = self.context.dfg.type_of_value(rhs);  // ← WRONG
    let bit_size = rhs_type.bit_size();  // Should use LHS bit size!
    // ...
    let overflow = self.insert_binary(rhs, BinaryOp::Lt, max);
}
```

**Counter-Examples:**
```rust
// Case 1: FALSE NEGATIVE (accepts invalid code)
let x: u8 = 1;
let shift: u32 = 10;
x << shift;  
// Current: Checks 10 < 32 ✓ PASSES (WRONG!)
// Should: Check 10 < 8 ✗ FAILS (correct - shift too large for u8)

// Case 2: FALSE POSITIVE (rejects valid code)  
let x: u64 = 1;
let shift: u8 = 40;  
x << shift;  
// Current: Checks 40 < 8 ✗ FAILS (WRONG!)
// Should: Check 40 < 64 ✓ PASSES (correct - valid shift for u64)
```

**Why Tests Pass:** All tests use matching types (`u32 << u32`, `i32 << i32`)

**Impact:** CRITICAL - Accepts overflowing shifts OR rejects valid shifts depending on type mismatch

**Fix:**
```rust
fn enforce_bitshift_rhs_lt_bit_size(&mut self, lhs: ValueId, rhs: ValueId) {
    let lhs_type = self.context.dfg.type_of_value(lhs);  // ← Use LHS
    let bit_size = lhs_type.bit_size();
    // ... rest unchanged
}

// Call site (line 119):
bitshift_context.enforce_bitshift_rhs_lt_bit_size(lhs, rhs);
```

---

## SUMMARY

| Bug | Severity | Exploitable | Test Gap |
|-----|----------|-------------|----------|
| Array Set Multi-Block | HIGH | Depends on flattening guarantees | Missing multi-block test |
| Constrain Operand Order | LOW | Likely prevented by frontend | Missing reversed operand test |
| Bit Shift Type Check | **CRITICAL** | **YES - immediate** | Missing mismatched type tests |

**Recommended Actions:**
1. **URGENT:** Fix bit shift bug - wrong type used for overflow check
2. Add test cases with mismatched operand types
3. Clarify array set preconditions or add inter-block analysis
4. Document constrain operand order assumption