Skip to content

Commit 605395c

Browse files
committed
fortify: Add protection for strlcat()
The definition of strcat() was defined in terms of unfortified strlcat(), but that meant there was no bounds checking done on the internal strlen() calls, and the (bounded) copy would be performed before reporting a failure. Additionally, pathological cases (i.e. unterminated destination buffer) did not make calls to fortify_panic(), which will make future unit testing more difficult. Instead, explicitly define a fortified strlcat() wrapper for strcat() to use. Signed-off-by: Kees Cook <[email protected]>
1 parent 21a2c74 commit 605395c

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

include/linux/fortify-string.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,70 @@ __FORTIFY_INLINE ssize_t strscpy(char * const POS p, const char * const POS q, s
371371
return __real_strscpy(p, q, len);
372372
}
373373

374+
/* Defined after fortified strlen() to reuse it. */
375+
extern size_t __real_strlcat(char *p, const char *q, size_t avail) __RENAME(strlcat);
376+
/**
377+
* strlcat - Append a string to an existing string
378+
*
379+
* @p: pointer to %NUL-terminated string to append to
380+
* @q: pointer to %NUL-terminated string to append from
381+
* @avail: Maximum bytes available in @p
382+
*
383+
* Appends %NUL-terminated string @q after the %NUL-terminated
384+
* string at @p, but will not write beyond @avail bytes total,
385+
* potentially truncating the copy from @q. @p will stay
386+
* %NUL-terminated only if a %NUL already existed within
387+
* the @avail bytes of @p. If so, the resulting number of
388+
* bytes copied from @q will be at most "@avail - strlen(@p) - 1".
389+
*
390+
* Do not use this function. While FORTIFY_SOURCE tries to avoid
391+
* read and write overflows, this is only possible when the sizes
392+
* of @p and @q are known to the compiler. Prefer building the
393+
* string with formatting, via scnprintf(), seq_buf, or similar.
394+
*
395+
* Returns total bytes that _would_ have been contained by @p
396+
* regardless of truncation, similar to snprintf(). If return
397+
* value is >= @avail, the string has been truncated.
398+
*
399+
*/
400+
__FORTIFY_INLINE
401+
size_t strlcat(char * const POS p, const char * const POS q, size_t avail)
402+
{
403+
const size_t p_size = __member_size(p);
404+
const size_t q_size = __member_size(q);
405+
size_t p_len, copy_len;
406+
size_t actual, wanted;
407+
408+
/* Give up immediately if both buffer sizes are unknown. */
409+
if (p_size == SIZE_MAX && q_size == SIZE_MAX)
410+
return __real_strlcat(p, q, avail);
411+
412+
p_len = strnlen(p, avail);
413+
copy_len = strlen(q);
414+
wanted = actual = p_len + copy_len;
415+
416+
/* Cannot append any more: report truncation. */
417+
if (avail <= p_len)
418+
return wanted;
419+
420+
/* Give up if string is already overflowed. */
421+
if (p_size <= p_len)
422+
fortify_panic(__func__);
423+
424+
if (actual >= avail) {
425+
copy_len = avail - p_len - 1;
426+
actual = p_len + copy_len;
427+
}
428+
429+
/* Give up if copy will overflow. */
430+
if (p_size <= actual)
431+
fortify_panic(__func__);
432+
__underlying_memcpy(p + p_len, q, copy_len);
433+
p[actual] = '\0';
434+
435+
return wanted;
436+
}
437+
374438
/**
375439
* strncat - Append a string to an existing string
376440
*

0 commit comments

Comments
 (0)