Safeguard
Security Guides•

Is a static call graph exact, or just an estimate?

A static call graph is an estimate that leans toward over-reporting. Learn what that means for reachability results in plain words, with a comparison.

Safeguard Security Team
3 min read

A static call graph is an estimate, not an exact map. Computing the exact one is undecidable, so tools approximate, and the usual approximation includes every real call plus some that never happen. For reachability, that means a "reachable" label can be wrong more often than a "not reachable" one.

What a call graph is

A call graph is a set of nodes and edges. Each node is a function, and each edge says one function can call another. Reachability analysis asks whether a path of edges runs from something your application executes to the vulnerable function inside a dependency.

Call graphs come in two broad kinds. A dynamic call graph records one real run, for example from a profiler. It can be exact, but only for that run. A static call graph tries to cover every possible run, which is why it is approximate. Both points come from the Wikipedia call graph entry.

Why the estimate leans toward extra edges

Take a method call on an interface in an object-oriented language. The tool may not know which concrete class will be used, so it may add an edge to every class that could fit. Some of those edges will never run. This is called over-approximation, and it is a deliberate choice: it is safer to include a call that never happens than to miss one that does.

For a security question, that tilt matters. Over-approximation protects you from false "not reachable" claims but produces more "reachable" findings than reality would. The graph can also miss edges when code uses reflection or dynamic loading, which are patterns where behaviour is decided at runtime.

Precision costs time

The same source describes a spectrum. A context-insensitive graph has one node per function and is cheap. A fully context-sensitive graph has a node for every call stack that can reach a function, which is much more precise and usually too slow for large programs. Tools choose a point in between.

ApproachPrecisionCostTypical risk
Context-insensitiveLowerLowExtra reachable findings
Partly context-sensitiveMediumMediumSome extra findings
Fully context-sensitiveHighVery highRarely practical at scale
Dynamic (one run)Exact for that runNeeds executionMisses paths not exercised

What to do with this

Read "reachable" as "a path was found that the tool believes is possible", and read "not reachable" as "no path under the tool's assumptions". If a decision carries cost, such as a risk acceptance, ask for the call path and read it. A path you can follow by eye is stronger than a bare label.

Safeguard includes reachability analysis alongside SBOM and dependency scanning, and you can run a free scan at gold.safeguard.sh/scan to see the output on your own repository.

Quick check

Can a static call graph ever be exact?

Not in general. The exact version is an undecidable problem, so tools approximate. Small, simple programs may happen to come out exact, but you cannot count on it.

Is a dynamic call graph better?

It is exact for the run it recorded, but it only shows paths that run exercised. Many teams use static analysis for coverage and runtime data as a cross-check.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.