Two cumulative-snprintf patterns in log.c (rsyserr) and main.c
(output_itemized_counts) had the shape
len = snprintf(buf, sizeof buf, ...);
len += snprintf(buf+len, sizeof buf - len, ...);
with no guard between calls. snprintf returns the would-have-been
length on truncation, so a truncated first call leaves
"sizeof buf - len" as a negative-then-promoted-to-size_t value,
underflowing into a huge size_t and writing past buf.
Realistic exposure is small in both cases (log header well under
buffer, only ~5 itemized iterations writing ~25 chars each into a
1024-byte buffer) but the defect class matches
bb0a8118 and the
fix is cheap. Guard before each subsequent call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
char buf[BIGPATHBUFLEN];
size_t len;
+ /* snprintf returns the would-have-been length on truncation, so
+ * each cumulative call must be guarded; if not, sizeof buf - len
+ * can underflow when promoted to size_t and the next call writes
+ * past the buffer. */
len = snprintf(buf, sizeof buf, RSYNC_NAME ": [%s] ", who_am_i());
- va_start(ap, format);
- len += vsnprintf(buf + len, sizeof buf - len, format, ap);
- va_end(ap);
+ if (len < sizeof buf) {
+ va_start(ap, format);
+ len += vsnprintf(buf + len, sizeof buf - len, format, ap);
+ va_end(ap);
+ }
if (len < sizeof buf) {
len += snprintf(buf + len, sizeof buf - len,
counts[0] -= counts[1] + counts[2] + counts[3] + counts[4];
for (j = 0; j < 5; j++) {
if (counts[j]) {
+ /* snprintf can return more than its size arg
+ * on truncation; keep len <= sizeof buf - 2 so
+ * the closing ')' and trailing NUL always
+ * have room and the next iteration's
+ * sizeof buf - len - 2 cannot underflow. */
+ if (len >= (int)sizeof buf - 2)
+ break;
len += snprintf(buf+len, sizeof buf - len - 2,
"%s%s: %s",
pre, labels[j], comma_num(counts[j]));
+ if (len > (int)sizeof buf - 2)
+ len = (int)sizeof buf - 2;
pre = ", ";
}
}