Additional projects

safemath

Unchecked size and offset arithmetic can wrap before allocation or bounds validation.

A header-only bool-returning API checks size addition, subtraction, multiplication, offsets and alignment without changing output on failure.

DevelopmentMITC99
Last verified
2026-09-18 · README, API contracts, header and verification reviewed
Platform scope
Portable C99; integration depends on caller-provided adapters
01 / FAILURE-PRESERVING ARITHMETIC

Checked helpers that leave outputs alone on failure

safemath is a header-only C/C++ utility for overflow-safe size arithmetic used in allocation sizing, buffer bounds and alignment. Every result-writing API returns bool; success writes the output and failure leaves the caller's output unchanged.

Callers must check the result. A false return means overflow, underflow, invalid input or unsupported configuration. The boolean does not identify which cause occurred.
02 / SIZE_T OPERATIONS

Add, subtract and multiply with explicit checks

sm_add, sm_sub and sm_mul handle size_t values. The implementation can select compiler overflow builtins when available or use a portable arithmetic path. SAFEMATH_FORCE_PORTABLE selects the latter; SAFEMATH_FORCE_BUILTINS requests builtins and fails compilation when the required support is absent. Defining both modes is a compile-time error.

C99 · CHECKED ALLOCATION SIZE
size_t bytes = 0u;
if (!sm_array_bytes(&bytes, count, sizeof(struct record))) {
    return false; /* bytes remains unchanged on failure */
}
allocate(bytes);

The API uses standard C headers and has no third-party runtime dependency. Optional SAFEMATH_NODISCARD attributes help compilers warn when a result is ignored.

03 / BUFFER AND ALLOCATION SIZE

Compose multiplication and addition without wrapping

sm_array_bytes(out, count, elem_size) checks multiplication. sm_alloc_bytes(out, count, elem_size, extra) checks both the array product and the extra-byte sum. A mathematically valid zero-sized element calculation returns zero; it is not treated as an error by itself.

element count × element size→checked array bytes+optional extra bytes→checked allocation size

These functions calculate a size; they do not allocate memory, validate a particular allocator's policy, or make a caller's later indexing safe by themselves.

04 / EXCLUSIVE-END BOUNDS

One-past-end can be valid without being dereferenceable

sm_offset_add(out, base, offset, limit) first checks that the sum fits in size_t, then requires the result to be no greater than limit. Equality is valid and represents an exclusive end. That result may mark the boundary after the final element; it must not be dereferenced as an array index.

RANGE CONTRACT
base + offset > SIZE_MAX → fail
↓ else
sum > limit → fail
↓ else
store sum, including sum == limit
caller still checks whether a position is dereferenceable
05 / POWER-OF-TWO ALIGNMENT

Validate alignment before rounding up

sm_is_pow2 returns true only for non-zero powers of two. sm_align_up requires such an alignment. sm_align_up_bytes combines checked array sizing and alignment. Invalid alignment and arithmetic overflow return false while preserving the output value.

Alignment helpers calculate a byte count. They do not guarantee that the eventual allocator returns an address with matching alignment; pointer alignment is the responsibility of the allocation interface.

06 / PORTABILITY EVIDENCE

Portable path and builtin path are checked separately

The repository verification log records C99 and C++11 tests, CMake/CTest, a package consumer, and auto/forced-portable paths. It also records MSVC, GCC and Clang host execution, static analysis and ARM Cortex-M0/M4/M7 smoke builds. A forced-builtin MSVC compile is expected to fail clearly because that compiler lacks the required builtins; that is feature detection, not a library test failure.

These are repository-recorded host and compile results. They are not evidence of allocation behavior in an application or correctness of every downstream bounds calculation.

CMAKE · STRICT CHECKS
cmake -S . -B build -DSAFEMATH_BUILD_TESTS=ON \
  -DSAFEMATH_BUILD_PROPERTY_TESTS=ON -DSAFEMATH_STRICT_WARNINGS=ON
cmake --build build
ctest --test-dir build --output-on-failure

API contracts · Portability · Verification log

07 / INTEGRATION BOUNDARIES

Useful arithmetic checks; no automatic safety proof

  • The boolean result does not distinguish overflow, underflow, invalid input and unsupported mode.
  • Every result must be checked; ignoring false can make stale output look valid.
  • Successful offset equality means one-past-end, not an accessible element.
  • The helper does not own memory or validate later pointer use.
  • Callers still need correct object counts, domains, lifetime and allocator alignment.

All products