CertC++-STR37

Arguments to character-handling functions must be representable as an unsigned char

Required inputs: IR

According to the C Standard, 7.4 [ ISO/IEC 9899:2011],

The header <ctype.h> declares several functions useful for classifying and mapping characters. In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.

See also undefined behavior 113.

This rule is applicable only to code that runs on platforms where the char data type is defined to have the same range, representation, and behavior as signed char.

Following are the character classification functions that this rule addresses:

isalnum() isalpha() isascii()XSI isblank()
iscntrl() isdigit() isgraph() islower()
isprint() ispunct() isspace() isupper()
isxdigit() toascii()XSI toupper() tolower()

XSI denotes an X/Open System Interfaces Extension to ISO/IEC 9945-POSIX. These functions are not defined by the C Standard.

This rule is a specific instance of STR34-C. Cast characters to unsigned char before converting to larger integer sizes.

Noncompliant Code Example

On implementations where plain char is signed, this code example is noncompliant because the parameter to isspace(), *t, is defined as a const char *, and this value might not be representable as an unsigned char:

#include <ctype.h>
#include <string.h>
 
size_t count_preceding_whitespace(const char *s) {
  const char *t = s;
  size_t length = strlen(s) + 1;
  while (isspace(*t) && (t - s < length)) {
    ++t;
  }
  return t - s;
} 

The argument to isspace() must be EOF or representable as an unsigned char; otherwise, the result is undefined.

Compliant Solution

This compliant solution casts the character to unsigned char before passing it as an argument to the isspace() function:

#include <ctype.h>
#include <string.h>
 
size_t count_preceding_whitespace(const char *s) {
  const char *t = s;
  size_t length = strlen(s) + 1;
  while (isspace((unsigned char)*t) && (t - s < length)) {
    ++t;
  }
  return t - s;
} 
Risk Assessment

Passing values to character handling functions that cannot be represented as an unsigned char to character handling functions is undefined behavior.

Rule Severity Likelihood Remediation Cost Priority Level
STR37-C Low Unlikely Low P3 L3
Related Guidelines
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard STR34-C. Cast characters to unsigned char before converting to larger integer sizes Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Passing arguments to character-handling functions that are not representable as unsigned char [chrsgnext] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-704, Incorrect Type Conversion or Cast 2017-06-14: CERT: Rule subset of CWE
Bibliography
[ ISO/IEC 9899:2011] 7.4, "Character Handling < ctype.h>"
[ Kettlewell 2002] Section 1.1, "< ctype.h> and Characters Types"
Excerpt from SEI CERT C++ Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/characters-and-strings-str/str37-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

cast_from_char_to_larger_type

Arguments to character-handling functions must be representable as an unsigned char

None

False

Options

ignored_typedefs

ignored_typedefs : set[str] = set()

Set of typedefs referring to signed character types which should be ignored by this rule. For instance, this can be used to avoid messages for the fixed-with integer types.
 

only_arguments_of

only_arguments_of

Type: set[str]

Default: {'isalnum', 'isalpha', 'isascii', 'isblank', 'iscntrl', 'isdigit', 'isgraph', 'islower', 'isprint', 'ispunct', 'isspace', 'isupper', 'isxdigit', 'toascii', 'tolower', 'toupper'}

Can be used to provide a set of function/macro names; only arguments to them will be considered then
 

show_operand_in_entity

show_operand_in_entity : bool = False

Whether entity should be "from->to" or "(from->to)operand".
 

type_system

type_system : bauhaus.ir.common.types.type_systems.TypeSystem = <bauhaus.ir.common.types.type_systems.CompilerTypeSystem object at 0x7f6f1c5fd510>

Which type system to use: compiler types, underlying types, essential types.