Static code analysis for complex systems
Visao reads your source code and reports defects before the program runs. It finds values that overflow, indexes past the end of an array and memory used after release.
Each finding names the rule, the line and the path that leads to it. Visao is the static analysis part of Midair.
$visao compile_commands.json Using toolchain clang (clang) Parsing 1 / 3... Solving … src/rx.c:19:5: error: The index is out of range src/rx.c:26:12: error: Possible integer overflow src/rx.c:33:19: error: Dangling pointer ✓ Finished successfully · 0.4 s
What is static code analysis?Checking code without running it.
Static code analysis checks source code without running the program. It looks at every path the code can take and reports the places where a value, a pointer or a resource can go wrong.
Testing answers a different question: what happens on the runs you actually make. A test sees one path at a time. A static analyzer reasons about many paths together, including the rare ones a test never reaches.
So the two work together. Static analysis finds defects early and without hardware. Tests confirm how the software behaves in its real environment.
We compare the two methods in detail, with examples: Static and dynamic code analysis: the complete comparison.
Read a finding.Three flags in one C file.
The file and the findings below are an example. Choose a flag in the margin to see the finding and the path that leads to it.
/* rx.c: receive path of a serial driver */#include <stdint.h>#include <stdlib.h>#define RX_SIZE 64struct rx_ring {uint8_t data[RX_SIZE];uint16_t head;};void log_bytes(const uint8_t *p, uint16_t n);void rx_put(struct rx_ring *r, uint8_t byte){if (r->head > RX_SIZE) {r->head = 0;}r->data[r->head] = byte;r->head++;}int32_t rx_timeout_us(const uint8_t *cfg){uint16_t ms = (uint16_t)(cfg[0] | (cfg[1] << 8));return ms * 1000 * 1000;}void rx_drop(uint8_t *buf, uint16_t len, int keep_log){free(buf);if (keep_log) {log_bytes(buf, len);}}
src/rx.c:19:5High
The index is out of range
- Rule
array/index/out_of_range- MISRA C:2012
- Rule 18.1 (Required)
- Seen by
- Interval domain
The path to it
- 9head is a 16-bit counter, and data holds 64 bytes, so the last valid index is 63.
- 16The reset runs only when head is above 64. When head is exactly 64, the condition is false and the code goes on.
- 19On that path the store writes data[64], one byte past the end of the array.
src/rx.c:26:12High
Possible integer overflow
- Rule
arithm/integer/overflow- MISRA C:2012
- Rule 12.4 (Advisory)
- Seen by
- Interval domain
The path to it
- 25ms is built from two configuration bytes, so it can hold any value from 0 to 65535.
- 26ms is promoted to int and multiplied by one million. The largest result is 65 535 000 000.
- 26A 32-bit int holds at most 2 147 483 647. Any ms above 2147 overflows.
src/rx.c:33:19Critical
Dangling pointer
- Rule
mem/ptr/dangling- MISRA C:2012
- Rule 1.3 (Required)
- Seen by
- Pointer and resource-state domains
The path to it
- 31free releases the block that buf points to. buf still holds the old address.
- 32Nothing in the function fixes the value of keep_log, so both branches stay possible.
- 33When keep_log is set, log_bytes receives buf and reads memory that was already released.
How Visao sees a problem.Four ways to reason about one program.
Visao turns each source file into one internal representation and runs several analyses over it. Choose one to see what it tracks.
Interval
Visao tracks the lowest and highest value each variable can hold on every path. When a range goes past what the type or the array allows, Visao reports it.
Examplems in [0, 65535]
ms * 1000 * 1000 in [0, 65 535 000 000]
int32_t max 2 147 483 647
the range does not fit: overflow
Octagon
Visao also tracks relations between two variables, such as i < n. The range of i alone is often too wide to decide. The relation shows whether an access stays inside the array.
Examplefor (i = 0; i < n; i++) sum += in[i];
interval i in [0, 65535] cannot decide
octagon i - n <= -1 in[i] stays inside in[n]
Pointer
Visao follows what each pointer can point to and the state of each resource: allocated, released, open, closed. A read through a released pointer, a second release or a handle that stays open is a finding.
Exampleline 29 buf -> block A live
line 31 free(buf) A released
line 33 log_bytes(buf, len) A read after release
SMT
When the analyses above answer Unknown, Visao can ask the Z3 or CVC5 solver whether all conditions on a path can hold at once. If they cannot, the path does not exist and Visao reports nothing there.
Examplepath to the access: x > 10 and 2 * x < 15
solver (Z3): unsat
the path cannot happen: no finding
Where your code states its assumptions as ACSL-style contracts, Visao checks those too.
One finding, four formats.For people, for CI and for scripts.
The same finding from rx.c, as each report format writes it. The examples are shortened.
The default console output. Each finding shows file, line and column, the message and the source line.
$ visao src/rx.csrc/rx.c:19:5: error: The index is out of range
r->data[r->head] = byte;
^
✓ Finished successfully · 0.4 sFor GitHub Code Scanning, Jenkins Warnings NG and Azure DevOps. A fingerprint on each result keeps the finding stable when lines move.
$ visao --report-format sarif --report-output visao.sarif ...{
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"version": "2.1.0",
"runs": [{
"tool": { "driver": { "name": "Visao", "rules": [ ... ] } },
"results": [{
"ruleId": "array/index/out_of_range",
"level": "error",
"message": { "text": "The index is out of range" },
"locations": [{ "physicalLocation": {
"artifactLocation": { "uri": "src/rx.c" },
"region": { "startLine": 19, "startColumn": 5 } } }],
"partialFingerprints": { "visao/v1": "..." }
}]
}]
}A compact findings array for jq and your own scripts.
$ visao --report-format json src/rx.c{
"tool": "visao",
"findings": [{
"ruleId": "array/index/out_of_range",
"helpUri": "https://defectdb.org/array/index/out_of_range",
"severity": "High",
"level": "error",
"message": "The index is out of range",
"file": "src/rx.c",
"line": 19,
"column": 5
}]
}The GitLab SAST report schema. GitLab shows it in the security widget of the merge request.
$ visao --report-format gitlab --report-output gl-sast-report.json ...{
"version": "15.0.6",
"scan": { "type": "sast", "scanner": { "id": "visao", "name": "Visao" } },
"vulnerabilities": [{
"id": "array/index/out_of_range:src/rx.c:19",
"category": "sast",
"name": "array/index/out_of_range",
"description": "The index is out of range",
"severity": "High",
"location": { "file": "src/rx.c", "start_line": 19 },
"identifiers": [
{ "type": "visao_rule", "value": "array/index/out_of_range" },
{ "type": "cwe", "value": "119" }
]
}]
}Gate on new findings only
Save the findings of the main branch as a SARIF baseline. On a merge request, --baseline drops every finding already in it. Then --fail-on high exits with code 1 only when a new finding is High or above.
Further reading.Guides, research and related pages.
Questions.Short answers.
What is static code analysis?
Static code analysis checks source code without running the program. It reasons about every path the code can take. It reports defects such as overflows, indexes past the end of an array and memory used after release.
How is it different from testing?
A test runs the program and sees the paths that run. Static analysis sees many paths at once, including rare ones, but it does not watch the program in its real environment. Most teams use both.
Which languages does Visao analyze?
Nineteen languages, including C, C++, Rust, Go, Python, Java and assembly. For C and C++ it reads your compile_commands.json, so it uses the same flags and include paths as your build.
Does Visao check MISRA C?
Yes. With --standard misra-c-2012, each matching finding carries its MISRA C:2012 rule number. The run ends with a summary of the rules checked and not checked.
How does it fit into CI?
Visao is one command. It writes SARIF 2.1.0, JSON or a GitLab SAST report, and with --fail-on the exit code fails the step. A baseline limits the gate to new findings.
Does our code leave our machines?
No. Visao runs on your machine, sends nothing anywhere and uses no third-party service.
Start with one repository.See what Visao finds in your code.
Compare the plans on the pricing page. Prices are monthly, in euros.
Join the waitlist