Skip to content

Unable to reassemble a disassembled program that contains unused subroutines #6489

@nullun

Description

@nullun

@dragmz identified an unusual issue where some existing smart contracts were unable to be assmbled after being disassembled.

The two application IDs originally identified as having this problem were: 2747284875 and 2309798439.

After a little investigating I discovered that during disassembly, any unused subroutines/labels doesn't get given a label. Which should be fine since labels are there for us humans, and any branch/call opcode is actually using a signed int16 offset.

The problem however, is when an unused subroutine uses the proto opcode. This results in any use of frame_dig or frame_bury now being globally scoped and these errors being produced.

Example:

Using one of the existing programs.

% curl -s https://mainnet-api.algonode.cloud/v2/applications/2747284875 \
| jq -r '.params."approval-program"' \
| base64 -d \
| goal clerk compile -D - \
| goal clerk compile -

-: 829: itxn_field AssetReceiver arg 0 wanted type address got uint64
-: 834: itxn_field AssetAmount arg 0 wanted type uint64 got []byte
-: 839: itxn_field XferAsset arg 0 wanted type uint64 got []byte
-: 859: itxn_field AssetReceiver arg 0 wanted type address got uint64
-: 864: itxn_field AssetAmount arg 0 wanted type uint64 got []byte
-: 869: itxn_field XferAsset arg 0 wanted type uint64 got []byte
-: 6 errors

MRE

This program will happily compile and deploy. But disassembling it results in the "unusedSubroutine" label being omitted.

#pragma version 12

callsub usedSubroutine
pushint 1
return

usedSubroutine:
  retsub

unusedSubroutine:
  proto 0 0

  pushint 0
  pushint 1
  frame_bury 0
  pop

  retsub

Assemble + Disassemble: Label not given to unusedSubroutine

% goal clerk compile test.teal -o - \
| goal clerk compile -D -

#pragma version 12
callsub label1
pushint 1
return
label1:
retsub
proto 0 0     // <--- no label
pushint 0
pushint 1
frame_bury 0
pop
retsub

Assemble + Dissasmble + Assemble: Errors

% goal clerk compile test.teal -o - \
| goal clerk compile -D - \
| goal clerk compile -

-: 1 error: 10: frame_bury above stack
-: 1 error: 10: frame_bury above stack

If you comment out the usedSubroutine all together, you'll get a different error: proto must be unreachable from previous PC.

% goal clerk compile test.teal -o - \
| goal clerk compile -D - \
| goal clerk compile -

-: 1 error: 4: proto must be unreachable from previous PC
-: 1 error: 4: proto must be unreachable from previous PC

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions