Skip to main content

Module allocator

Module allocator 

Source
Expand description

C ABI allocator compatibility — xmlMemSetup, xmlMemGet, xmlMalloc, xmlFree, etc. (§58).

This module implements the complete memory hook system exposed by libxml2:

  • Global allocator function pointers (malloc, realloc, free, strdup)
  • xmlMemSetup() / xmlMemGet() — set/get allocator hooks
  • xmlGcMemSetup() / xmlGcMemGet() — GC-aware allocator hooks (wrappers)
  • xmlMalloc() / xmlMallocAtomic() / xmlRealloc() / xmlFree() / xmlMemStrdup()
  • xmlMemUsed() / xmlMemBlocks() — allocation tracking
  • xmlMemDisplay() / xmlMemShow() — debugging output

§Phase 1 status

Complete — all allocator APIs are implemented; state lives in the five exported function-pointer variables exactly as upstream (globals.c), giving xmlMemSetup and direct xmlMalloc = custom assignment one shared override mechanism (R-000176). Since 11.1-Z.3 (R-000178) the default bodies are plain libc malloc/realloc/free/strdup wrappers — no Rust std::alloc layout fabrication (that was UB) and no accounting, byte-identical with upstream’s globals.c defaults (xmlMalloc = malloc etc.).

§Safety

Allocator hooks are unsafe because they operate on raw pointers and are called from C code. Every public function documents its safety contract.

§Upstream contract

The parity target is libxml2 2.15.3 (SRC-LIBXML2-2.15.0-XMLMEMORY-C: oracle/historical/src/libxml2-2.15.0/xmlmemory.c) plus the allocator globals of globals.c. The 5 allocator entry points (xmlMalloc, xmlMallocAtomic, xmlRealloc, xmlFree, xmlMemStrdup) are exported as DATA function-pointer globals matching the upstream XMLPUBVAR declarations (R-000162). Upstream initializes them to the C runtime functions (xmlFree = free, xmlMalloc = malloc, xmlMallocAtomic = malloc, xmlRealloc = realloc, xmlMemStrdup = xmlPosixStrdup); the candidate initializes them to the *Default bodies, which are libc wrappers with identical observable behavior.

§Conceptual behavior

This module implements the complete libxml2 memory-hook system: swappable allocator hooks via xmlMemSetup/xmlMemGet (and the GC aliases), plus the deprecated debug-named surface (xmlMemMalloc/xmlMemFree/xmlMemRealloc/ xmlMemoryStrdup and the *Loc variants) which upstream keeps as a separately-tagged debug allocator. There are therefore two allocation planes, exactly as in upstream 2.15.0:

  • the five exported variables (the hook system): default = libc, and xmlMemSetup/direct assignment re-route them. Untracked — upstream’s debugMemSize/debugMemBlocks counters are only maintained by the debug allocator, so with the default installed xmlMemUsed() == 0, xmlMemBlocks() == 0 and xmlMemSize() == 0 (verified against the oracle);
  • the debug-named surface (deprecated, exported for legacy consumers): always libc-backed and tracked by the per-block registry, mirroring upstream xmlMemMalloc et al. xmlMemSize returns the recorded size for these blocks and xmlMemUsed/xmlMemBlocks count them.

The display entry points (xmlMemDisplay, xmlMemDisplayLast, xmlMemShow, xmlMemoryDump) are no-ops matching upstream 2.15.0, which removed that feature.

§Ownership & safety invariants

Every pointer returned by an xml* allocator must be freed with xmlFree (OWNERSHIP_ATLAS section 1). The block registry records ptr -> (size, file, line) for the debug-named surface only, so xmlMemSize is exact for debug-surface blocks and 0 for default-allocator blocks (upstream’s MEMHDR tag lookup behaves identically: a plain malloc block carries no tag). xmlMemSetup custom allocators bypass the registry entirely, matching upstream’s debug-allocator-only contract.

§Historical quirks & epochs

R-000178 (11.1-Z.3): the pre-Z.3 default allocator routed through Rust’s global allocator with fabricated Layouts — default_free deallocated every pointer with a 1-byte layout and default_realloc passed the requested new size as the old allocation layout; both are invalid-layout UB under the Rust allocator contract. Replaced with libc malloc/realloc/free (C allocation semantics; no layout exists), and the default no longer maintains the accounting registry so xmlMemUsed/ xmlMemBlocks/xmlMemSize match the oracle’s 0s; the registry now backs only the debug-named surface. R-000131 (11.1-J) sealed: xmlMemSize returns the recorded size for debug-surface blocks, the *Loc variants accept-and-ignore file/line exactly like upstream 2.15.0’s ATTRIBUTE_UNUSED parameters, and the display functions are upstream-faithful no-ops. R-000133 (11.1-H): the legacy names (xmlMemMalloc/xmlMemFree/xmlMemRealloc/xmlMemoryStrdup) were declared-but-unexported and had to be implemented for the honest-header rule.

§Deliberate oddities

