# #49003 \[SC-Low] Array Underflow Vulnerability in UInt64SetLib leads to contract failure

**Submitted on Jul 10th 2025 at 12:44:28 UTC by @HandsomeEarthworm6 for** [**Audit Comp | Folks Smart Contract Library**](https://immunefi.com/audit-competition/folks-sc-library)

* **Report ID:** #49003
* **Report Type:** Smart Contract
* **Report severity:** Low
* **Target:** <https://github.com/Folks-Finance/algorand-smart-contract-library/blob/main/contracts/library/UInt64SetLib.py>
* **Impacts:**
  * Temporary denial of service (smart contract is made unable to operate for one block, functionality is restored in the next block)

## Description

## Brief/Intro

The UInt64SetLib.py contract contains an integer underflow vulnerability in the remove\_item function that causes temporary contract failure when attempting to remove items from empty arrays.

## Vulnerability Details

The vulnerability exists in the remove\_item\_for\_header function of contracts/library/UInt64SetLib.py:

last\_idx = items.length - 1 # Critical underflow when items.length = 0

When items.length equals 0, the subtraction operation 0 - 1 causes an integer underflow in the underlying TEAL assembly code. Since Algorand uses unsigned 64-bit integers (UInt64), this underflow wraps the value to UInt64::MAX. The contract then attempts to access items\[18446744073709551615], which immediately fails with an array bounds error as shown below. intc\_0 // 0 <- Push 0 (array length) extract\_uint16 <- Extract as 16-bit uint dup <- Duplicate the length value intc\_1 // 1 <- Push 1

* <--- Error <- Subtraction causes underflow and crash

The vulnerability affects multiple code paths:

Direct calls to removeItem() with empty arrays

Cascading failures when arrays become empty through normal operations

Batch operations that process items sequentially

Test Results Confirmation: Six separate test cases failed with identical underflow errors, confirming the vulnerability affects:

1.remove\_item on empty array should not crash

2.remove\_item on empty array with different values

3.single item removal leading to empty array

4.operations on empty arrays

5.consecutive operations on empty arrays

## Impact Details

Contract Failure: Any call to remove\_item with an empty array immediately crashes the contract

\##Recommendation check if the array is empty before trying to calculate last\_idx

## Proof of Concept

## Proof of Concept

add this test to tests/library/UInt64SetLib.test.ts and then run :;

npx jest tests/library/UInt64SetLib.test.ts --testTimeout=30000

/ Add these test cases to your existing describe("UInt64SetLib") block

describe("Security Vulnerability Tests", () => { describe("Critical Issue: Array Underflow on Empty Array", () => { test("remove\_item on empty array should not crash", async () => { // This test will likely FAIL and expose the critical bug const result = await client.removeItem({ args: \[1n, \[]] }); expect(result).toEqual(\[false, \[]]); });

```
  test("remove_item on empty array with different values", async () => {
    expect(await client.removeItem({ args: [0n, []] })).toEqual([false, []]);
    expect(await client.removeItem({ args: [999999n, []] })).toEqual([false, []]);
  });
});

describe("Edge Cases", () => {
  test("single item removal leading to empty array", async () => {
    // Remove the only item to create empty array
    const removeResult = await client.removeItem({ args: [42n, [42n]] });
    expect(removeResult).toEqual([true, []]);
    
    // Try to remove from the now-empty result
    const removeFromEmpty = await client.removeItem({ args: [42n, removeResult[1]] });
    expect(removeFromEmpty).toEqual([false, []]);
  });

  test("operations on empty arrays", async () => {
    // Test has_item on empty array
    expect(await client.hasItem({ args: [1n, []] })).toBeFalsy();
    expect(await client.hasItem({ args: [0n, []] })).toBeFalsy();
    
    // Test add_item on empty array
    expect(await client.addItem({ args: [1n, []] })).toEqual([true, [1n]]);
    expect(await client.addItem({ args: [0n, []] })).toEqual([true, [0n]]);
    
    // Test remove_item on empty array
    expect(await client.removeItem({ args: [1n, []] })).toEqual([false, []]);
    expect(await client.removeItem({ args: [0n, []] })).toEqual([false, []]);
  });

  test("consecutive operations on empty arrays", async () => {
    // Multiple remove operations on empty arrays
    for (let i = 0; i < 3; i++) {
      const result = await client.removeItem({ args: [BigInt(i), []] });
      expect(result).toEqual([false, []]);
    }
  });
});

describe("Resource Tests", () => {
  test("operations on large arrays", async () => {
    // Create a moderately large array
    const largeArray = Array.from({ length: 50 }, (_, i) => BigInt(i));
    
    // These should complete without issues
    const hasResult = await client.hasItem({ args: [25n, largeArray] });
    expect(typeof hasResult).toBe("boolean");
    
    const addResult = await client.addItem({ args: [999n, largeArray] });
    expect(Array.isArray(addResult)).toBe(true);
    
    const removeResult = await client.removeItem({ args: [25n, largeArray] });
    expect(Array.isArray(removeResult)).toBe(true);
  });
});
```

});
