Case 146: RTTI Exported for an Internal Type (Single-Release Audit)¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Quality (Compatible) |
| Platforms | Linux |
| Flags | Bad practice |
Detected ChangeKinds |
rtti_for_internal_type |
| Source files | examples/case146_audit_rtti_for_internal/ |
Category: Quality (Audit) | Verdict: ๐ข COMPATIBLE (bad practice)
Verdict and consumer impact¶
Single-release audit: one build's evidence checked against itself, no
baseline. abicheck's verdict is COMPATIBLE, but the audit flags an
advisory finding: _ZTI12InternalNode/_ZTV12InternalNode (the C++
typeinfo and vtable for InternalNode) are both exported from the dynamic
symbol table, but InternalNode is declared only in a private header โ
render() is the sole function the public headers actually declare.
Consumers can't name InternalNode in their own code, so they can't be
handed one directly, but the exported RTTI still lets any code that obtains
an InternalNode* polymorphically (e.g. via a base-class pointer returned
from elsewhere in the library) run dynamic_cast/typeid against it. That
couples consumer binaries to an internal class's identity without the
maintainer ever intending InternalNode to be part of the API.
What this snapshot contains¶
snapshot.abi.json is a single, hand-built AbiSnapshot for one build of
libdemo.so, with the export table and the header provenance of the type
its RTTI belongs to:
| Source in the snapshot | What it records |
|---|---|
Binary export table (L0, elf.symbols) |
_Z6renderv (render), _ZTI12InternalNode, _ZTV12InternalNode โ three exported symbols |
Public-header AST (L2, functions/types) |
only render() is declared in a public header; the InternalNode struct entry carries origin: private_header |
abicheck command¶
Expected abicheck finding¶
Verdict: COMPATIBLE (exit 0)
crosscheck:rtti_for_internal_type present typeinfo exports โ private-header
provenance: 2 RTTI symbol(s) for one of 1 private type(s)
ABI-hygiene catalog (intra-version, advisory)
[warning] rtti_for_internal_type: 2
Minimum evidence¶
min_evidence: L2 โ the export table alone (L0) sees _ZTI/_ZTV symbols
but has no way to know which class they belong to is public vs. private; the
public-header AST (L2) is what supplies the class's provenance, which the
check demangles the RTTI symbol name against.
Why abicheck catches it¶
rtti_for_internal_type is a cross-source check: abicheck demangles
every exported _ZTI*/_ZTV* symbol back to its class name, then looks up
that class's provenance in the public-header AST (binary_exports and
public_header_ast, per provider_assertions). RTTI mapped to a class
whose provenance is private_header is exactly this finding โ a plain
export-table read has no notion of which class is public, and a plain
header parse never sees the compiled RTTI symbols at all.
Why this matters for a real release¶
InternalNode's layout, base classes, and existence are all considered free
to change since nothing declares it publicly โ but as long as its RTTI is
exported, any consumer binary that gets a polymorphic pointer into the
library's object graph can dynamic_cast against InternalNode and observe
identity/layout changes as a crash or silent misbehavior, not a compile
error. Because RTTI symbols are emitted per translation unit that uses the
class polymorphically, this is easy to introduce by accident (any TU that
dynamic_casts or throws the type emits it) and easy to miss in review.
Safe redesign¶
Give InternalNode a key function defined in exactly one, internal
translation unit so its vtable/RTTI aren't emitted (and, ideally,
__attribute__((visibility("hidden"))) on the class) โ or, if InternalNode
is genuinely meant to be part of the public API, move its declaration into
an installed header so the finding reflects an intentional decision instead
of an accident.
Cross-tool comparison¶
rtti_for_internal_type is a cross-source check unique to abicheck's audit
mode โ it reconciles exported RTTI symbols against header provenance within
the same build, which isn't something abidiff/abi-compliance-checker
do (they diff two ABI dumps against each other, not a binary's exports
against its own header provenance).
Source files¶
snapshot.abi.json
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).