Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

State model analysis

Tracks resource lifecycle and authentication state through a function. Detects use-after-close, double-close, leaks, and unauthenticated access to privileged operations.

State analysis is on by default. Disable with scanner.enable_state_analysis = false. It runs in --mode full and --mode taint; AST-only mode skips it.

Rule IDs

Rule IDSeverity
state-use-after-closeHigh
state-double-closeMedium
state-resource-leakMedium
state-resource-leak-possibleLow
state-unauthed-accessHigh

What it detects

state-use-after-close: Resource transitions to CLOSED (via close, fclose, disconnect, …), then a use operation happens on it.

FILE *f = fopen("data.txt", "r");
fclose(f);
fread(buf, 1, 100, f);  // state-use-after-close

state-double-close: Resource closed twice. Crashes or undefined behaviour on most runtimes.

state-resource-leak: Resource opened but never closed on any path through the function. Definite leak.

state-resource-leak-possible: Resource closed on some paths but not others. Lower confidence; often an early-return error path.

state-unauthed-access: A function recognised as a web handler reaches a privileged sink without an auth call on the path.

A function counts as a web handler if its name starts with handle_, route_, or api_ (sufficient on its own), or starts with serve_/process_ and the file uses web-shaped parameter names (request, req, ctx, res, response, w, writer, language-dependent). main is excluded.

Managed-resource suppression

Several language-specific cleanup patterns suppress leak findings:

PatternLanguagesEffect
RAII / DropRustAll leak findings suppressed except alloc/dealloc
Smart pointersC++make_unique/make_shared treated as managed; raw new/malloc still tracked
deferGodefer f.Close() suppresses leak at exit
with context managerPythonwith open(f) as f: suppresses leak for the bound name
try-with-resourcesJavaTWR-bound resources suppressed

What it can’t detect

  • Cross-function resource ownership. Open in one function, close in another, leak gets reported in the opener. The most common FP source for leak detection.
  • Factory / builder functions that return a resource for the caller to manage.
  • Variable shadowing across scopes. Same name in inner and outer scope shares one symbol; an inner close masks an outer leak.
  • Resources stored in collections. Handles in arrays / maps / channels and cleaned up via iteration are not tracked.
  • Dynamic dispatch. Close called via trait object or interface may not be recognised.
  • Type-state authentication. AuthenticatedRequest<T> and similar Rust patterns are not recognised as auth.

Common false positives

ScenarioWhyMitigation
Factory returns a resourceCaller owns itKnown limitation
Framework-managed handlesConnection pool, request scopeExclude framework code or downgrade
Variable name shadowingSame name reusedKnown limitation

Per-language detection

LanguageLeakDouble-closeUse-after-closeNotes
Cyesyesyesfopen/fclose, malloc/free, pthread_mutex_*
C++yesyesyesC pairs plus new/delete; smart pointers suppressed
Pythonyesyesyeswith suppressed; open, socket, connect
Goyesyesyesdefer suppressed; os.Open / .Close
Rustunsafe onlyn/an/aRAII suppresses everything except alloc/dealloc
JavaScriptyesyespartialfs.openSync/closeSync
TypeScriptyesyespartialSame as JS
PHPyesyespartialfopen/fclose, curl_init/curl_close, mysqli_*
RubypartialpartialpartialFile.open/close, TCPSocket
JavalimitedlimitedlimitedConstructor-callee matching is incomplete

Tuning

nyx scan . --severity ">=MEDIUM"   # Skip "possible" leaks (Low)
[scanner]
enable_state_analysis = true        # default
excluded_directories  = ["tests", "test", "spec"]

Recognised pairs

The state engine ships these acquire/release pairs. Custom pairs are not yet configurable; file an issue if you need one.

C / C++

AcquireRelease
fopenfclose
openclose
socketclose
malloc, calloc, reallocfree
pthread_mutex_lockpthread_mutex_unlock
new, new[] (C++)delete, delete[]

Rust

AcquireRelease
File::open, File::createdrop, close
TcpStream::connectshutdown
lock, read, write (Mutex/RwLock)drop

Java

AcquireRelease
new FileInputStream (and friends)close
getConnectionclose
new Socketclose

Go, Python, JavaScript, Ruby, PHP follow language-idiomatic equivalents.

Use-after-close triggers

These operations on a closed resource fire state-use-after-close:

read, write, send, recv, fread, fwrite, fgets, fputs, fprintf, fscanf,
fflush, fseek, ftell, rewind, feof, ferror, fgetc, fputc, getc, putc,
ungetc, query, execute, fetch, sendto, recvfrom, ioctl, fcntl,
strcpy, strncpy, strcat, strncat, memcpy, memmove, memset, memcmp,
strcmp, strncmp, strlen, sprintf, snprintf