# Bit Shift Safety

> Trigger Pattern Always (Sui Move) -- Move VM aborts on shift = bit width - Inject Into Breadth agents, depth-edge-case

- Skill: `plamentsv/bit-shift-safety` (Agent Skill)
- Install (CLI): `npx skillmds@latest add plamentsv/bit-shift-safety`
- Raw SKILL.md: https://api.skillmd.com/api/skills/plamentsv/bit-shift-safety/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: plamentsv (https://skillmd.com/u/plamentsv)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/plamentsv/bit-shift-safety

---


# BIT_SHIFT_SAFETY Skill

> **Trigger Pattern**: Always (Sui Move) -- Move VM aborts on shift >= bit width
> **Inject Into**: Breadth agents, depth-edge-case

For every bit shift operation in the protocol:

**BACKGROUND**: The Move VM (shared by Sui and Aptos) aborts the entire transaction if a bit shift operand equals or exceeds the bit width of the type. For `u64`, shifting by 64 or more aborts. For `u128`, shifting by 128 or more aborts. This is NOT a revert with an error code -- it is a VM abort that cannot be caught. On Sui, a PTB (Programmable Transaction Block) abort means ALL commands in the PTB fail, and shared objects that were locked for the transaction are released without state changes.

## 1. Shift Operation Inventory

Enumerate ALL bit shift operations (`<<` and `>>`) across all modules:

| Module | Function | Line | Operation | Type | Bit Width | Shift Amount Source | Validated? |
|--------|----------|------|-----------|------|-----------|--------------------|-----------:|
| {mod} | {func} | {L} | `<<` / `>>` | u8/u64/u128/u256 | 8/64/128/256 | {literal/parameter/computed} | YES/NO |

**Grep pattern**: Search all `.move` files for `<<` and `>>` operators.

**Type-to-width mapping**:
| Type | Max Safe Shift |
|------|---------------|
| `u8` | 7 |
| `u64` | 63 |
| `u128` | 127 |
| `u256` | 255 |

## 2. Shift Amount Source Classification

For each shift operation, classify the shift amount source:

### 2a. Literal Shifts (Low Risk)
```move
let x = value << 32;  // Literal: always safe if < bit width
```
**Check**: Is the literal < bit width of the type? If yes -> SAFE. If no -> compilation may succeed but runtime aborts.

### 2b. Parameter-Derived Shifts (Medium Risk)
```move
public fun shift_by(value: u64, amount: u8): u64 {
    value << (amount as u8)  // Parameter: caller controls shift amount
}
```
**Check**: Is the `amount` parameter validated before the shift? Common patterns:
- `assert!(amount < 64, E_SHIFT_OVERFLOW)` -- explicit validation
- `amount % 64` -- wrapping (changes semantics but prevents abort)
- No validation -- FINDING

### 2c. Computed Shifts (High Risk)
```move
let shift = calculate_precision(decimals);  // Computed: depends on runtime state
let result = base << shift;
```
**Check**: Can the computation ever produce a value >= bit width? Trace the computation to its inputs. If any input is user-controlled or state-derived -> HIGH RISK.

## 3. Abort Impact Analysis

For each unvalidated shift operation, assess the abort impact:

| Function | Called By | Shared Objects Locked? | PTB Context | Abort Impact |
|----------|----------|----------------------|-------------|-------------|
| {func} | {callers} | YES/NO | {typical PTB} | {impact} |

**Sui-specific abort consequences**:
- **PTB abort**: All commands in the Programmable Transaction Block fail atomically. If the shift is in command 3 of a 5-command PTB, commands 1-2 are also rolled back.
- **Shared object locking**: If the aborting function locks shared objects (accessed via `&mut` reference), those objects are temporarily unavailable during consensus. Repeated aborts can cause transient unavailability. This is NOT permanent locking -- Sui releases locks after the transaction fails.
- **Gas consumption**: The sender pays gas for the aborted transaction up to the abort point.
- **Griefing vector**: If an attacker can trigger the abort via a public function with a user-controlled shift amount, they can grief other users by causing their PTBs to abort. This is especially impactful when the aborting function is called as part of a common user flow (deposit, swap, claim).

### 3a. Griefing Scenario Modeling

For each unvalidated shift in a public/entry function:

```
Scenario: Shift Abort Griefing
1. Attacker calls {FUNCTION} with shift amount = {BIT_WIDTH}
2. Move VM aborts the transaction
3. Impact on other users: {IMPACT}
   - If function modifies shared state: other PTBs depending on that state must retry
   - If function is part of a multi-step user flow: user loses gas + must restart
4. Attacker cost: gas for one failed transaction
5. Severity: {based on impact}
```

## 4. Common Vulnerable Patterns

