January 23, 2026·6 min read·engineeringcglibcmemoryc23

The memory contracts arriving in glibc 2.43

glibc 2.43 implements C23 memory APIs. Their contracts explain when callers can supply allocation metadata and what explicit erasure can guarantee.

The glibc 2.43 announcement, published on January 23, adds several C23 interfaces that make memory operations more explicit. The additions include sized deallocation, an alignment query and an erasure function whose writes must survive optimization. I am interested in the obligations these interfaces place on callers. A function that accepts more information can express more intent, but it also gives a program more opportunities to provide incorrect information.

C was my first language, and I have worked with it for thirty years. I still value its importance for interoperation, while treating undefined behavior as a boundary that requires deliberate attention. These additions fit that view of the language. They make particular memory contracts expressible through standard library calls. They do not establish that a pointer has the right ownership, or that an allocation length still matches the value a caller remembers.

Sized deallocation requires allocation history

The versioned glibc manual documents free_sized as a deallocator for storage obtained through malloc, calloc or realloc. Its size argument must match the total requested allocation size. This is information about the allocation request. The amount of useful data currently stored in the allocation may be smaller, and that useful length is not the argument the function requires.

That distinction matters for a buffer with separate length and capacity fields. Suppose a program requests space for a message, writes a shorter message into it, and retains both numbers. The deallocator requires the requested allocation size. Passing the message length would violate the documented contract even if every byte of the message lies inside the allocation. My preference is to make that difference visible in the names of the fields that retain the values.

The manual also restricts the allocation families that may use this function. Aligned allocations require the corresponding aligned deallocator. Storage returned by strdup or strndup must continue to use free, even though the caller owns the responsibility to release it. Knowing that a string has a particular length does not establish the allocation request that produced its storage. Replacing every occurrence of free by a sized call would therefore be an incorrect migration strategy.

A small example keeps the allocation request beside the deallocation. It avoids a resizable buffer because such a buffer would also need to update the recorded size whenever a resize succeeds.

#include <stdlib.h>

int allocate_buffer(void)
{
    const size_t allocation_size = 4096;
    unsigned char *buffer = malloc(allocation_size);
    if (buffer == NULL)
        return -1;

    /* Work with buffer without transferring its ownership. */

    free_sized(buffer, allocation_size);
    return 0;
}

This example follows the allocation family and matching-size requirements in the manual. It deliberately makes no performance claim. A call with correct metadata expresses a stronger contract, but the function signature alone cannot tell us whether an allocator uses that metadata to avoid work. That question requires implementation evidence and measurements for the allocator actually in use.

The reviewed implementation patch is useful evidence for understanding the design. Its sized functions forward to free and do not validate the supplied size or alignment. The comments explain that forwarding preserves existing malloc replacements, and leave validation to sanitizers. That patch is a reason to avoid advertising the interface as a new checking mechanism. It also shows why a program can appear to work despite supplying metadata that violates the API contract.

For my own adoption decision, I would require a clear account of where the allocation size comes from. Carrying a guessed size solely to call the new function would add a correctness obligation without an established benefit. An owner that already records the exact request has a more credible use for the interface. The manual gives enough constraints to review that decision locally, without assuming the allocator will diagnose a mistake.

Alignment has a separate contract

free_aligned_sized accepts the original alignment as well as the requested size. The deallocation documentation permits allocations from aligned_alloc, memalign and posix_memalign, and requires both values to match the original request. A stronger alignment that happens to hold for a returned address does not become the alignment argument that the caller originally supplied.

The distinction is relevant because the release also adds memalignment. The aligned-allocation chapter says this function returns the alignment of the pointer as a power of two. A null pointer produces zero. For the C23 interface, the argument must be a valid pointer to an object or a null pointer. glibc documents a broader extension for arbitrary pointer bit patterns, which portable code should keep separate from the standard contract.

An address query cannot reconstruct the history of an allocation. An allocation requested with one alignment may happen to satisfy a greater alignment, so feeding the query result into a sized aligned deallocation would confuse two different facts. The deallocator needs the request. The query describes the address. I would keep the requested alignment in the same ownership record as the requested allocation size if I chose to use this deallocation API.

The manual explains why the query exists despite familiar integer-and-mask tests. Unconventional platforms need not encode alignment in the low bits of a pointer in the way conventional platforms do. A standard function can express the alignment question without requiring that representation assumption. This is consistent with what I value about C interoperation. Code that crosses platform boundaries needs to distinguish a language guarantee from a property of one machine.

The documentation also cautions against making alignment dispatch more complicated than necessary. For its example of reading an integer from possibly unaligned storage, it prefers copying into a properly aligned local object with memcpy. The compiler can generate the appropriate access for the architecture. I would read the new query as support for code that actually needs the alignment value, without treating it as a requirement to add branches around every memory access.

Erasure must remain observable to the implementation

memset_explicit addresses a different problem. The glibc erasure documentation explains that an optimizer can remove an ordinary clearing operation when no correct execution can read the object afterward. Clearing sensitive storage is intended to reduce exposure through bugs. That purpose can conflict with reasoning based only on the behavior of correct executions.

The new function takes a pointer, a byte value and a byte count, following the shape of memset. It guarantees that the writes happen even if ordinary optimization would regard them as unnecessary. The original C proposal describes the motivation in terms of passwords and keying material whose contents should be cleared after use. glibc now provides a standard spelling for that operation alongside its existing explicit_bzero interface.

#include <stddef.h>
#include <string.h>

void erase_buffer(unsigned char *buffer, size_t size)
{
    memset_explicit(buffer, 0, size);
}

The caller must supply a valid destination covering the requested bytes. More importantly, this function only describes the erasure of that destination. It does not establish that this is the only copy of a secret. The glibc manual warns that registers and temporary stack storage may retain copies that the library function cannot identify. Its guarantee should therefore appear in a security review as a bounded property of a particular operation.

The manual describes an awkward consequence of taking an address solely to erase an object. Data that could otherwise remain in registers may acquire a RAM copy for the erasure call, while the original register value remains. Declaring the sensitive variable volatile can worsen the situation by keeping it in memory and producing more copies. I would avoid claiming complete removal of a secret based on either technique. The documented guarantee is that the specified writes are not eliminated as unnecessary.

The separation between releasing storage and erasing its contents also matters. The deallocation manual warns that freeing a block changes its contents and makes it available for reuse. I would require an erasure decision before deallocation when the program owns sensitive data in that block. A deallocator accepting a size does not itself express a request to overwrite those bytes.

Adoption still needs a compatibility review

The release changes more than the memory entry points. Its C23 assert macro accepts variable arguments, which supports commas inside compound literal initializers without extra surrounding parentheses. The announcement also identifies const-preserving macros for search functions that return pointers into their inputs. It warns that this change can expose compilation problems in code that is not prepared for the updated declarations.

For an upgrade review, I would separate those source compatibility checks from decisions to adopt the new memory functions. Existing callers can need attention even when nobody chooses to use sized deallocation. Conversely, adding a new call requires reviewing that call against the documented allocation or erasure contract. The release announcement and the versioned manual provide different parts of that review, and both deserve a place beside the proposed code change.