Contenu principal

CERT C: Rec. INT15-C

R2026b

Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types

Since R2026b

Description

Use intmax_t or uintmax_t for formatted IO on programmer-defined integer types1

Polyspace Implementation

Polyspace checks for the issue Formatted I/O with Programmer-Defined Integer Types.

Examples

expand all

Issue

The issue occurs when a programmer-defined integer type is used as an argument to a formatted I/O function such as printf() or scanf() without casting to intmax_t or uintmax_t.

Polyspace® considers a type to be a programmer-defined integer type when these conditions are true:

  • The type is a typedef.

  • The underlying type is integral but not an enumeration.

  • The underlying type is not intmax_t or uintmax_t.

The checker flags a violation in these cases:

  • The programmer-defined integer type argument has no cast.

  • The argument is cast to another programmer-defined integer type instead of intmax_t or uintmax_t.

  • The argument is cast to a standard type such as unsigned long long or long long instead of intmax_t or uintmax_t.

  • A cast to intmax_t or uintmax_t is wrapped inside a narrower cast, for example (unsigned long long)(uintmax_t)x.

The checker operates on ISO and POSIX variants of printf() and scanf() family functions, including fprintf(), fscanf(), and similar functions.

Risk

Programmer-defined integer types can change width across platforms or when the typedef is updated. If you print or scan these types using a fixed-width format specifier such as %llu, the format specifier and the actual type width can silently diverge. This mismatch causes:

  • Undefined behavior in printf() when the argument width does not match what the format specifier expects. The output can be corrupted, or the program can crash.

  • Data loss in scanf() when the format specifier reads more or fewer bytes than the receiving variable can hold, truncating the value or overwriting adjacent memory.

Fix
  • For printf() family functions, cast the programmer-defined integer type to uintmax_t or intmax_t and use the %ju or %jd format specifier.

  • For scanf() family functions, scan into a temporary uintmax_t or intmax_t variable using %ju or %jd, then assign the result to the programmer-defined type after a range check.

Example — Printing Programmer-Defined Unsigned Integer Type

In this example, the programmer-defined type counter_t is used in calls to printf() without casting to uintmax_t. Polyspace reports a violation on each argument that does not use the prescribed cast.


#include <stdio.h>
#include <stdint.h>

typedef unsigned long counter_t;

void print_counts(counter_t a, counter_t b) {
    printf("%llu", (unsigned long long)a);  // Noncompliant
    printf("%lu", b);                       // Noncompliant
}

Polyspace reports a violation on the first call because counter_t is cast to unsigned long long instead of uintmax_t. Polyspace reports a violation on the second call because counter_t is passed without any cast to uintmax_t.

Correction — Cast to uintmax_t with %ju Format Specifier

Cast the programmer-defined type to uintmax_t and use the %ju format specifier. This approach produces correct output regardless of the width of counter_t on a given platform.


#include <stdio.h>
#include <stdint.h>

typedef unsigned long counter_t;

void print_counts(counter_t a, counter_t b) {
    printf("%ju", (uintmax_t)a);  // Compliant
    printf("%ju", (uintmax_t)b);  // Compliant
}
Example — Scanning Programmer-Defined Integer Type Directly

In this example, the address of count, which has the programmer-defined type counter_t, is passed directly to fscanf(). Polyspace reports a violation because the format specifier can read more or fewer bytes than the variable holds.


#include <stdio.h>
#include <stdint.h>

typedef unsigned long counter_t;

void read_count(counter_t *count) {
    fscanf(stdin, "%llu", count);  // Noncompliant
}

Polyspace reports a violation because count points to a programmer-defined integer type that is passed to fscanf() without using a uintmax_t intermediate variable.

Correction — Scan into uintmax_t Temporary with Range Check

Scan into a uintmax_t temporary variable using the %ju format specifier, then assign to the programmer-defined type after verifying the value is in range.


#include <stdio.h>
#include <stdint.h>
#include <limits.h>

typedef unsigned long counter_t;
#define COUNTER_MAX ULONG_MAX

void read_count(counter_t *count) {
    uintmax_t temp;
    if (fscanf(stdin, "%ju", &temp) == 1) {  // Compliant
        if (temp <= COUNTER_MAX) {
            *count = (counter_t)temp;
        }
    }
}

Check Information

Group: Rec. 04. Integers (INT)
PQL Name: std.cert.INT15_C

Version History

Introduced in R2026b


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.