Formal Verification · Z3 SMT
arXiv 2604.05292 ↗

Mathematically verify
high-risk vulnerabilities before they ship.

COBALT uses the Z3 SMT solver to prove whether critical bug classes in your C/C++ code are reachable or impossible. Not another risk score. A verifiable SAT/UNSAT verdict with a solver-backed witness your team can inspect — for every supported check.

Every other scanner
"We didn't find it"
Absence of detection — not a guarantee
COBALT UNSAT
"It cannot exist"
Modeled condition: unsatisfiable
Built for teams shipping
Security-critical C/C++ systemsCrypto & authentication librariesRegulated software (medical, aerospace)High-consequence firmware
Supported proof classes →
CWE-190 Integer OverflowCWE-191 Integer UnderflowCWE-195 Sign ConversionCWE-369 Divide-by-ZeroCWE-457 Unbound VariableCWE-125 Heap Over-read
28+
Disclosed Targets
wolfSSL · Mozilla · Hydro-QC · Zephyr · NASA
112+
Z3 Proofs
SAT + UNSAT verdicts issued
0
Unwitnessed SAT
Every positive includes a concrete counterexample
100%
Cross-file Coverage
Full interprocedural call graph

Why teams use COBALT

Static analysis tells you what might be wrong. COBALT tells you whether a critical condition is actually reachable — and for supported proofs, whether it can be ruled out altogether. That matters when code is safety-critical, externally exposed, or too costly to ship on probabilistic confidence.

Prove real bugs, not patterns
Every positive finding includes a SAT witness: a concrete input that triggers the vulnerability. Not a heuristic match — a proven counterexample.
Certify specific classes as impossible
For supported checks, COBALT returns an UNSAT verdict showing the class cannot be triggered under the modeled constraints. An inspectable proof verdict, not a probabilistic score.
Validate your own fixes
Submit a remediation and COBALT re-runs the proof. UNSAT on the fix means the patch is mathematically correct for the modeled property.
What you receive
Affected code path with CWE classification
Solver-backed witness for every SAT finding
UNSAT certificate for supported remediations
Proof package — inspectable, not a black box

Real code.
Real proofs.

Watch COBALT parse the AST, lift arithmetic operations into Z3 bitvector formulas, and return a mathematical verdict — in seconds.

libclang AST parser
Full type system — widths, signedness, implicit promotions
Z3 4.12 SMT engine
Bitvector + real arithmetic + theory combination
Cross-file taint
Data flow tracking across function and module boundaries
cobalt — z3-scanner

How COBALT works

01
AST Parsing
libclang parses C/C++ into a typed AST. Every arithmetic node, cast, and branch is extracted with full type metadata — widths, signedness, implicit promotions.
02
Symbolic Lift
COBALT encodes every operation into Z3 bitvector formulas. Types, widths, signedness, and implicit promotions — all precise.
03
Z3 SMT Solving
The solver searches for counterexamples. SAT = bug is real and triggerable. UNSAT = property holds for every possible input.
04
Proof Certificate
Every finding ships with its Z3 formula, counterexample witness, CWE class, severity, and a fix certified UNSAT.

Verified across sectors that cannot afford probabilistic confidence

28+ disclosed targets across critical infrastructure, cryptography, embedded systems, and regulated software.

For security teams validating critical C/C++ code before release, remediation, or disclosure.

Hall of Proof

Real bugs. Mathematical proofs. Responsible disclosure. Click to expand.

