Skip to content
GUIDES

C runtime contracts

Implicit output through puts, putchar, printf and vprintf carries a named standard-stream requirement for stdout through helpers. The C entry point begins with standard streams available; known closure, replacement or unknown mutation prevents later discharge. This condition is separate from format and argument validation.

RFC 0024 adds checked contracts for comparison/search operations, scalar math, stream and descriptor I/O, formatted output and variadic forwarding. Calls must satisfy the operation’s memory and type requirements. Reports identify the C library as a modeled trusted dependency. Source definitions and resolved callback implementations take precedence over a familiar library name.

memcmp, strcmp, bounded comparisons and searches require initialized input. A search result remains a nullable borrow from the original object. Freeing that object invalidates a saved search result. Floating operations such as fabs and floor add no memory requirements; prediction builtins preserve their first argument and both possible branch outcomes.

read and fread initialize the prefix described by their successful returned count, in bytes and complete elements respectively. Testing success never initializes the unused tail. fgets establishes a terminated initialized prefix only on its non-null result. Streams must have live matching provenance; standard streams have explicit library provenance.

char buffer[64];
ssize_t received = read(fd, buffer, sizeof buffer);
if (received > 0) {
use_byte(buffer[0]); /* initialized */
/* buffer[63] still needs proof that received is at least 64 */
}

The bounded format parser checks ordinary narrow integer, character, string, pointer and floating conversions. Consumed arguments need the correct promoted types, including int for * width and precision. A precision can bound a %s read without requiring a terminator beyond that bound. Formatting input and output must satisfy the operation’s non-overlap requirement.

For snprintf, the result is the length that would have been written. After a nonnegative result and positive capacity, the destination contains an initialized terminated prefix. This does not prove the entire capacity initialized. Exact literal bounds can establish more; unbounded sprintf requires a sufficient output bound. Fortified forms retain their additional object-size checks.

va_start creates an active argument list. va_copy creates an independent active cursor. A vprintf family traversal consumes its cursor, and every locally started or copied list needs va_end on each returning path. Checked helpers can forward a format and trailing arguments, or an incoming argument list, through portable requirements. Callers must discharge those requirements against their actual format and arguments. A helper that may consume an incoming list retires the caller’s cursor for further traversal. The initial cursor model covers local variables and incoming parameters; argument lists embedded in records remain unsupported.

The parser is limited to 4,096 format bytes, 64 conversions and 128 argument slots. Dynamic formats that cannot be bound through a supported interface, positional and wide conversions, %n, direct va_arg, escaping lists and unrepresented output bounds remain explicit coverage gaps. Directly ending an incoming caller-owned cursor is outside this milestone; end locally owned lists. No format annotation or warning suppression supplies missing checked evidence.

For a constant positive snprintf capacity, testing the returned count can also establish the exact written prefix. If 0 <= n && n < sizeof buffer, buffer[n] is the written terminator. If n >= sizeof buffer, truncation initializes the capacity and its final NUL. A negative or overwritten result establishes neither.

Runtime records use checked encoding 9, summary format 23 and sidecar format 24. Rebuild objects carrying older sidecars. The cache validates the executable and source dependencies before reusing these records.