Skip to content

cmd/recordexec: Also save proposed blocks to filesystem - #1

Draft
hi-im-milan wants to merge 1 commit into
kjcamann:ken/event-recovery-new-event-sourcefrom
hi-im-milan:rfc-recordexec
Draft

hi-im-milan wants to merge 1 commit into
kjcamann:ken/event-recovery-new-event-sourcefrom
hi-im-milan:rfc-recordexec

Conversation

@hi-im-milan

Copy link
Copy Markdown

What?

We can dump all the proposed blocks to the filesystem in proposals subdir and once they get finalised - simply move them to the correct archive directory. Benefit of this approach is that applications ingesting monad events can rely completely on the filesystem operations (including inotify) to detect new blocks, without waiting for block to be verified.

This is a RFC commit, there are things that need to be cleaned up + it hasn't been tested.
I think this approach shouldn't add much overhead, unless we are expecting to have lot of blocks which are proposed but not verified. rename(2) should be pretty much an instant operation as long as src and dst are on the same filesystem (which should be the case). This feature can also be optionally enabled.

We can dump all the proposed blocks to the filesystem in `proposals` subdir
and once they get finalised - simply move them to the correct archive directory.
Benefit of this approach is that applications ingesting monad events can rely
completely on the filesystem operations (including inotify) to detect new blocks,
without waiting for block to be verified.

This is a RFC commit, there are things that need to be cleaned up + it
hasn't been tested.
I think this approach shouldn't add much overhead, unless we are expecting to
have lot of blocks which are proposed but not verified. rename(2) should be
pretty much an instant operation as long as src and dst are on the same
filesystem (which should be the case). This feature can also be optionally enabled.

Signed-off-by: Milan Pandurov <milan.pandurov@tenderly.co>
kjcamann pushed a commit that referenced this pull request Sep 5, 2026
dump_result loaded a full uint256_t out of result.output_data whenever
output_size was non-zero. Context::copy_result_data allocates that buffer
with std::malloc(*size) for exactly the size the executed bytecode passes
to RETURN, so any RETURN of 1..31 bytes over-read the allocation by up to
31 bytes and hex-printed adjacent heap into the tool's JSON. The same
load silently truncated results longer than 32 bytes to their first word.

Hex-encode exactly output_size bytes instead, which fixes both.

Bytecode returning a single byte 0x2a, run three times before the fix:

    $ printf '602a60005360016000f3' > ret1.hex
    $ mce -r ret1.hex
    {"result": "2a00099925560000000000000000000030306633000000006102000000000000"}
    {"result": "2a46eb64be600000000000000000000030306633000000006102000000000000"}
    {"result": "2a731c3e85600000000000000000000030306633000000006102000000000000"}

Only the leading 2a is the result; the other 31 bytes are adjacent heap
and differ per run. The recurring 30306633 is ASCII "00f3", the tail of
the input file's own hex text. After the fix that input prints
{"result": "2a"} on every run.

The truncation is the same defect from the other side - 64 returned
bytes rendered as one digit before the fix:

    $ printf '60406000f3' > ret64.hex
    $ mce -r ret64.hex
    {"result": "0"}

and now prints all 128 hex digits.

Output format changes for results that are not exactly one word wide: a
32-byte RETURN of 42 renders as 64 zero-padded hex digits rather than
"2a". Nothing consumes this output - the only references to mce outside
its own sources are cmd/vm/mce/CMakeLists.txt and cmd/CMakeLists.txt.

category/vm/compiler/types.hpp is dropped alongside the two integer
headers; it was included only for its uint256_t alias, used solely on
the removed line.

Confirmed under gcc-asan. Before, on the one-byte return:

    ERROR: AddressSanitizer: heap-buffer-overflow
    READ of size 32 at 0x72602abe01f0
      category-labs#3 dump_result  cmd/vm/mce/mce.cpp:154
    0x72602abe01f1 is located 0 bytes after 1-byte region
    allocated by:
      #1 Context::copy_result_data  category/vm/runtime/context.cpp:208

After: no overflow on returns of 0, 1, 32 or 64 bytes.

The pre-existing heap-use-after-free at mce_main scope exit - Binary bin
is declared before asmjit::JitRuntime rt, so rt is destroyed first and
bin's shared_ptr<Nativecode> releases into the freed JitAllocator - is
left untouched for a separate change.

Raised as EXEC-063 in the mythos-review audit batch (EXE-150).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant