Updates
160,000 USDC
View results
Submission Details
Severity: high
Valid

uint2str will return an unaligned string, which can cause access to dirty memory data

Summary

uint2str will return an unaligned string, which can cause access to dirty memory data.

Vulnerability Details

The uint2str function can convert uint into a string. The type size of the string is determined by the number of digits in the uint. If the value is not 0, then the string is written from the end of the allocated memory to the high bits, which may result in an unaligned string object being returned. For example x : uint256 = 1 uint2str(x) returns 0x000000000000000000000000000000000000000000000000000000000000000131, the data after 0x31 belongs to other objects or garbage data.

When the code exits a block, the variable memory allocated by the block will be reclaimed, but the memory will not be cleared. When variables need to be allocated again, allocation will be done from the reclaimed memory first.So we can control variable allocation to generate specific garbage data.

Based on the above information, we can use uint2str, slice and memory occupancy to generate a data access bug.

POC Code:

@external
def test():
    x: uint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935  # 2**256 - 1
    y: uint256 = 22704331223003175573249212746801550559464702875615796870481879217237868556850   # 0x3232323232323232323232323232323232323232323232323232323232323232
    z: uint96 = 1
    if True:
        placeholder : uint256[16] = [y, y, y, y, y, y, y, y, y, y, y, y, y, y, y, y]
    s :String[32] = slice(uint2str(z), 1, x)	# uint2str(z) == "1"
    assert slice(s, 1, 2) == "22"

The block corresponding to the first if statement will allocate a bunch of memory and fill it with 0x323232323232.....
When the if exits, the memory is reclaimed,uint2str will allocate a new variable, using the memory reclaimed above.
uint2str(uint96) will generate a String[29] object,whose length is less than 32. slice will use the simple method of copy_bytes to copy the array,So that dirty data 0x3232.... is copied to the new String generated by slice(),

Here we use the bug of slice(). You can set the length of Bytes or String to 2**256-1, and then use slice(s,1,2) to access the garbage data to get the string "22".

