cs161 Lecture 26: Static Analysis (Metal) Static Analyis verifies some program property at "compile" time. Type checking is the most familiar In many langauges, type checking is easy We'd like to know more interesting things Do I have a memory (or resource) leak? Do I have a race condition? Do I have a buffer overflow? (or other security problem) Do I have infinite loops? Generally: using an API incorrectly Everything "interesting" is undecidable Key insight: real programs are often simpler than theory might imply Sound / Unsound Sound == "correct". Verification flags a problem, and problems causes flags. Type checking, usually. Programming langauge research has mostly focused on sound checks With unsound, you generally hav eto worry about false postive - correct programs, flagged as errors false negatives - undetected errors Annotations often required to make an analysis sound maybe they're a good thing anyway? (documentation) The usual game - dataflow analysis Intraprocedural (straight-forward, less likely to find "the good stuff") Interprocedural (usually hard, limits assumptions) Examining all paths takes exponential time Try to limit work by summarizing, dynamic programming, etc. Whole-program (easier to be complete, less practical) Still takes too much time, but now you know all callers In the real-world, access to source for the "whole-program" is hard Some terminology basic blocks control flow graph Metal - Bring static analysis to the developer, not the tool writer. Developers write simple checkers, using state machines to model behavior One global state machine One state machine per tracked variable Metal runs through "all paths" Some transitions spit out errors. Draw CFG for contrived() Intraprocedural analysis Simple DFS Cache to avoid exponential blowup Extensions must be deterministic Loops compute a maximal fixed-point At each entry to a block, known transitions are removed Block is aborted if that leaves no tuples Interprocedural Similar cache to reduce work But here, metal skips, it doesn't backtrack (different callsites) Refine / restore - deal with scoping State is copied from one variable to another File-scope is handled specially Function summaries track more state changes Dealing with unsoundness Metal revels in its unsoundness False negatives Better to analyse more things, imperfectly, than few things perfectly Practically, suppressing poor warnings seems reasonable False postives Supression on definition (p = 0, i = 1 means stop tracking p, a[i]) Synonyms (p = q) False path pruning (try to track data values, for branching) Targeted suppression - extensions can pattern match weird idioms History - remember FPs across runs (how do they label an FP?) Rank by ease of diagnosis (motivation may be kernel developer cooperation) Distance Conditionals - maybe it was a false path Indirection - synonyms Local/interproc Group by common analysis SECURITY - handles user data ERROR - error paths Statistical ranking - determine the probability that a rule is right Is this checker usually corrcet? Is this block of code usually analysed correctly? Here, "correct" really means "consistent"