xmlMemSetup/direct variable assignment bypass the accounting registry (deliberate: upstream’s block table exists only in the debug allocator). The five exported variables (xmlMalloc, xmlMallocAtomic, xmlRealloc, xmlFree, xmlMemStrdup) are the single source of truth (R-000176, 11.1-Z.2): xmlMemSetup assigns them and every internal allocation reads them through the *Impl indirection, exactly like upstream internal xmlMalloc(...) calls. The debug-named functions deliberately do NOT route through the variables (upstream’s debug allocator is independent of the hooks); they are always libc-backed + registry-tracked.

§Proving courts

ABI-DATA, ALLOCATOR, ALLOCATOR-DEFAULT, GLOBAL-STATE and THREADING court families; the allocator probes (tools/abi/*_probe.py + courts/suites/data-abi/*) compile the same C probe against the oracle DSO and the candidate and require byte-identical output; ALLOCATOR-DEFAULT-001 proves the default-allocator contract (many sizes, zero-size, grow/shrink realloc, realloc-to-zero, realloc/malloc failure, strdup, direct exported-variable calls, long churn, xmlMemSize/xmlMemUsed/xmlMemBlocks exactness — all byte-identical with the oracle, R-000178); the DSO-LOADER court resolves every exported symbol from the built DSO.

§Tempting simplifications that would break parity

A tempting simplification is to keep the pre-Z.3 default allocator — Rust std::alloc with fabricated layouts is invalid-layout UB (R-000178) and returning nonzero xmlMemUsed/xmlMemBlocks under the default diverges from the oracle’s 0s. Another tempting shortcut is exporting the allocator entry points as plain functions — upstream exports them as data function pointers, so the allocator-override mechanism (xmlMalloc = custom) could not link (R-000162 lesson).

Statics§

xmlFree
xmlFreeFunc xmlFree — the free hook.
xmlMalloc
xmlMallocFunc xmlMalloc — the malloc hook (default: xmlMallocDefault).
xmlMallocAtomic
xmlMallocFunc xmlMallocAtomic — the atomic-malloc hook.
xmlMemStrdup
xmlStrdupFunc xmlMemStrdup — the strdup hook.
xmlRealloc
xmlReallocFunc xmlRealloc — the realloc hook.

Functions§

__xmlFree
Upstream xmlFreeFunc *__xmlFree(void).
__xmlMalloc
Upstream xmlMallocFunc *__xmlMalloc(void).
__xmlMallocAtomic
Upstream xmlMallocFunc *__xmlMallocAtomic(void).
__xmlMemStrdup
Upstream xmlStrdupFunc *__xmlMemStrdup(void).
__xmlRealloc
Upstream xmlReallocFunc *__xmlRealloc(void).
xmlCleanupMemory
Clean up the memory layer.
xmlFreeImpl
Free through the exported xmlFree variable.
xmlGcMemGet
Get GC-aware memory allocator functions (upstream xmlmemory.h).
xmlGcMemSetup
Set GC-aware memory allocator functions (upstream xmlmemory.h).
xmlInitMemory
Initialize the memory layer with debugging support.
xmlMallocAtomicImpl
Allocate through the exported xmlMallocAtomic variable.
xmlMallocAtomicLoc
Allocate memory, recording the allocation site (upstream xmlmemory.h).
xmlMallocAtomicZero
Allocate zero-initialized memory (atomic variant).
xmlMallocImpl
Allocate memory through the exported xmlMalloc variable.
xmlMallocLoc
Allocate memory, recording the allocation site (upstream xmlmemory.h).
xmlMallocZero
Allocate zero-initialized memory.
xmlMemBlocks
Return the current number of allocated blocks (approximate).
xmlMemDisplay
Display memory allocation information to a file.
xmlMemDisplayLast
Display a limited amount of memory debug information (upstream xmlmemory.h).
xmlMemFree
Free memory through the debug allocator (upstream xmlmemory.h).
xmlMemGet
Get the current memory allocator functions (upstream xmlmemory.h).
xmlMemMalloc
Allocate memory through the debug allocator (upstream xmlmemory.h).
xmlMemRealloc
Reallocate memory through the debug allocator (upstream xmlmemory.h).
xmlMemSetup
Set custom memory allocator functions (upstream xmlmemory.h).
xmlMemShow
Show memory allocation information.
xmlMemSize
Return the size of an allocated block (upstream xmlmemory.h).
xmlMemStrdupImpl
Duplicate a C string through the exported xmlMemStrdup variable.
xmlMemStrdupLoc
Duplicate a string, recording the allocation site (upstream xmlmemory.h).
xmlMemUsed
Return the total amount of memory currently allocated (approximate).
xmlMemoryDump
Dump memory allocation statistics (upstream xmlmemory.h).
xmlMemoryStrdup
Duplicate a string through the debug allocator (upstream xmlmemory.h).
xmlReallocImpl
Reallocate through the exported xmlRealloc variable.
xmlReallocLoc
Reallocate memory, recording the allocation site (upstream xmlmemory.h).
xmlReallocZero
Reallocate and zero-initialize the new portion.