cybersecurity · intermediate · ~20 min
Defensive concat with truncation reporting; the strlcat contract.
Write a sane, truncation-aware string concatenation — the safe replacement for strncat, whose third argument is famously NOT the buffer size.
Implement size_t safe_strncat(char *dst, size_t cap, const char *src) following the strlcat contract:
dst is a NUL-terminated string living in a buffer of total size cap.src as fits, always leaving room for the terminating NUL.dst.cap were unlimited (strlen(dst) + strlen(src)), so the caller can detect truncation.dst has no NUL within cap bytes (malformed), return cap and leave dst unchanged.dst: a buffer holding a NUL-terminated string, total size cap.cap: the full size of the dst buffer in bytes.src: a NUL-terminated string to append.Returns the would-be total length as a size_t (a value >= cap means truncation happened).
buf="hi" (cap 16) + "!" -> 3, buf = "hi!"
buf="hi" (cap 6) + "!world" -> 8, buf = "hi!wo" (truncated; return is would-be length)
buf="" (cap 16) + "abc" -> 3, buf = "abc"
buf="hello"(cap 16)+ "" -> 5, buf = "hello"
dst with no NUL in cap -> cap, dst unchanged
src or empty dst.src larger than the remaining space: truncate, but still report the would-be length.dst missing its NUL within cap: return cap, no write.cap is the buffer size, not a byte budget — do not confuse it with strncat's argument.dst.strncat has a famously hostile API (the third argument is the max bytes appended, NOT the buffer size). Writing a saner version forces you to internalise the off-by-one nightmare that has produced so many CVEs.
A NUL-terminated dst in a cap-byte buffer, the buffer size cap, and a NUL-terminated src.
The would-be total length (>= cap signals truncation); dst always NUL-terminated.
cap is the buffer size, not a byte budget; no UB even if dst lacks a NUL within cap.
#include <stddef.h>
size_t safe_strncat(char *dst, size_t cap, const char *src) { /* TODO */ return 0; }
Confusing cap (buffer size) with 'max bytes to append' (the strncat trap). Forgetting the +1 for the NUL.
Empty src; empty dst; src bigger than remaining space; dst missing its NUL.
O(strlen(dst) + strlen(src)).
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.