HexBrain

Tor · Report #3709605

Conflux-queued zero-length RELAY_END triggers heap out-of-bounds read

Validstatus
mediumseverity
33upvotes
10 Sept 2026disclosed

How it was found

Summary

A zero-length RELAY_END cell can trigger a heap out-of-bounds read when the cell is copied through Conflux out-of-order delivery and then processed on a non-open AP stream.

Tor explicitly allows zero-length RELAY_END cells by treating them as END_STREAM_REASON_MISC, but connection_ap_process_end_not_open() reads msg->body[0] before checking msg->length.

This is normally masked by relay-cell backing storage. Conflux out-of-order queuing copies messages into exact-size heap allocations using relay_msg_copy(). For msg->length == 0, the copied msg->body points immediately after the allocation, and the unguarded get_uint8(msg->body) becomes a true heap OOB read.

Affected Code

  • src/core/or/relay.c:846
int reason = get_uint8(msg->body);

The length check occurs later:

if (msg->length == 0) {
  reason = END_STREAM_REASON_MISC;
}
  • src/core/or/conflux.c:923
c_msg->msg = relay_msg_copy(msg);
  • src/core/or/relay_msg.c:71
void *alloc = tor_malloc_zero(sizeof(relay_msg_t) + msg->length);

Proof of Concept

I added a local ASan regression PoC as a patch to this checkout. The relevant patched files are:

  • src/test/test_relaycell.c
  • Test name: relaycell/end_cell_conflux_copy_oob

{F5843197} contains the test_relaycell_end_cell_conflux_copy_oob() test and its copy_msg_through_conflux_ooo() helper.

The PoC:

  1. Builds a zero-length RELAY_END message for a non-open AP stream.
  2. Sends it through Tor's actual conflux_process_relay_msg() out-of-order queue.
  3. Dequeues the exact heap-backed relay_msg_t produced by relay_msg_copy().
  4. Simulates Conflux ordered delivery into connection_edge_process_relay_cell().
  5. ASan reports a heap-buffer-overflow at connection_ap_process_end_not_open().

Reproduction

From a clean Tor checkout with this PoC patch applied:

./autogen.sh
CC=clang \
CFLAGS='-O0 -g -fsanitize=address -fno-omit-frame-pointer' \
LDFLAGS='-fsanitize=address' \
./configure --disable-asciidoc --disable-manpage --disable-html-manual
make micro-revision.i
make -j"$(nproc)" src/test/test
ASAN_OPTIONS='abort_on_error=1:halt_on_error=1:detect_leaks=0:symbolize=1' \
  ./src/test/test relaycell/end_cell_conflux_copy_oob

In my environment I also needed to point configure at Homebrew OpenSSL:

PKG_CONFIG_PATH=/home/linuxbrew/.linuxbrew/lib/pkgconfig \
CPPFLAGS='-I/home/linuxbrew/.linuxbrew/include' \
LDFLAGS='-fsanitize=address -L/home/linuxbrew/.linuxbrew/lib -Wl,-rpath,/home/linuxbrew/.linuxbrew/lib' \
./configure --with-openssl-dir=/home/linuxbrew/.linuxbrew \
  --disable-asciidoc --disable-manpage --disable-html-manual

ASan Evidence

The PoC crashes with:

ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1
    #0 get_uint8 src/lib/arch/bytes.h:25
    #1 connection_ap_process_end_not_open src/core/or/relay.c:846
    #2 connection_edge_process_relay_cell_not_open src/core/or/relay.c:1402
    #3 connection_edge_process_ordered_relay_cell src/core/or/relay.c:2168
    #4 connection_edge_process_relay_cell src/core/or/relay.c:2099
    #5 test_relaycell_end_cell_conflux_copy_oob src/test/test_relaycell.c:1136

allocated by thread T0 here:
    #3 relay_msg_copy src/core/or/relay_msg.c:71
    #4 conflux_process_relay_msg src/core/or/conflux.c:923
    #5 copy_msg_through_conflux_ooo src/test/test_relaycell.c:214

0 bytes after 16-byte region

Suggested Fix

Read the reason byte only when one is present:

int reason = msg->length > 0 ? get_uint8(msg->body)
                             : END_STREAM_REASON_MISC;

Impact

An attacker who can cause a zero-length RELAY_END to be queued out-of-order on a Conflux circuit can trigger a remote heap OOB read in Tor's relay-cell handling path. In ASan or hardened builds this is a reliable process crash. In non-ASan builds, this remains memory-unsafe behavior and reads from heap-adjacent memory.

The strongest reachable case is a non-open AP stream, such as a stream still in AP_CONN_STATE_CONNECT_WAIT, receiving an empty RELAY_END from the exit side after Conflux has copied the cell through the out-of-order queue.

Vector: CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H

Justification:

  1. AV:N: the malformed relay cell can be delivered over the Tor network.
  2. AC:H: exploitation requires a linked Conflux pair, an out-of-order sequence gap, and a non-open AP stream state such as AP_CONN_STATE_CONNECT_WAIT.
  3. PR:N: the attacker does not need privileges on the target client; they act as a relay/exit capable of sending relay cells on the circuit.
  4. UI:N: after the circuit and stream exist, no additional user interaction is required to send the malformed RELAY_END.
  5. S:U: the impact is within the vulnerable Tor process.
  6. C:N: no direct information disclosure primitive is demonstrated.
  7. I:N: the OOB byte is overwritten with END_STREAM_REASON_MISC for zero-length messages before meaningful reason handling.
  8. A:H: ASan/hardened builds reliably abort the Tor process on the heap OOB read.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original