CertC-STR38

Do not confuse narrow and wide character strings and functions

Required inputs: IR

Passing narrow string arguments to wide string functions or wide string arguments to narrow string functions can lead to unexpected and undefined behavior. Scaling problems are likely because of the difference in size between wide and narrow characters. (See ARR39-C. Do not add or subtract a scaled integer to a pointer.) Because wide strings are terminated by a null wide character and can contain null bytes, determining the length is also problematic.

Because wchar_t and char are distinct types, many compilers will produce a warning diagnostic if an inappropriate function is used. (See MSC00-C. Compile cleanly at high warning levels.)

Noncompliant Code Example (Wide Strings with Narrow String Functions) 

This noncompliant code example incorrectly uses the strncpy() function in an attempt to copy up to 10 wide characters. However, because wide characters can contain null bytes, the copy operation may end earlier than anticipated, resulting in the truncation of the wide string.

#include <stddef.h>
#include <string.h>
 
void func(void) {
  wchar_t wide_str1[]  = L"0123456789";
  wchar_t wide_str2[] =  L"0000000000";

  strncpy(wide_str2, wide_str1, 10);
}
Noncompliant Code Example (Narrow Strings with Wide String Functions)

This noncompliant code example incorrectly invokes the wcsncpy() function to copy up to 10 wide characters from narrow_str1 to  narrow_str2. Because narrow_str2 is a narrow string, it has insufficient memory to store the result of the copy and the copy will result in a buffer overflow.

#include <wchar.h>
 
void func(void) {
  char narrow_str1[] = "01234567890123456789";
  char narrow_str2[] = "0000000000";

  wcsncpy(narrow_str2, narrow_str1, 10);
}
Compliant Solution

This compliant solution uses the proper-width functions. Using wcsncpy() for wide character strings and strncpy() for narrow character strings ensures that data is not truncated and buffer overflow does not occur.

#include <string.h>
#include <wchar.h>
 
void func(void) {
  wchar_t wide_str1[] = L"0123456789";
  wchar_t wide_str2[] = L"0000000000";
  /* Use of proper-width function */
  wcsncpy(wide_str2, wide_str1, 10);

  char narrow_str1[] = "0123456789";
  char narrow_str2[] = "0000000000";
  /* Use of proper-width function */
  strncpy(narrow_str2, narrow_str1, 10);
}
Noncompliant Code Example ( strlen())

In this noncompliant code example, the strlen() function is used to determine the size of a wide character string:

#include <stdlib.h>
#include <string.h>
 
void func(void) {
  wchar_t wide_str1[] = L"0123456789";
  wchar_t *wide_str2 = (wchar_t*)malloc(strlen(wide_str1) + 1);
  if (wide_str2 == NULL) {
    /* Handle error */
  }
  /* ... */
  free(wide_str2);
  wide_str2 = NULL;
}

The strlen() function determines the number of characters that precede the terminating null character. However, wide characters can contain null bytes, particularly when expressing characters from the ASCII character set, as in this example. As a result, the strlen() function will return the number of bytes preceding the first null byte in the wide string. 

Compliant Solution

This compliant solution correctly calculates the number of bytes required to contain a copy of the wide string, including the terminating null wide character:

#include <stdlib.h>
#include <wchar.h>
 
void func(void) {
  wchar_t wide_str1[] = L"0123456789";
  wchar_t *wide_str2 = (wchar_t *)malloc(
    (wcslen(wide_str1) + 1) * sizeof(wchar_t));
  if (wide_str2 == NULL) {
    /* Handle error */
  }
  /* ... */

  free(wide_str2);
  wide_str2 = NULL;
}
Risk Assessment

Confusing narrow and wide character strings can result in buffer overflows, data truncation, and other defects.

Rule Severity Likelihood Remediation Cost Priority Level
STR38-C High Likely Low P27 L1
Bibliography
[ ISO/IEC 9899:2011] 7.24.2.4, "The strncpy Function"
7.29.4.2.2, "The wcsncpy Function"
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/characters-and-strings-str/str38-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

cafe_message

{}

None

False

Options

message_predicate

message_predicate

Type: typing.Callable[[Cafe_Message], bool] | None

Default: <bound method Str38Rule.msg_for_narrow_and_wide_char of <bauhaus.rules.cert.c.str.certc_str38.Str38Rule object at 0x7f6f17677ee0>>

If provided, a custom predicate to filter relevant messages. Receives the message node and should return True for messages to report.
 

reported_messages

reported_messages : set[int] | None = {167}

If provided, only messages of these types are reported.
 

reported_severities

reported_severities : set[str] = {'error', 'remark', 'warning'}

List of severities to display.
 

use_error_number

use_error_number : bool = False

Whether the error number from the frontend should be used.
 

use_rule_severity

use_rule_severity : bool = True

Whether the rule's severity or the compiler's severity should be used.