Here is the IRs

    [seq,
     [seq,
      [
    assert, [iszero, callvalue]],
    [goto, external___init____address_uint256_uint256__common],
        # Line 9
    [seq,
     [label,
      external___init____address_uint256_uint256__common,
      var_list,
      [seq,
       [mstore,
        64,
       / * uint160 bounds check * /
       [
    with,
    val,
    [dload, 0],
    [seq, [assert, [iszero, [shr, 160, val]]], val]]],
    [seq, [exit_to, external___init____address_uint256_uint256__cleanup], pass]]],
    [label, external___init____address_uint256_uint256__cleanup, var_list, pass]]],
    [deploy,
    96,
    [seq,
    [with,
    _calldata_method_id,
    [shr, 224, [calldataload, 0]],
    [seq,
    [label, selector_bucket_0, var_list, seq],
    [seq,
    [if,
    [iszero, [xor, _calldata_method_id, 4171824493 < 0xf8a8fd6d: test() >]],
    [seq,
     [
    assert, [iszero, callvalue]],
    [seq,
     [goto, external_test____common],
     # Line 14
     [seq,
      [label,
       external_test____common,
       var_list,
       [seq,
       # Line 15
       / * x: uint256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935 * /
                        [mstore,
                         64,
                         115792089237316195423570985008687907853269984665640564039457584007913129639935 < 115792089237316195423570985008687907853269984665640564039457584007913129639935 >],
    # Line 16
    / *y: uint256 = 22704331223003175573249212746801550559464702875615796870481879217237868556850 * /
                    [mstore,
                     96,
                     22704331223003175573249212746801550559464702875615796870481879217237868556850 < 22704331223003175573249212746801550559464702875615796870481879217237868556850 >],
    # Line 17
    / *z: uint96 = 1 * / [mstore, 128, 1 < 1 >],
        # Line 18
                   [seq,
                   # Line 19
                   / * placeholder: uint256[16] = [y, y, y, y, y, y, y, y, y, y, y, y, y, y, y, y] * /
                                                  [seq,
                                                   [mstore, 160, [mload, 96 < y >]],
                                                   [mstore, 192, [mload, 96 < y >]],
                                                   [mstore, 224, [mload, 96 < y >]],
                                                   [mstore, 256, [mload, 96 < y >]],
                                                   [mstore, 288, [mload, 96 < y >]],
                                                   [mstore, 320, [mload, 96 < y >]],
                                                   [mstore, 352, [mload, 96 < y >]],
                                                   [mstore, 384, [mload, 96 < y >]],
                                                   [mstore, 416, [mload, 96 < y >]],
                                                   [mstore, 448, [mload, 96 < y >]],
                                                   [mstore, 480, [mload, 96 < y >]],
                                                   [mstore, 512, [mload, 96 < y >]],
                                                   [mstore, 544, [mload, 96 < y >]],
                                                   [mstore, 576, [mload, 96 < y >]],
                                                   [mstore, 608, [mload, 96 < y >]],
                                                   [mstore, 640, [mload, 96 < y >]]],
    pass],
    # Line 20
    / *s: String[32] = slice(uint2str(z), 1, x) * /
                       [
    with,
    src,
    / * slice(uint2str(z), 1, x) * /
    [with,
    src,
    / * uint2str(z) * /
    [with,
    val,
    [mload, 128 < z >],
    [if,
    [iszero, val],
    [seq, [mstore, 225, 48], [mstore, 224, 1], 224],
    [seq,
    [repeat,
    uint2str_i1,
    0,
    30,
    30,
    [if,
    [iszero, val],
    [seq,
    [mstore, [sub, 253, uint2str_i1], uint2str_i1],
    [set, val, [sub, 253, uint2str_i1]],
    break],
    [seq,
    [mstore, [sub, 253, uint2str_i1], [add, 48, [mod, val, 10]]],
    [set, val, [div, val, 10]]]]],
    # Line 20
    val]]],
    [
    with,
    length,
    [mload, 64 < x >],
    [seq,
    [assert, [le, [add, 1 < 1 >, length], [mload, src]]],
    [with, src, [add, [add, src, 32], 1 < 1 >], [mstore, 320, [mload, src]]],
    [mstore, 288, length],
    288]]],
    [seq, [mstore, 160, [mload, src]], [mstore, 192, [mload, [add, src, 32]]]]],
    # Line 21
    / * assert (slice(s, 1, 2) == "22") * /
    [assert,
    / * slice(s, 1, 2) == "22" ([with,
    buf,
    ...==[with,
    buf,
    ...) * /
    [iszero,
    [xor,
    [with,
    buf,
    / * slice(s, 1, 2) * /
    [seq,
    [assert, [le, 3, [mload, 160 < s >]]],
    [mstore, 384, [mload, 193]],
    [mstore, 352, 2 < 2 >],
    352],
    / * keccak256 * /[sha3, [add, buf, 32], [mload, buf]]],
    [with,
    buf,
    / * "22" * /
    [seq,
    [mstore, 416, 2],
    [mstore,

448,
22703984782402238635145372884546502928383168074851703611862639686610164121600],
416],
/ *keccak256 * / [sha3, [add, buf, 32], [mload, buf]]]]]],
# Line 14
[exit_to, external_test____cleanup],
pass]],
[label, external_test____cleanup, var_list, stop]]]]],
[goto, fallback]]]],
[goto, fallback],
[label, fallback, var_list, / * Default
function * / [revert, 0, 0]]],
0]]

We can see that the result generated by uint2str is at memory offset 224 ([seq, [mstore, 225, 48], [mstore, 224, 1], 224]), located inside the previously allocated placeholder.

Impact

Medium Risk

Recommendations

Updates

Lead Judging Commences

patrickalphac Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other
KuroHashDit Submitter
over 1 year ago
patrickalphac Auditor
over 1 year ago
patrickalphac Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

integer slice overflow

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.