29 entries · all Z3-verified
CRITICALCWE-457HQ-001DISCLOSED
5 Findings — Power Curtailment Disabled + Blackout Detection Crash + InfluxDB Silent Drop
Hydro-Québec OSS · power_limit_mpc.py:146
Three distinct bug classes in Hydro-Québec open-source grid software. (1) CWE-704 locale error disables power curtailment enforcement in power_limit_mpc.py. (2) CWE-670 dead code path causes blackout detection crash on Redis failure in detect_blackout.py. (3) CWE-457 unbound variable causes silent InfluxDB data API crash. Combined: grid constraint enforcement silently disabled under fault conditions.
2026-03-28
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-190ZEP-001DISCLOSED
8 Integer Overflows — CoAP / LwM2M / OSDP / LoRaWAN (ALL 32-bit targets)
Zephyr RTOS · subsys/net/lib/coap/coap.c:419
coap.c and 7 sibling files perform signed integer shifts at the 31-bit boundary on 32-bit targets (ARM Cortex-M, RISC-V, x86). Z3 proves all 8 overflow paths are reachable, producing invalid protocol lengths in CoAP packet parsing, LwM2M object encoding, OSDP message framing, and LoRaWAN payload sizing.
2026-03-31
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-841UQ-001DISCLOSED
Silent Motor Fire — is_busy() False Ready → Command into Moving Motor
UQAT pyvmx (Velmex VXM) · libvxm.py:88
Python motor controller for underground mine scanning equipment. is_busy() silently returns False (ready) on serial timeout instead of raising. Z3 proves a move command fires into a still-moving motor. CWE-20 missing bounds validation on move distance. CWE-116 command string corruption on special characters.
2026-03-29
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-369OH-001DISCLOSED
IOB Silent Zero — Division by Zero → Insulin Overdose Path
OpenAPS oref0 · oref0-calculate-iob.js:47
iobCalcBilinear() accepts dia=0 as valid. Z3 proves insulin-on-board computes silently to 0, masking active insulin. Algorithm subsequently interprets 0 IOB as no active insulin → double-dosing decision path reachable.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-190BC-001DISCLOSED
Integer Overflow — Queue Ticket Counter (32-bit signed rollback)
BC Gov queue-management · api/app/models/theq/citizen.py:112
Ticket counter uses int32 incremented without bounds check. Z3 proves counter wraps to negative value at 2,147,483,647 + 1, producing negative ticket numbers. Affects BC Service Centre queue assignments.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-476BC-005DISCLOSED
Null Dereference — Court Interpreter Assignment Crash
BC Gov court-interpreter-scheduling · api/services/interpreter_service.py:203
Interpreter scheduling logic dereferences a booking object without null guard. On concurrent modification or cancellation, the object is null at the point of access. Z3 proves AttributeError reachable on any empty-result booking query.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-20BC-011DISCLOSED
Missing Input Validation — Business Filing Parser Crash
BC Gov LEAR (Legal Entities) · legal-api/src/legal_api/services/filing/handler.py:318
LEAR filing parser processes XML/JSON payloads without schema validation. Z3 proves attacker-controlled filing data can trigger null dereference in business entity resolution. Affects BC Registries legal filing infrastructure.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-457GC-001DISCLOSED
Unbound Variable — SMS Batch Celery Worker Silent Crash
GC Notify (CDS) · app/celery/tasks.py:345
saved_notifications used after except block that does not raise. NameError on SQLAlchemy error causes Celery worker crash with no retry. SMS batch delivery silently fails — no error surfaced to sender.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
CRITICALCWE-457GC-002DISCLOSED
Unbound Variable — Email Batch Worker Silent Drop (tasks.py:462)
GC Notify (CDS) · app/celery/tasks.py:462
Parallel pattern to GC-001 in the email batch processing worker. saved_notifications referenced after exception handler that swallows the error without raising. Email batches silently drop on database error with no retry or alert.
2026-03-31
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-190WS-MLDISCLOSED
11 Integer Overflows — FIPS 140-3 Post-Quantum Signatures (dilithium.c)
wolfSSL ML-DSA (FIPS 204/205) · wolfcrypt/src/dilithium.c:1
dilithium.c encodes ML-DSA (CRYSTALS-Dilithium) polynomial coefficients using signed left shifts on 32-bit targets. Z3 proves 11 shift overflow paths across encode_w1(), encode_gamma1(), and wc_slhdsa.c produce corrupted signature encodings. FIPS 140-3 certified build affected. Fix PR #10096 drafted by wolfSSL.
2026-03-31
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-195LMB-001DISCLOSED
23 Sign/Unsigned Conversions — Industrial Modbus Protocol (MSP430 / PIC24)
libmodbus · src/modbus.c:1
modbus.c (19 instances) and modbus-tcp.c (4 instances) assign signed int16 register values to unsigned buffer offsets without sign validation. Z3 proves negative register values produce near-maximal unsigned offsets on 16-bit MSP430 and PIC24 targets, causing out-of-bounds reads in industrial Modbus packet parsing.
2026-03-31
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-195NSS-002DISCLOSED
RSA-PSS Salt Validation Bypass — Sign/Unsigned Conversion (secsign.c)
Mozilla NSS · lib/cryptohi/secsign.c:955
cryptohi/secsign.c:955 and :1018 compare a signed salt length against an unsigned modulus size without sign normalization. Z3 proves a negative salt value bypasses the validation check entirely, allowing RSA-PSS signing with an out-of-spec salt length. Submitted Mozilla Bugzilla 2026-03-28.
2026-03-28
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-195WS-001DISCLOSED
Signed-to-Unsigned Conversion — TLS Record Length Truncation
wolfSSL · src/tls13.c:2847
TLS record length field assigned from signed int to uint16_t without bounds validation. Z3 proves negative signed values produce maximal unsigned lengths, bypassing length-constrained buffer reads.
2026-03-29
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-195OVP-001DISCLOSED
Signed-to-Unsigned Conversion — TLS-Auth HMAC Length Bypass
OpenVPN · src/openvpn/tls_crypt.c:162
tls_crypt.c assigns a signed packet length into an unsigned buffer offset without sign validation. Z3 proves a negative length survives the cast, yielding a near-maximal unsigned offset that bypasses the HMAC boundary check and allows TLS-auth bypass.
2026-03-31
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-195OVP-002DISCLOSED
Signed-to-Unsigned Conversion — Token Auth Bypass
OpenVPN · src/openvpn/auth_token.c:165
auth_token.c performs the same pattern: a signed token length is implicitly cast to unsigned before buffer bounds validation. Z3 proves a crafted negative value skips the token length check, enabling token authentication bypass.
2026-03-31
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-190SS-001DISCLOSED
Attacker-Controlled alloca() — IKEv2/TLS Stack Overflow
strongSwan IKEv2 · src/libcharon/sa/ikev2/tasks/tls_server.c:539
tls_server.c calls alloca() with a size derived from a TLS record field — network-attacker controlled. Z3 proves the size can exceed typical stack frame limits, causing stack overflow. Maintainer Tobias Brunner acknowledged finding, fix incoming via heap migration.
2026-04-01
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-125AST-001DISCLOSED
RTP Extension Header Heap Over-read (~248 KB)
Asterisk (Sangoma) · res/res_rtp_asterisk.c:7854
res_rtp_asterisk.c reads RTP extension header extensions_size from the network without bounds clamping. Z3 proves an attacker can send extensions_size = 0xFFFF triggering a heap over-read of ~248 KB beyond the rawdata struct into adjacent heap memory.
2026-04-01
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-190LYB-001DISCLOSED
Integer Overflow → Heap Buffer Overflow — LYB String Parser
libyang (CESNET) · src/parser_lyb.c:280
lyb_read_string() in parser_lyb.c computes string length as a 32-bit integer from two network-supplied 8-bit fields. Z3 proves the product overflows to a small value, allocating an undersized heap buffer, followed by a write of the full (large) string — heap buffer overflow.
2026-04-01
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-476BC-007DISCLOSED
Null Dereference — Language Filter with Empty Interpreter Pool
BC Gov court-interpreter-scheduling · api/services/availability_service.py:178
Language availability filter dereferences the first result of a filtered queryset without checking for empty results. Z3 proves that querying for a rare language with zero available interpreters causes NoneType dereference and HTTP 500 on the public booking endpoint.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-20BC-012DISCLOSED
Missing Validation — Director Address Parser Crash on Partial Data
BC Gov LEAR (Legal Entities) · legal-api/src/legal_api/services/business/address_service.py:94
Business director address resolution skips validation for optional fields when a partial JSON body is submitted. Z3 proves a filing submission omitting the postal_code field triggers KeyError in address normalization, crashing the filing service.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-476HS-001DISCLOSED
Bare Type Assertion Panic — AWS STS Credentials Daemon Crash
Hootsuite vault-ctrl-tool · vaultclient/creds_aws_sts.go:48
creds_aws_sts.go performs bare type assertions on Vault response fields (access_key, secret_key, security_token) with no nil check or ok-pattern. If Vault returns a response missing any field — misconfigured AWS role, revoked lease, API change — the program panics immediately. In sidecar mode, the entire secrets daemon crashes and all credentials stop refreshing silently.
2026-04-02
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-457HS-002DISCLOSED
Security Control Bypass — TOKEN_RENEWABLE=false Silently Overwritten to true
Hootsuite vault-ctrl-tool · vaulttoken/vault_token.go:167
vault_token.go parses TOKEN_RENEWABLE env var correctly via strconv.ParseBool, then unconditionally overwrites the result with true in the else branch. An operator setting TOKEN_RENEWABLE=false gets the exact opposite behavior — token renewal enabled — silently, with no log warning. Security control bypass via copy-paste logic error.
2026-04-02
2 SAT
/ 4 proofs
▼ expand proof trace
HIGHCWE-476HS-003DISCLOSED
Unchecked Error — config-dir Loop Nil Panic on Malformed YAML
Hootsuite vault-ctrl-tool · config/config.go:201
config.go --config-dir loop calls ReadConfig() but never checks the returned error before accessing currentConfig fields. If ReadConfig returns (nil, err) on a malformed YAML file, the immediate field access panics. Inconsistent with the main config path (line ~170) which correctly checks err != nil. In Kubernetes init containers, this crashes the container with no useful error message.
2026-04-02
2 SAT
/ 4 proofs
▼ expand proof trace
MEDIUMCWE-459HS-004DISCLOSED
Incomplete Cleanup — config.wip Leaks Infrastructure Metadata on Error
Hootsuite vault-ctrl-tool · secrets/sts.go:29
sts.go removes wipCredsFilename twice on write error (copy-paste bug) and never removes wipCfgFilename. The leftover config.wip contains AWS region and profile name. On a shared container volume, this file leaks infrastructure metadata across pod restarts.
2026-04-02
2 SAT
/ 4 proofs
▼ expand proof trace
MEDIUMCWE-190FRT-001DISCLOSED
Signed Integer Overflow — ARM Cortex-M3 Tick Counter (Shift UB)
FreeRTOS / Amazon · portable/MSVC-MingW/port.c:607
port.c on the MSVC/MinGW simulator port shifts a signed int by 31 bits to compute the interrupt mask. Z3 proves signed left shift at bit-width boundary is undefined behaviour — the counter wraps to negative on MSVC/MinGW, producing an invalid interrupt mask. Amazon acknowledged (engagement ID on file).
2026-03-31
2 SAT
/ 4 proofs
▼ expand proof trace
MEDIUMCWE-190NS-001DISCLOSED
BER OID Length Wraparound — 32-bit SNMP Trap Parsing
net-snmp · snmplib/asn1.c:1503
asn1.c encodes OID component count as a 32-bit integer when parsing BER-encoded SNMP traps. Z3 proves a crafted OID with 2^32 + 1 components wraps the counter to 1, allocating an undersized buffer while decoding the full OID — heap underwrite on 32-bit platforms.
2026-04-01
2 SAT
/ 4 proofs
▼ expand proof trace
MEDIUMCWE-476PMK-001DISCLOSED
Missing Return — AUTH_FAILED Handler → None.ssh_check_mic() AttributeError
Paramiko · paramiko/auth_handler.py:705
gssapi-keyex auth handler sets AUTH_FAILED but does not return. Execution continues to ssh_check_mic() called on None transport object. Z3 proves AttributeError reachable on any GSSAPI key exchange authentication failure.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace
LOWCWE-191NSS-001DISCLOSED
Integer Underflow — RSA-PSS Parameter (FIXED same-day by Mozilla)
Mozilla NSS · lib/cryptohi/seckey.c:412
sec_CreateRSAPSSParameters computes a buffer offset as the difference of two unsigned values without checking for underflow. Z3 proves a crafted RSA-PSS key with modulus < salt_length produces a negative offset that wraps to maximal unsigned, causing out-of-bounds read. Mozilla patched same-day.
2026-04-01
2 SAT
/ 4 proofs
▼ expand proof trace
LOWCWE-208YUB-001DISCLOSED
Non-Constant-Time Comparison — FIDO2 authenticate_complete()
python-fido2 (Yubico) · fido2/server.py:351
authenticate_complete() uses != (Python bytewise equality, timing-variable) while register_complete() correctly uses constant_time.bytes_eq(). Z3 proves timing oracle exploitable to extract credential bytes via ~128 server queries.
2026-03-30
2 SAT
/ 4 proofs
▼ expand proof trace

Why proof-based AppSec behaves differently

CapabilitySnykSonarQubeGitHub CodeQLCOBALT
Mathematical proof (Z3 SMT)
SAT/UNSAT verdict per finding
Counterexample witness
Fix certified UNSAT
Cross-file integer overflow~
Zero false positives (witness required)
PDF proof certificate
Disclosed CVE-class findings

Get a proof verdict on your critical code.

Submit a repository or codebase. COBALT runs Z3 against it and returns a signed proof certificate — not a risk score, not a list of warnings. A mathematical verdict on every finding.

Most teams hear back within 48 hours.
Or email: dominik@qreativelab.io
Enterprise API · CI/CD Integration · Signed Proof Certificates