diff --git a/defensive-coding/en-US/C/Libc.xml b/defensive-coding/en-US/C/Libc.xml index 1f16fee..970f5bf 100644 --- a/defensive-coding/en-US/C/Libc.xml +++ b/defensive-coding/en-US/C/Libc.xml @@ -249,26 +249,103 @@
- <function>strncpy</function> and related functions + <function>strncpy</function> - There are other functions which operator on NUL-terminated - strings and take a length argument which affects the number of - bytes written to the destination: strncpy, - strncat, and stpncpy. - These functions do not ensure that the result string is - NUL-terminated. For strncpy, - NUL termination can be added this way: + The strncpy function does not ensure that + the target buffer is NUL-terminated. A common idiom for + ensuring NUL termination is: + + Another approach uses the strncat + function for this purpose: + + + + +
+
+ <function>strncat</function> + + The length argument of the strncat + function specifies the maximum number of characters copied + from the source buffer, excluding the terminating NUL + character. This means that the required number of bytes in + the destination buffer is the length of the original string, + plus the length argument in the strncat + call, plus one. Consequently, this function is rarely + appropriate for performing a length-checked string operation, + with the notable exception of the strcpy + emulation described in . + + + To implement a length-checked string append, you can use an + approach similar to : + + + + + + In many cases, including this one, the string concatenation + can be avoided by combining everything into a single format + string: + + + + + + But you should must not dynamically construct format strings + to avoid concatenation because this would prevent GCC from + type-checking the argument lists. + + + It is not possible to use format strings like + "%s%s" to implement concatenation, unless + you use separate buffers. snprintf does + not support overlapping source and target strings. + +
+
+ <function>strlcpy</function> and + <function>strlcat</function> Some systems support strlcpy and strlcat functions which behave this way, - but these functions are not part of GNU libc. Using - snprintf with a suitable format string is a - simple (albeit slightly slower) replacement. + but these functions are not part of GNU libc. + strlcpy is often replaced with + snprintf with a "%s" + format string. See for a caveat + related to the snprintf return value. + + + To emulate strlcat, use the approach + described in . + +
+
+ ISO C11 Annex K *<function>_s</function> functions + + ISO C11 adds another set of length-checking functions, but GNU + libc currently does not implement them. + +
+
+ Other <function>strn</function>* and + <function>stpn</function>* functions + + GNU libc contains additional functions with different variants + of length checking. Consult the documentation before using + them to find out what the length actually means.
diff --git a/defensive-coding/en-US/C/snippets/String-Functions-strncat-as-strncpy.xml b/defensive-coding/en-US/C/snippets/String-Functions-strncat-as-strncpy.xml new file mode 100644 index 0000000..0895eaf --- /dev/null +++ b/defensive-coding/en-US/C/snippets/String-Functions-strncat-as-strncpy.xml @@ -0,0 +1,8 @@ + + + + +buf[0] = '\0'; +strncpy(buf, data, sizeof(buf) - 1); + diff --git a/defensive-coding/en-US/C/snippets/String-Functions-strncat-emulation.xml b/defensive-coding/en-US/C/snippets/String-Functions-strncat-emulation.xml new file mode 100644 index 0000000..12f5437 --- /dev/null +++ b/defensive-coding/en-US/C/snippets/String-Functions-strncat-emulation.xml @@ -0,0 +1,9 @@ + + + + +char buf[10]; +snprintf(buf, sizeof(buf), "%s", prefix); +snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s", data); + diff --git a/defensive-coding/en-US/C/snippets/String-Functions-strncat-merged.xml b/defensive-coding/en-US/C/snippets/String-Functions-strncat-merged.xml new file mode 100644 index 0000000..3deaa94 --- /dev/null +++ b/defensive-coding/en-US/C/snippets/String-Functions-strncat-merged.xml @@ -0,0 +1,7 @@ + + + + +snprintf(buf, sizeof(buf), "%s%s", prefix, data); + diff --git a/defensive-coding/src/C-String-Functions.c b/defensive-coding/src/C-String-Functions.c index 3522eaa..34d9c31 100644 --- a/defensive-coding/src/C-String-Functions.c +++ b/defensive-coding/src/C-String-Functions.c @@ -68,5 +68,24 @@ main(void) //- assert(strlen(buf) == 9); assert(strncmp(buf, data, 9) == 0); + //+ C String-Functions-strncat-as-strncpy + buf[0] = '\0'; + strncpy(buf, data, sizeof(buf) - 1); + //- + } + { + const char *const prefix = "prefix"; + const char *const data = " suffix"; + + //+ C String-Functions-strncat-emulation + char buf[10]; + snprintf(buf, sizeof(buf), "%s", prefix); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s", data); + //- + puts(buf); + //+ C String-Functions-strncat-merged + snprintf(buf, sizeof(buf), "%s%s", prefix, data); + //- + puts(buf); } }