fix: cap dlt_buffer_get copy size to max_size to prevent overflow (#829)#834
fix: cap dlt_buffer_get copy size to max_size to prevent overflow (#829)#834aki1770-del wants to merge 1 commit intoCOVESA:masterfrom
Conversation
…VESA#829) When head.size > max_size, dlt_buffer_get() logged a warning but still called dlt_buffer_read_block() with head.size, writing past the end of the caller's buffer (CWE-119). Fix: - Introduce copy_size = min(head.size, max_size) and pass it to dlt_buffer_read_block() so at most max_size bytes are written to data. - When clipping occurs and delete=1, advance the ring buffer read pointer by the full head.size (not copy_size) to keep the shared memory ring buffer in a consistent state for subsequent reads. - Return copy_size instead of head.size to correctly report the number of bytes written to the caller's buffer. The warning message is updated to include both head.size and max_size and to note that the copy was clipped rather than leaving the caller to infer that from the size mismatch.
There was a problem hiding this comment.
Pull request overview
Fixes a buffer overflow in dlt_buffer_get() by capping the number of bytes copied into the caller-provided buffer when the stored block is larger than max_size, while keeping ring-buffer read pointer advancement consistent when deleting entries.
Changes:
- Introduces
copy_size = min(head.size, max_size)and uses it for the data copy to prevent overflow. - When clipping and
delete=1, advances the ring buffer read pointer by the fullhead.size(not justcopy_size). - Returns
copy_sizeto reflect the actual number of bytes written to the caller buffer.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if ((data != NULL) && max_size) { | ||
| /* read data */ | ||
| dlt_buffer_read_block(buf, &read, data, (unsigned int)head.size); | ||
|
|
||
| if (delete) | ||
| /* read data — copy at most copy_size bytes to avoid overflow */ | ||
| dlt_buffer_read_block(buf, &read, data, (unsigned int)copy_size); |
There was a problem hiding this comment.
max_size is an int, so if a caller accidentally passes a negative value, the current logic will set copy_size = max_size (since max_size is truthy and head.size > max_size), and then cast that negative copy_size to unsigned int for dlt_buffer_read_block(), leading to a very large copy/read and memory corruption. Please treat max_size <= 0 as invalid (or as “no copy”) by changing the checks to max_size > 0, and ensure copy_size cannot become negative before casting to an unsigned size type.
| int next_read = read + (head.size - copy_size); | ||
| if ((unsigned int)next_read >= buf->size) | ||
| next_read -= (int)buf->size; | ||
| /* update buffer pointers */ | ||
| ((int *)(buf->shm))[1] = read; /* set new read pointer */ | ||
| ((int *)(buf->shm))[1] = next_read; |
There was a problem hiding this comment.
The wrap logic if ((unsigned int)next_read >= buf->size) next_read -= buf->size; changes a valid pointer value of exactly buf->size into 0. Elsewhere this code treats read == buf->size as in-range (e.g., the pointer range check allows == buf->size), and empty-buffer validation expects write == read when count == 0. If write ends up as buf->size and read gets normalized to 0, the next call will log an error and reset the buffer even though it’s actually empty. Consider matching the existing semantics by only wrapping on > buf->size (not >=), or by reusing the same <= / else pattern used below when advancing read by head.size.
| /* read data — copy at most copy_size bytes to avoid overflow */ | ||
| dlt_buffer_read_block(buf, &read, data, (unsigned int)copy_size); | ||
|
|
||
| if (delete) { |
There was a problem hiding this comment.
I believe that updating the read pointer based on the real copy size is a mistake since it won't point to the next header.
Problem
dlt_buffer_get()insrc/shared/dlt_common.clogs a warning whenhead.size > max_sizebut still passeshead.sizetodlt_buffer_read_block(), writing past the end of the caller's buffer — CWE-119 heap/stack overflow depending on the call site.Reproducer (from issue #829):
Fix (
src/shared/dlt_common.c)Three changes, one function:
Cap the copy: introduce
copy_size = min(head.size, max_size)and pass it todlt_buffer_read_block().Ring buffer consistency: when clipping occurs and
delete=1, advance the ring buffer read pointer by the fullhead.size(notcopy_size) so subsequent reads start at the correct position.Return value: return
copy_sizeinstead ofhead.sizeto correctly report the number of bytes written to the caller's buffer.+18 / -10 lines, 1 file.
Fixes #829.