Skip to content
Back to skills

Bit Shift Safety

ASecurity

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

  • 281 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 6, 2026
blockchain

Security analysis

A100/100

Scanned September 6, 2026

npx -y skills add PlamenTSV/plamen --skill bit-shift-safety --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Bit Shift Safety?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Bit Shift Safety
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/plamentsv-bit-shift-safety-plamen/badge)](https://www.skillsdirectory.com/skills/plamentsv-bit-shift-safety-plamen)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: "bit-shift-safety"
description: "Trigger Pattern Always (Sui Move) -- Move VM aborts on shift = bit width - Inject Into Breadth agents, depth-edge-case"
---

# 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]`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.