STIG Help

External symbol categories

Some functions used by a program have no implementation body in the LLVM module being analyzed. This is normal for dynamically linked libraries: the executable calls functions supplied by a shared library, such as glibc. LLVM intrinsics also appear without ordinary function bodies; LLVM defines their operations, and the compiler lowers them to machine instructions or runtime calls.

Analysis can handle these cases through models of the functions' behavior:

  • Existing PhASAR support. PhASAR has built-in analysis summaries for operations such as llvm.memcpy.*, llvm.memmove.*, and llvm.memset.*. These describe how the operations copy or write memory, so analysis can follow their effects without a C/C++ implementation body.

  • Models derived from library contracts. A standard, specification, or man page can describe the behavior needed to write a STIG bitcode rewrite or C/C++ replacement. The original library LLVM is not needed when that contract specifies the relevant behavior.

For example, these two functions provided by glibc have published C/POSIX contracts:

Function

Documented behavior used to derive a model

strcmp

Compares two NUL-terminated strings as unsigned bytes and returns a negative value, zero, or a positive value according to their ordering. The model reads both strings through the first difference or terminating NUL.

strlen

Returns the number of bytes before the first NUL. The model reads the string until that terminator and returns the length, excluding the NUL.

These contracts provide the basis for reviewing the replacements even when the captured LLVM contains only declarations of strcmp and strlen. The categories below record how STIG recognizes external symbols.

The category types, recognition lists, and name classifier are defined in STIG at:

stig-engine/src/main/kotlin/com/stigway/gradle/support/StigSymbolClassification.kt

The source-analysis skill bundles a version-matched copy under references/source/, using the same repository path. Model configuration is documented in Bitcode rewrite modeling.

What a missing definition means

In the default CHECK_ALL_UNDEFINED_SYMBOLS mode, STIG uses LLVM to enumerate symbols that the inspected module references but does not define. An entry in that check's allowed_external_symbols.json therefore has no definition in that module. This says nothing by itself about its definition in another activity, the initial LLVM module, P3, or the native executable. The name-only classifySymbol() helper does not inspect definitions itself.

State in the inspected LLVM module

Consequence

Definition is present

It is not a missing-definition finding. Classification does not delete the body.

Declaration is referenced and no definition is present

Classify and account for the external symbol; check the behavior needed by the analysis.

Function declaration has no uses

LLVM use-def checking excludes the unused prototype. It needs no model for this module merely because its name appears.

Required run-plan symbol is absent even as a declaration

Execution/integrity blocker; an ignore pattern does not provide the missing endpoint.

LLVM intrinsic, such as llvm.memcpy.*

Handled separately from ordinary runtime symbols; llvm.* names are excluded from this external-symbol enumeration.

Some C/POSIX or C++ library code can be included while other library calls remain external. Headers can contribute inline definitions; captured library compilation units can contribute bodies. A dynamically linked library can remain outside the LLVM module, but so can a prebuilt static library or a compilation unit excluded from the captured inputs. A missing body is not proof of dynamic linking. Use compilation/link provenance when its cause matters; do not infer it from a standard-looking function name.

The presence of the library on the analysis machine does not provide its behavior to PhASAR. The analyzer needs the selected LLVM implementation or an applicable analysis summary; it does not execute the host library to infer dependencies. A compiler-generated call to memcpy is also different from an LLVM llvm.memcpy.* intrinsic. Model support must cover the call form actually present.

Categories

The descriptions below reproduce the SymbolCategory KDoc. Exact membership and matching order are maintained in StigSymbolClassification.kt.

Category

Description

Membership source

CONFIGURED_IGNORE

Undefined symbol exempted by analyzerInput.symbolVerification.ignorePatterns, including STIG defaults. No behavior model is supplied by the exemption.

Effective analyzerInput.symbolVerification.ignorePatterns.

PHASAR_MODELED

Undefined symbol expected to have PhASAR support. Actual behavior depends on the active analysis model and LLVM call form.

Hard-coded PHASAR_MODELED_SYMBOLS, including memory operations and allocation functions.

CXX_ABI_RUNTIME

Undefined C++ ABI runtime symbol. Consult the applicable ABI and runtime implementation for its behavior; this category supplies no model.

Hard-coded CXX_ABI_PREFIXES and CXX_ABI_MANGLED_PREFIXES.

POSIX_RUNTIME

Undefined listed C/POSIX or runtime-extension symbol, permitted as opaque. Behavior comes from the applicable library contract; classification supplies no model.

Hard-coded POSIX_SYMBOLS, including GNU extensions as well as C/POSIX functions.

CXX_STDLIB

Undefined C++ library symbol. Consult the C++ standard or implementation-specific library contract; this category supplies no model.

Demangled names starting with std:: or containing for std::.

