Skip to content

fix(logstorage): replace unbounded strcat with snprintf in storage_dir_info#873

Open
SoundMatt wants to merge 3 commits into
COVESA:masterfrom
SoundMatt:fix/logstorage-strcat-overflow
Open

fix(logstorage): replace unbounded strcat with snprintf in storage_dir_info#873
SoundMatt wants to merge 3 commits into
COVESA:masterfrom
SoundMatt:fix/logstorage-strcat-overflow

Conversation

@SoundMatt
Copy link
Copy Markdown

Summary

dlt_logstorage_storage_dir_info() in dlt_offline_logstorage_behavior.c builds a file path from three pieces using three chained strcat() calls into a fixed-size stack buffer:

char tmpfile[DLT_OFFLINE_LOGSTORAGE_MAX_LOG_FILE_LEN + 1] = { '\0' };  // 121 bytes
if (dir != NULL) {
    strcat(tmpfile, dir);          // up to ~99 chars (from config)
    strcat(tmpfile, "/");          // + 1
}
strcat(tmpfile, files[i]->d_name); // up to NAME_MAX = 255 chars

dir is derived from a config-bounded field (max DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN = 100 chars), so the directory component is bounded. However files[i]->d_name comes from scandir() and can be up to NAME_MAX (255 bytes) on Linux.

dir(~99) + "/" + d_name(~255) = ~355 bytes can easily overflow the 121-byte tmpfile buffer, causing a stack buffer overflow. The overflow is triggerable whenever the storage directory contains a file whose name exceeds the buffer capacity, which can happen naturally or via a crafted mount-point with long filenames.

Changes

Replace the three strcat() calls with a single snprintf(..., sizeof(tmpfile), ...), which hard-limits the write to the buffer size. Add a strncpy for the no-directory branch for consistency.

Testing

  • Paths shorter than DLT_OFFLINE_LOGSTORAGE_MAX_LOG_FILE_LEN are copied unchanged.
  • Paths longer than the buffer are safely truncated; no stack overflow.

SoundMatt added 3 commits May 5, 2026 06:55
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.
Companion fix to the OOB-read fix in this same commit series. 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.

Replace the no-op dlt_set_id_v2 calls with conditional pointer
assignments into msg->databuffer, mirroring the surgical approach
taken for set_log_level_v2 in PR COVESA#864 and unregister_context_v2 in
PR COVESA#868. The bounds checks added in the previous commit ensure the
pointer-into-databuffer assignments are safe.

Closes COVESA#870.
Related: COVESA#866.
…r_info

Three chained strcat() calls build a path as dir + "/" + d_name into a
fixed-size tmpfile[] buffer (DLT_OFFLINE_LOGSTORAGE_MAX_LOG_FILE_LEN+1,
i.e. 121 bytes).  dir comes from dirname() applied to a config field
that is bounded by DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN (100 chars),
but d_name is returned by scandir() and can be up to NAME_MAX (255 chars)
on Linux.  dir(~100) + "/" + d_name(~255) can easily exceed 121 bytes,
overflowing tmpfile on the stack.

Replace with a single snprintf(..., sizeof(tmpfile), "%s/%s", dir,
files[i]->d_name) which hard-limits the write to the buffer size, and
use strncpy for the no-dir branch to be consistent.

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.

1 participant