Skip to content

daemon: fix OOB read and parse bug in dlt_daemon_control_get_log_info_v2 (closes #870)#861

Open
SoundMatt wants to merge 2 commits into
COVESA:masterfrom
SoundMatt:fix/get-log-info-v2-oob-read
Open

daemon: fix OOB read and parse bug in dlt_daemon_control_get_log_info_v2 (closes #870)#861
SoundMatt wants to merge 2 commits into
COVESA:masterfrom
SoundMatt:fix/get-log-info-v2-oob-read

Conversation

@SoundMatt

@SoundMatt SoundMatt commented May 5, 2026

Copy link
Copy Markdown

Problem

dlt_daemon_control_get_log_info_v2 had two compounding bugs, each
addressed by one commit in this PR.

1. Out-of-bounds read parsing apidlen / ctidlen

The fixed-size precheck only validated the 11-byte minimum
GET_LOG_INFO V2 request before the function read two attacker-
controlled uint8_t length fields (apidlen, ctidlen) and used
them to advance an offset into msg->databuffer. A short message
with non-zero length fields caused the variable-length reads to walk
past the end of msg->databuffer (up to ~514 bytes). Reachable from
any client able to talk to the daemon's control socket. Bounded OOB
read; no OOB write.

2. Function never actually parses apid / ctid (#870)

req is calloc'd, so its char *apid / char *ctid pointer fields
start NULL. The dlt_set_id_v2(req->apid, ...) calls intended to
populate them are no-ops (the helper early-returns when its
destination is NULL). req->apid / req->ctid therefore stayed
NULL and were passed to dlt_daemon_application_find_v2 /
dlt_daemon_context_find_v2 with non-zero corresponding lengths —
turning every non-empty apid / ctid lookup into a zero-length one.
Full analysis in #870.

Fix

Two commits:

  1. Bounds checks after each variable-length field is parsed,
    freeing the calloc'd req and returning when the message cannot
    satisfy the next read.
  2. Replace the no-op dlt_set_id_v2(req->apid, ...) calls with
    conditional pointer assignments
    into msg->databuffer,
    mirroring the surgical approach taken for set_log_level_v2 in
    daemon: parse apid/ctid in dlt_daemon_control_set_log_level_v2 #864 and unregister_context_v2 in daemon: fix multi-bug response builder in dlt_daemon_control_message_unregister_context_v2 (closes #867) #868.

req shares storage with the message buffer for the duration of
this function call, so the pointer-into-databuffer assignments are
lifetime-safe.

Notes

Closes #870.
Related: #866 (META).

@minminlittleshrimp

Copy link
Copy Markdown
Collaborator

Please signoff in your commit

The fixed-size precheck at the entry of this function validates only the
11-byte minimum GET_LOG_INFO V2 request (apidlen = ctidlen = 0). The
function then reads two attacker-controlled uint8_t length fields
(apidlen, ctidlen) from msg->databuffer and uses them to advance an
offset, before passing pointers into the buffer to dlt_set_id_v2()
(which reads up to apidlen / ctidlen bytes via dlt_strnlen_s) and
finishing with a 4-byte memcpy of the trailing com field. A short
message with non-zero length fields causes the variable-length reads to
walk past the end of msg->databuffer.

Add bounds checks after each length is parsed, freeing the calloc'd
request and returning when the message cannot satisfy the next read.

Note: the existing early-return paths inside this function already leak
the calloc'd req pointer; left untouched here to keep this commit
focused on the security fix.

Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
Companion fix to the OOB-read fix in the previous commit. The function
never assigned req->apid or req->ctid: req is calloc'd, so the char *
pointer fields start NULL, and the dlt_set_id_v2(req->apid, ...) call to
populate them was a no-op (dlt_set_id_v2 early-returns when its
destination is NULL). req->apid / req->ctid stayed NULL and were then
passed to dlt_daemon_application_find_v2 and dlt_daemon_context_find_v2
despite req->apidlen / req->ctidlen being non-zero — every non-empty
lookup was silently turned into a zero-length one.

Copy the apid/ctid into local fixed-size buffers through the shared
dlt_set_id_v2() helper and point req->apid / req->ctid at those buffers,
keeping id initialisation inside the helper API rather than aliasing the
receive buffer. The buffers live for the whole function scope and req is
freed (struct only) before return, so the pointers stay valid for every
use. The bounds checks added in the previous commit keep the helper's
reads within msg->databuffer.

Closes COVESA#870.
Related: COVESA#866.

Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.com>
@SoundMatt SoundMatt force-pushed the fix/get-log-info-v2-oob-read branch from 808c02b to fcde3e6 Compare June 18, 2026 15:00
@SoundMatt

Copy link
Copy Markdown
Author

Done — added Signed-off-by to both commits (force-pushed).

While here I also reworked the apid/ctid parsing to use the shared dlt_set_id_v2() helper into local char[DLT_V2_ID_SIZE] buffers instead of aliasing the receive buffer, matching the approach taken in #864/#868 (the struct's apid/ctid are char *, so the original dlt_set_id_v2(req->apid, …) was a no-op against a NULL destination). The OOB bounds checks are unchanged; the buffers live for the whole function scope and req is freed (struct only) before return, so the pointers stay valid for every use.

SoundMatt added a commit to SoundMatt/dlt-daemon that referenced this pull request Jun 18, 2026
Rewrite the handler to parse the GET request field-by-field instead of
casting msg->databuffer directly to DltServiceSetLogLevelV2 *. The struct
has char *apid / char *ctid pointer fields, so a direct cast filled them
with attacker-controlled values from network input that were then
dereferenced — and the old code also hit the same NULL-deref pattern as
COVESA#863. Add bounds checks for the two attacker-controlled length fields so
the variable-length reads stay within msg->databuffer.

Copy the apid/ctid into local fixed-size buffers through the shared
dlt_set_id_v2() helper and point apid/ctid at those buffers, keeping id
initialisation inside the helper API rather than aliasing the receive
buffer (consistent with COVESA#864/COVESA#868/COVESA#861).

Related: COVESA#866.
Signed-off-by: Matt Jones <47545907+SoundMatt@users.noreply.github.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.

dlt_daemon_control_get_log_info_v2 never assigns req->apid / req->ctid; downstream lookups get NULL pointers

2 participants