CertC-STR07¶
Use the bounds-checking interfaces for string manipulation
Required inputs: IR
The C Standard, Annex K (normative), defines alternative versions of
standard string-handling functions designed to be safer replacements for
existing functions. For example, it defines the
strcpy_s(),
strcat_s(),
strncpy_s(), and
strncat_s() functions as replacements for
strcpy(),
strcat(),
strncpy(), and
strncat(), respectively.
The Annex K functions were created by Microsoft to help retrofit its existing legacy code base in response to numerous, well-publicized security incidents over the past decade. These functions were subsequently proposed to the international standardization working group for the programming language C (ISO/IEC JTC1/SC22/WG14) for standardization.
The
strcpy_s() function, for example, has this signature:
errno_t strcpy_s( char * restrict s1, rsize_t s1max, const char * restrict s2 );
The signature is similar to
strcpy() but takes an extra argument of type
rsize_t that specifies the maximum length of the destination
buffer. Functions that accept parameters of type
rsize_t diagnose a constraint violation if the values of those
parameters are greater than
RSIZE_MAX. Extremely large object sizes are frequently a sign that
an object's size was calculated incorrectly. For example, negative numbers
appear as very large positive numbers when converted to an unsigned type like
size_t. For those reasons, it is sometimes beneficial to restrict
the range of object sizes to detect errors. For machines with large address
spaces, the C Standard, Annex K, recommends that
RSIZE_MAX be defined as the smaller of the size of the largest
object supported or
(SIZE_MAX >> 1), even if this limit is smaller than the size
of some legitimate, but very large, objects (see also
INT01-C.
Use rsize_t or size_t for all integer values representing the size of an
object).
The semantics of
strcpy_s() are similar to the semantics of
strcpy(). When there are no input validation errors, the
strcpy_s() function copies characters from a source string to a
destination character array up to and including the terminating null character.
The function returns 0 on success.
The
strcpy_s() function succeeds only when the source string can be
fully copied to the destination without overflowing the destination buffer.
Specifically, the following checks are made:
- The source and destination pointers are checked to see if they are
NULL. - The maximum length of the destination buffer is checked to see if it is equal
to 0, greater than
RSIZE_MAX, or less than or equal to the length of the source string. - Copying is not allowed between objects that overlap.
When a runtime-constraint violation is detected, the destination string is set
to the null string (as long as it is not a null pointer, and the maximum length
of the destination buffer is greater than 0 and not greater than
RSIZE_MAX), and the function returns a nonzero value. In the
following example, the
strcpy_s() function is used to copy
src1 to
dst1:
char src1[100] = "hello";
char src2[8] = {'g','o','o','d','b','y','e','\0'};
char dst1[6];
char dst2[5];
int r1;
int r2;
r1 = strcpy_s(dst1, sizeof(dst1), src1);
r2 = strcpy_s(dst2, sizeof(dst2), src2);
However, the call to copy
src2 to
dst2 fails because insufficient space is available to copy the
entire string, which consists of eight characters, to the destination buffer.
As a result,
r2 is assigned a nonzero value and
dst2[0] is set to the null character.
Users of the C Standard Annex K functions are less likely to introduce a security flaw because the size of the destination buffer and the maximum number of characters to append must be specified. ISO/IEC TR 24731 Part II [ ISO/IEC TR 24731-2:2010] offers another approach, supplying functions that allocate enough memory for their results. ISO/IEC TR 24731 Part II functions also ensure null termination of the destination string.
The C Standard Annex K functions are still capable of overflowing a buffer if the maximum length of the destination buffer and number of characters to copy are incorrectly specified. ISO/IEC TR 24731 Part II functions can make it more difficult to keep track of memory that must be freed, leading to memory leaks. As a result, the C Standard Annex K and the ISO/IEC TR 24731 Part II functions are not particularly secure but may be useful in preventive maintenance to reduce the likelihood of vulnerabilities in an existing legacy code base.
Noncompliant Code Example
This noncompliant code overflows its buffer if
msg is too long, and it has
undefined
behavior if
msg is a null pointer:
void complain(const char *msg) {
static const char prefix[] = "Error: ";
static const char suffix[] = "\n";
char buf[BUFSIZ];
strcpy(buf, prefix);
strcat(buf, msg);
strcat(buf, suffix);
fputs(buf, stderr);
}
Compliant Solution (Runtime)
This compliant solution will not overflow its buffer:
void complain(const char *msg) {
errno_t err;
static const char prefix[] = "Error: ";
static const char suffix[] = "\n";
char buf[BUFSIZ];
err = strcpy_s(buf, sizeof(buf), prefix);
if (err != 0) {
/* Handle error */
}
err = strcat_s(buf, sizeof(buf), msg);
if (err != 0) {
/* Handle error */
}
err = strcat_s(buf, sizeof(buf), suffix);
if (err != 0) {
/* Handle error */
}
fputs(buf, stderr);
}
Compliant Solution (Partial Compile Time)
This compliant solution performs some of the checking at compile time using a static assertion (see DCL03-C. Use a static assertion to test the value of a constant expression).
void complain(const char *msg) {
errno_t err;
static const char prefix[] = "Error: ";
static const char suffix[] = "\n";
char buf[BUFSIZ];
/*
* Ensure that more than one character
* is available for msg
*/
static_assert(sizeof(buf) > sizeof(prefix) + sizeof(suffix),
"Buffer for complain() is too small");
strcpy(buf, prefix);
err = strcat_s(buf, sizeof(buf), msg);
if (err != 0) {
/* Handle error */
}
err = strcat_s(buf, sizeof(buf), suffix);
if (err != 0) {
/* Handle error */
}
fputs(buf, stderr);
}
Risk Assessment
String-handling functions defined in the C Standard, subclause 7.24, and elsewhere are susceptible to common programming errors that can lead to serious, exploitable vulnerabilities. Proper use of the C11 Annex K functions can eliminate most of these issues.
| Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| STR07-C | High | Probable | Medium | P12 | L1 |
Related Guidelines
| ISO/IEC TR 24731-2:2010 | |
| ISO/IEC TR 24772:2013 | Use of Libraries [TRJ] |
Bibliography
| [ Seacord 2005b] | "Managed String Library for C, C/C++" |
| [ Seacord 2013] | Chapter 2, "Strings" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
forbidden_libfunc_call |
Use the bounds-checking interface instead of this function. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
blacklist¶
blacklist
Dictionary of header globbing to (list of) function name globbing(s) of forbidden functions. These string manipulation functions should be replaced by their _s counterpart.Type: dict[bauhaus.analysis.config.FileGlobPattern, list[bauhaus.analysis.config.GlobPattern]]
Default:
{ 'string.h': ['strcpy', 'strcat', 'strncpy', 'strncat'] }