PHASAR_MODELED_SYMBOLS is not derived from active analyzer capabilities. The name classifier recognizes expected support without verifying the particular analysis implementation. ABI and standard-library name matches likewise do not establish absence of data, memory, exception, or termination effects.

Configured ignores and classification order

Ignore patterns default to __cxa_*, _Unwind_*, __gxx_*, and __cxx_*. They are configurable through AnalyzerInputSymbolVerificationDsl in AnalyzerInputDsl.kt, with defaults/group/element resolution in ResolvedAnalyzerInputConfig.kt. Plain patterns use * and ?; regular expressions use the regex: prefix. Matching considers demangled and mangled names. In NativeSymbolVerification.kt, a matching ignore for an UNDEFINED_IN_BITCODE entry takes precedence over the other categories. Thus CONFIGURED_IGNORE can originate from STIG defaults, without a project-authored exemption.

StigDeriveAnalysisConfigurationTask also uses the built-in classifySymbol() helper when determining which external references require application implementations. Its direct classification and the verifier's configurable ignore override are distinct call paths. Report the producing task and inspected module when comparing their output.

Library contracts and analysis models

Authoritative starting points are the POSIX function specifications, Linux man-pages, GNU C Library manual, GNU libstdc++ manual, LLVM libc++ documentation, and Itanium C++ ABI. LLVM intrinsics have contracts in the LLVM language reference. Record the exact function/section and applicable version, ABI, preconditions, and effects. A specification describes the required behavior; it does not establish that a particular analyzer implements that behavior.

An activity transform-rule use(...) entry selects a project registry rule definition. Category membership does not supply that definition: use("pthread_self") requires a corresponding project registry rule. The category lists do not fetch or install C/C++ implementations.

An available original/native definition, a project-authored C/C++ implementation, a Kotlin rewrite, or an applicable PhASAR summary can supply the behavior needed by an analysis. Review that concrete treatment against the contract and relevant callers. Keep the shared rule's semantic confirmation separate from activity P5 application evidence, as described in model-review guidance.

Project wrappers such as amp_isnanf and amp_isinff require their implementations; they are not POSIX exemptions. Use implementation(..., nativeCompileUnit = ..., mode = ADD) to retain the original body with its recorded native compiler command. External compilation units are resolved against the compiler inputs, including Bazel execution roots, rather than assuming that the logical path exists inside the source checkout.

For example, permitting fetestexcept as POSIX_RUNTIME leaves the call in LLVM; it does not model CPU exception flags or their influence on the result. Likewise, a documented pthread_self contract establishes calling-thread identity, but category membership does not implement that identity in the analysis.

Completeness and newly encountered functions

Do not add every POSIX function to the allow-list. Extend recognition for a demonstrated runtime symbol after checking its provenance and contract, and separately establish whether its relevant effects are modeled. An overly broad allow-list can conceal missing behavior just as an incomplete list can misreport a valid runtime dependency.

For a newly encountered function:

  1. Identify the exact LLVM symbol, activity, task/checkpoint, and whether a definition or referenced declaration is present. Keep unused declarations and functions absent from P5 input separate from required P5 definitions.

  2. Identify the category's source, including the matched ignore pattern when applicable. Check the exact library/ABI contract rather than inferring behavior from the category or namespace. A referenced unclassified symbol remains an unresolved application-symbol finding until its origin and treatment are established.

  3. Check available original/native definitions and existing project registry rules. For an allowed external, locate the active analyzer summary or state which effects remain opaque. Use only models actually supplied by the installed version or configured in the project.

  4. Account for relevant input bytes, output writes, return values, aliases, external state, callbacks, and termination. Present a concrete model when one is needed. Standard-library membership alone does not justify ignoring those effects or confirming a transform.

  5. Record missing bodies, available models, semantic review, activity P5 application, and remaining limitations separately. An unused declaration requires no repair for this module. An opaque external's lack of reported flow is not evidence that the original function cannot propagate a fault.

The present all-undefined-symbol check accounts for referenced unresolved symbols in one module. It does not prove that manually maintained category lists are exhaustive, or that their members have correct analyzer models. CHECK_REQUIRED_ANALYZER_SYMBOLS has a narrower scope and does not provide this module-wide accounting.

Classification tests establish name matching and reporting behavior; they do not establish function-specific memory/state propagation. Keep artifact counts by category and unresolved count separate from model-coverage and review totals.

Ask Codex with the existing source-analysis skill:

Use $stig:source-analysis to review the referenced undefined symbols for <activity> in <evidence>. Consult external-symbols-and-library-models.md. Explain each classification and its source, distinguish absent definitions from analyzer-model coverage, and propose models for relevant unresolved behavior. Keep unused declarations separate and summarize the reviewed symbols and unresolved totals.

Use Check and align analysis evidence for evidence identity and review scope, and the P5 symbol-completeness gate for execution consequences.

23 October 2026