### 4a. Decimal Conversion Shifts
```move
// VULNERABLE: decimals comes from token metadata, could be >= 64
let scale = 1u64 << decimals;
```
**Fix pattern**: `assert!(decimals < 64, E_INVALID_DECIMALS)` or use `math::pow(10, decimals)` instead.

### 4b. Bit Packing / Unpacking
```move
// VULNERABLE if position is not bounds-checked
let field = (packed >> position) & mask;
```
**Check**: Is `position` derived from user input or configuration? If yes and no validation -> FINDING.

### 4c. Fixed-Point Arithmetic
```move
// Common in DeFi: fixed-point multiplication with shift
let result = (a * b) >> PRECISION_BITS;
```
**Check**: Is `PRECISION_BITS` a constant? If yes and < bit width -> SAFE. If computed -> trace source.

### 4d. Loop-Based Shifts
```move
let mut i = 0;
while (i < n) {
    value = value << 1;  // Safe per iteration, but after 64 iterations value = 0 (not abort)
    i = i + 1;
};
```
**Note**: Shifting by 1 repeatedly does NOT abort (shift amount is always 1). But the value overflows silently to 0 after bit-width iterations. Check if this silent overflow causes logic errors.

## 5. Validation Pattern Verification

For each shift operation that IS validated, verify the validation is correct:

| Function | Validation | Correct? | Edge Case |
|----------|-----------|----------|-----------|
| {func} | `assert!(n < 64)` | YES | n=63 is max safe |
| {func} | `assert!(n <= 64)` | **NO** | n=64 aborts |
| {func} | `n % 64` | SAFE but semantic change | shift by 0 when n=64 |

**Common validation errors**:
- Off-by-one: `<= bit_width` instead of `< bit_width`
- Wrong bit width: validating against 64 for a `u128` shift (allows 64-127 to abort)
- Missing cast: `amount` is `u64` but shift operand must be `u8` -- does the cast truncate?

## 6. Cross-Function Shift Propagation

Trace shift amounts across function boundaries:

```
entry_function(user_input: u64)
  -> helper_a(derived_value)  // derived_value = user_input * 2
    -> helper_b(shift_amount) // shift_amount = derived_value + offset
      -> actual_shift: value << shift_amount  // Is shift_amount < bit_width?
```

**For each chain**: Can ANY combination of valid inputs to the entry function produce a shift amount >= bit width at the actual shift site? Document the full trace.

## Finding Template

```markdown
**ID**: [BS-N]
**Severity**: [HIGH if public function, MEDIUM if restricted caller, LOW if constant shift]
**Step Execution**: check1,2,3,4,5,6 | X(reasons) | ?(uncertain)
**Rules Applied**: [R4:Y, R10:Y, ...]
**Depth Evidence**: [BOUNDARY:shift=bit_width], [TRACE:user_input->shift_amount->abort]
**Location**: module::function:LineN
**Title**: Unvalidated bit shift in [function] causes VM abort on [condition]
**Description**: [Specific shift operation, source of shift amount, why it can reach bit width]
**Impact**: [Transaction abort, shared object locking, griefing potential, gas waste]
```

---

## Step Execution Checklist (MANDATORY)

> **CRITICAL**: You MUST report completion status for ALL sections. Findings with incomplete sections will be flagged for depth review.

| Section | Required | Completed? | Notes |
|---------|----------|------------|-------|
| 1. Shift Operation Inventory | YES | Y/X/? | Grep all `.move` files |
| 2. Shift Amount Source Classification | YES | Y/X/? | For each shift |
| 3. Abort Impact Analysis | IF unvalidated shifts found | Y/X(N/A)/? | |
| 3a. Griefing Scenario Modeling | IF unvalidated in public fn | Y/X(N/A)/? | |
| 4. Common Vulnerable Patterns | YES | Y/X/? | Check all 4 sub-patterns |
| 5. Validation Pattern Verification | IF validated shifts exist | Y/X(N/A)/? | Off-by-one check |
| 6. Cross-Function Shift Propagation | IF shift amount crosses functions | Y/X(N/A)/? | |

### Cross-Reference Markers

**After Section 1** (Shift Inventory):
- IF zero shift operations found -> mark skill as N/A, skip remaining sections
- IF shifts found in math/fixed-point libraries -> prioritize Section 4c

**After Section 3** (Abort Impact):
- IF abort affects shared objects -> cross-reference with ABILITY_ANALYSIS Section 2b (shared object analysis)
- IF abort is in a hot potato consumption path -> cross-reference with ABILITY_ANALYSIS Section 5 (hot potato enforcement) -- abort before consumption = permanent PTB failure

**After Section 6** (Cross-Function Propagation):
- IF any chain reaches bit width with valid inputs -> FINDING (minimum Medium)
- Tag: `[BOUNDARY:shift={bit_width}]`, `[TRACE:input_path->abort_site]`

