erts: Fix out-of-bounds read on distributed priority send of a fragmented message - #11417
Merged
rickard-green merged 1 commit intoAug 6, 2026
Conversation
Contributor
CT Test Results 3 files 136 suites 50m 36s ⏱️ Results for commit 013ecef. ♻️ This comment has been updated with latest results. To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass. See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally. Artifacts// Erlang/OTP Github Action Bot |
Contributor
|
Thanks for the fix! Please rebase this branch on top of |
…nted message
handle_altact_msg() replaces the message reference for a fragmented
distributed message with one allocated by erts_alloc_message(0, NULL) and
attaches the heap fragment separately. The priority path then inserted it
with insert_prepared_prio_msg(), which hard-codes ERTS_MSG_COMBINED_HFRAG
and so re-marks the message as carrying an embedded fragment.
erts_save_message_in_proc() trusts that marker and reads the embedded
fragment's off_heap.first at offset 48 of the 40-byte allocation -- an
8-byte out-of-bounds read (ASan heap-buffer-overflow, SIGABRT; a wild read
that can segfault the node on a normal build). One >32 KiB message sent
with [priority] to an erlang:alias([priority]) from a connected
distribution peer is enough to crash the whole node.
Pass mp->data.attached to insert_prepared_prio_msg_attached() instead, so
the separately attached fragment keeps its real ownership.
signal_SUITE:priority_messages_order only ever sent small priority
messages, staying below the 32 KiB fragmentation threshold. Its
distributed priority message is now 1 MiB, so the case covers this path
while still asserting ordering against the surrounding link, monitor and
exit signals.
Verified on a Clang ASan build (erts 17.0.4, emu_type=asan): a two-node
1 MiB priority-alias reproducer aborts 134 with the heap-buffer-overflow
in erts_save_message_in_proc() without this change and exits 0 clean with
it, from build contexts differing in exactly this one file;
signal_SUITE:priority_messages_order returns {1,0,{0,0}} with no
sanitizer findings.
potatosalad
force-pushed
the
fix/11416-priority-message-fragment
branch
from
August 5, 2026 14:37
d7b6e11 to
013ecef
Compare
Contributor
Author
|
@rickard-green Rebased onto |
Contributor
|
Thanks! |
rickard-green
approved these changes
Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix out-of-bounds read on distributed priority send of a fragmented message
What happens
A distributed
[priority]send larger than 32 KiB to anerlang:alias([priority])triggers an out-of-bounds read while the receiving node saves the message: the emulator reads 8 bytes past a 40-byte allocation. It's deterministic on the first such message, on stock OTP with no third-party code. Under ASan it aborts withheap-buffer-overflow; on a normal build the read lands in unrelated memory and the node can crash.Root cause
When a distributed message arrives fragmented,
handle_altact_msg()(erts/emulator/beam/erl_proc_sig_queue.c) allocates a bare message reference and attaches the fragment separately:erts_alloc_message(0, NULL)returns a 40-byteErtsMessagewith no room for an embeddedErlHeapFragment.The priority path then inserts that message through
insert_prepared_prio_msg(), a thin wrapper that hard-codes the "combined" (embedded-fragment) case:So a message whose fragment is separately attached gets tagged
ERTS_MSG_COMBINED_HFRAG, which claims the fragment is embedded in the message allocation.erts_save_message_in_proc()(erts/emulator/beam/erl_message.c:981) trusts that tag and reads the supposedly-embedded fragment'soff_heap.firstat offset 48 of the 40-byte region — hence the 8-byte over-read.Only sends above the 32 KiB fragmentation threshold reach this branch, which is why smaller priority messages are fine.
The fix
erts/emulator/beam/erl_proc_sig_queue.c— pass the message's actual attachment through instead of reclassifying it as combined:mp->data.attachedis exactly what the branch above just set, so the fragment keeps its real ownership and the save path no longer reads a fragment that was never embedded. The non-fragmented priority paths are untouched and still go throughinsert_prepared_prio_msg().Regression test
erts/emulator/test/signal_SUITE.erl—priority_messages_orderonly ever sent small priority messages before, so it never crossed the fragmentation threshold. Its distributed priority message is now 1 MiB:with the expected-message list updated to match. The case already runs twice — once against a local process, once against a
?CT_PEER()node — so the 1 MiB message crosses a real distribution link, and it still asserts the ordering of that message against the surrounding link/monitor/exit signals. Unpatched, the case aborts; patched, it passes.How it was verified
Everything below ran against a Clang ASan build of this tree (
otp_release=30 erts=17.0.4 emu_type=asan).A/B on the minimal reproducer. Two builds differing in exactly one file (
erts/emulator/beam/erl_proc_sig_queue.c), same two-node script: nodevcreatesalias([priority]), nodeusends 1 MiB with[priority],vacknowledges receipt.vexituexitThe over-read on the unpatched build, showing the read 8 bytes past the 40-byte allocation made in
handle_altact_msg():With the fix applied the same script produces no sanitizer output and both nodes exit 0.
OTP regression under ASan.
signal_SUITE:priority_messages_orderviact:run_test/1:{1,0,{0,0}}(1 ok, 0 failed, 0 skipped), no sanitizer files.Affected versions
Confirmed on OTP 29.0.4 (ERTS 17.0.4) and reproduced on
master(OTP 30 dev, ERTS 17.0.4); also seen on 28.4.1. Priority messages are an OTP 28 feature, so 28.x and 29.x are affected and ≤27 is not.