Spring 2016 Program Analysis and Verification

Slides:



Advertisements
Similar presentations
Automated Theorem Proving Lecture 1. Program verification is undecidable! Given program P and specification S, does P satisfy S?
Advertisements

Modular and Verified Automatic Program Repair Francesco Logozzo, Thomas Ball RiSE - Microsoft Research Redmond.
Abstract Interpretation Part II
Predicate Abstraction and Canonical Abstraction for Singly - linked Lists Roman Manevich Mooly Sagiv Tel Aviv University Eran Yahav G. Ramalingam IBM T.J.
Shape Analysis by Graph Decomposition R. Manevich M. Sagiv Tel Aviv University G. Ramalingam MSR India J. Berdine B. Cook MSR Cambridge.
Context-Sensitive Interprocedural Points-to Analysis in the Presence of Function Pointers Presentation by Patrick Kaleem Justin.
Pointer Analysis – Part I Mayur Naik Intel Research, Berkeley CS294 Lecture March 17, 2009.
3-Valued Logic Analyzer (TVP) Tal Lev-Ami and Mooly Sagiv.
A survey of techniques for precise program slicing Komondoor V. Raghavan Indian Institute of Science, Bangalore.
1 Lecture 08(a) – Shape Analysis – continued Lecture 08(b) – Typestate Verification Lecture 08(c) – Predicate Abstraction Eran Yahav.
Establishing Local Temporal Heap Safety Properties with Applications to Compile-Time Memory Management Ran Shaham Eran Yahav Elliot Kolodner Mooly Sagiv.
Next Section: Pointer Analysis Outline: –What is pointer analysis –Intraprocedural pointer analysis –Interprocedural pointer analysis (Wilson & Lam) –Unification.
Range Analysis. Intraprocedural Points-to Analysis Want to compute may-points-to information Lattice:
Abstract Interpretation Part I Mooly Sagiv Textbook: Chapter 4.
Intraprocedural Points-to Analysis Flow functions:
1 Program Analysis Systematic Domain Design Mooly Sagiv Tel Aviv University Textbook: Principles.
Comparison Caller precisionCallee precisionCode bloat Inlining context-insensitive interproc Context sensitive interproc Specialization.
Pointer analysis. Pointer Analysis Outline: –What is pointer analysis –Intraprocedural pointer analysis –Interprocedural pointer analysis Andersen and.
1 Tentative Schedule u Today: Theory of abstract interpretation u May 5 Procedures u May 15, Orna Grumberg u May 12 Yom Hatzamaut u May.
Dagstuhl Seminar "Applied Deductive Verification" November Symbolically Computing Most-Precise Abstract Operations for Shape.
Program Analysis and Verification Noam Rinetzky Lecture 10: Shape Analysis 1 Slides credit: Roman Manevich, Mooly Sagiv, Eran Yahav.
Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 2: Operational Semantics I Roman Manevich Ben-Gurion University.
Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 14: Numerical Abstractions Roman Manevich Ben-Gurion University.
Program Analysis and Verification Spring 2014 Program Analysis and Verification Lecture 14: Numerical Abstractions Roman Manevich Ben-Gurion University.
Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 9: Abstract Interpretation I Roman Manevich Ben-Gurion University.
Mark Marron 1, Deepak Kapur 2, Manuel Hermenegildo 1 1 Imdea-Software (Spain) 2 University of New Mexico 1.
Mark Marron IMDEA-Software (Madrid, Spain) 1.
Program Analysis and Verification Spring 2014 Program Analysis and Verification Lecture 4: Axiomatic Semantics I Roman Manevich Ben-Gurion University.
Convergence of Model Checking & Program Analysis Philippe Giabbanelli CMPT 894 – Spring 2008.
Symbolic Execution with Abstract Subsumption Checking Saswat Anand College of Computing, Georgia Institute of Technology Corina Păsăreanu QSS, NASA Ames.
Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 12: Abstract Interpretation IV Roman Manevich Ben-Gurion University.
1 Shape Analysis via 3-Valued Logic Mooly Sagiv Tel Aviv University Shape analysis with applications Chapter 4.6
Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 4: Axiomatic Semantics I Roman Manevich Ben-Gurion University.
Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 13: Abstract Interpretation V Roman Manevich Ben-Gurion University.
1 Combining Abstract Interpreters Mooly Sagiv Tel Aviv University
Program Analysis and Verification Spring 2014 Program Analysis and Verification Lecture 12: Abstract Interpretation IV Roman Manevich Ben-Gurion University.
Quantified Data Automata on Skinny Trees: an Abstract Domain for Lists Pranav Garg 1, P. Madhusudan 1 and Gennaro Parlato 2 1 University of Illinois at.
Roman Manevich Ben-Gurion University Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 16: Shape Analysis.
1 Numeric Abstract Domains Mooly Sagiv Tel Aviv University Adapted from Antoine Mine.
Program Analysis and Verification Spring 2014 Program Analysis and Verification Lecture 8: Static Analysis II Roman Manevich Ben-Gurion University.
Putting Static Analysis to Work for Verification A Case Study Tal Lev-Ami Thomas Reps Mooly Sagiv Reinhard Wilhelm.
Abstraction and Abstract Interpretation. Abstraction (a simplified view) Abstraction is an effective tool in verification Given a transition system, we.
Program Analysis and Verification Spring 2015 Program Analysis and Verification Lecture 8: Static Analysis II Roman Manevich Ben-Gurion University.
Spring 2017 Program Analysis and Verification
Spring 2016 Program Analysis and Verification
Spring 2017 Program Analysis and Verification
Spring 2016 Program Analysis and Verification
Spring 2016 Program Analysis and Verification
Spring 2017 Program Analysis and Verification
Beyond Strong vs. Weak Updates Isil Dillig, Thomas Dillig, Alex Aiken
Program Analysis and Verification
Spring 2016 Program Analysis and Verification
Graph-Based Operational Semantics
Topic 17: Memory Analysis
Fall Compiler Principles Lecture 6: Dataflow & Optimizations 1
G. Ramalingam Microsoft Research, India & K. V. Raghavan
Symbolic Implementation of the Best Transformer
Fall Compiler Principles Lecture 8: Loop Optimizations
Spring 2017 Program Analysis and Verification Operational Semantics
Program Analysis and Verification
Programming Languages and Compilers (CS 421)
Program Analysis and Verification
Program Analysis and Verification
Reduction in End-User Shape Analysis
Fall Compiler Principles Lecture 10: Loop Optimizations
Pointer analysis.
Data Structures & Algorithms
Spring 2016 Program Analysis and Verification Operational Semantics
Programming Languages and Compilers (CS 421)
Spring 2016 Program Analysis and Verification
Presentation transcript:

Spring 2016 Program Analysis and Verification Lecture 15: Shape Analysis I Roman Manevich Ben-Gurion University

Tentative syllabus Program Verification Program Analysis Basics Operational semantics Hoare Logic Applying Hoare Logic Weakest Precondition Calculus Proving Termination Data structures Automated Verification Program Analysis Basics From Hoare Logic to Static Analysis Control Flow Graphs Equation Systems Collecting Semantics Using Soot Abstract Interpretation fundamentals Lattices Fixed-Points Chaotic Iteration Galois Connections Domain constructors Widening/ Narrowing Analysis Techniques Numerical Domains Pointer analysis Shape Analysis Interprocedural Analysis

Previously Points-to analysis and alias analysis

Agenda Going beyond pointer analysis Shape analysis Property-based summarization Materialization Specialized analysis for singly-linked lists (3-valued Shape Analysis Framework / TVLA)

Limitations of pointer analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } // Singly-linked list // data type. class SLL { int data; public SLL n; // next cell SLL(Object data) { this.data = data; this.n = null; } }

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; }

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n L1

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n L1 tmp

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n L1 n tmp L2

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n L1 n n tmp L2

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n L1 n n tmp L2

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp != null tmp L2  h null t n L1 tmp == null tmp

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp L2  h null t n L1 tmp

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp L2 tmp == null ?

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp L2

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp L2 Possible null dereference!

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp L2 Fixed-point for first loop

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp L2

Flow&Field-sensitive Analysis h // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } null t n L1 n n tmp L2 Possible null dereference!

What was the problem? Pointer analysis abstract all objects allocated at same program location into one summary object. However, objects allocated at same memory location may behave very differently E.g., object is first/last one in the list Assign extra predicates to distinguish between objects with different roles Number of objects represented by summary object 1 – does not allow strong updates Distinguish between concrete objects (#=1) and abstract objects (#1) Join operator very coarse – abstracts away important distinctions (tmp=null/tmp!=null) Apply disjunctive completion

Improved solution Pointer analysis abstract all objects allocated at same program location into one summary object. However, objects allocated at same memory location may behave very differently E.g., object is first/last one in the list Add extra instrumentation predicates to distinguish between objects with different roles Number of objects represented by summary object 1 – does not allow strong updates Distinguish between concrete objects (#=1) and abstract objects (#1) Join operator very coarse – abstracts away important distinctions (tmp=null/tmp!=null) Apply disjunctive completion

Adding properties to objects Let’s first drop allocation site information and instead… Define a unary predicate x(v) for each pointer variable x meaning x points to x Predicate holds for at most one node Merge together nodes with same sets of predicates

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n {h, t}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n {h, t} tmp

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n {h, t} tmp n {tmp} No null dereference

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n {h, t} tmp n {tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null t n {t} tmp n {h, tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp {h, tmp}  h null t n {h, t} tmp

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp {h, tmp}  h null t n {h, t} tmp Do we need to analyze this shape graph again?

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp {h, tmp}  h null t n {h, t} tmp

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { h} n { tmp} No null dereference

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { h} n { tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n { h, tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n { h, tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n { h} n { tmp} No null dereference

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n { h} n { tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n { } n {h, tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n n {h, tmp} How many objects does it represent? Summarization

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n n {h} n { tmp} No null dereference

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n n {h} n { tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n n {h, tmp} Fixed-point for first loop

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n n {h, tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { } n n {h, tmp}

Flow&Field-sensitive Analysis // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n tmp { tmp?} n n { tmp}

Improving precision via partial concretization

Best (induced) transformer f#(a)= (f((a))) C A 4 f f# 3 1 2 Problem:  incomputable directly

Best transformer for tmp=tmp.n null h t {t} tmp n { } {h, tmp} null h n {t}  t n { } tmp n n {h, tmp} null h t {t} tmp n { } {h, tmp}

Best transformer for tmp=tmp.n:  null h t {t} tmp n { } {h, tmp} null h n {t}  t n { } tmp n n {h, tmp} null h t {t} tmp n { } {h, tmp} h null n t {t} n { } { } … { } n tmp {h, tmp}

Best transformer for tmp=tmp.n h null null n h t {t} n {t} n t {tmp } n n { } tmp {h} tmp n n {h, tmp} h h null null n n t {t} t {t} n { } n { } { } { } … tmp {tmp } {tmp } tmp n n {h} {h}

Best transformer for tmp=tmp.n:  h null null n h t {t} n {t} n t {tmp } n n { } tmp {h} tmp n n {h, tmp} h h null null n n t {t} t {t} n n { } { } … tmp {tmp } {tmp } tmp n n {h} {h}

Handling updates on summary nodes Transformers accessing only concrete nodes are easy Transformers accessing summary nodes are complicated Can’t concretize summary nodes – represents potentially unbounded number of concrete nodes We need to split into cases by “materializing” concrete nodes from summary node Introduce a new temporary predicate tmp.n Partial concretization

Transformer for tmp=tmp.n: ’ null h n {t} ’ t n { } tmp n n {h, tmp} Case 1: Exactly 1 object. Case 2: >1 objects.

Transformer for tmp=tmp.n: ’ null h t {t} tmp n {tmp.n } {h, tmp} null h n {t} ’ t n { } tmp n n {h, tmp} Summary node h null n t {t} spaghetti n { } tmp.n is a concrete node {tmp.n } n tmp {h, tmp}

Transformer tmp=tmp.n null h t {t} tmp n {t, tmp.n } {h} null h n {t} t n { } tmp n n {h, tmp} h null n t {t} n { } tmp {t, tmp.n } n {h}

Transformer for tmp=tmp.n:  null h t {t} tmp n {h} // Build a list SLL h=null, t = null; L1: h=t= new SLL(-1); SLL tmp = null; while (…) { int data = getData(…); L2: tmp = new SLL(data); tmp.n = h; h = tmp; } // Process elements tmp = h; while (tmp != t) { assert tmp != null; tmp.data += 1; tmp = tmp.n; } h null n t {t} n { } tmp {t} n {h}

Transformer via partial-concretization f#(a)= ’(f#’(’(a))) C A’ A ’ 6 4 f f#’ f# 3 5 1 2 ’

Recap Adding more properties to nodes refines abstraction Can add temporary properties for partial concretization Materialize concrete nodes from summary nodes Allows turning weak updates into strong ones Focus operation in shape-analysis lingo Not-trivial in general and requires more semantic reduction to clean up impossible edges General algorithms available via 3-valued logic and implemented in TVLA system

Analyzing singly-linked lists

Specialized analysis for SLL Very simple shape analysis Assumes SLL only data structure Data fields cannot point to SLL nodes No recursive procedures Simple algorithms, quick implementation Implemented in Abstract Interpretation package

What is it good for? Supports assertions Check cleanness properties assertReach(x,y) assertDisjointLists(x,y) Enables parallelization assertAcyclicList(x) assertCyclicList(x) assert(x==y) assert(x!=y) Check cleanness properties Absence of NullPointerException’s Absence of memory leaks (in C) No misuse of dangling pointers (in C)

Concrete store class SLL { int data; SLL n; } n n n n n n n n n x n n y We ignore the value field and consider only the n-links.

#interruptions ≤ 2 · #variables (bounded number of sharing patterns) Interrupting nodes Interruption: node pointed-to by a variable or shared by n fields n n n n n n n n n x n n n n y #interruptions ≤ 2 · #variables (bounded number of sharing patterns)

List segments Maximal sub-list between two interruptions not containing interruptions in-between n n n n n n n n n x n n n n y

List segments segment 1 segment 2 n n n n n n n n n x n n n n y

Abstraction idea Add following instrumentation predicates to each node x(v) – node v is pointed-to by variable x seg(v) – node v is properly contained in a list segment Merge nodes within the same end points of a segment

Instrumented store seg n n n n n n n n n x n n n n y seg seg

Abstract store seg n n n n n n x n n n y seg n seg

Store it represents seg n n n n n n n n n x n n n n y seg seg

Store it does not represent seg n n n n n n n n n x n n n n y seg seg

Simplified representation Make summary node implicit. Abstract length: how many links in segment {1, >1} >1 1 >1 x >1 y

Intuitive meaning of abstraction Retains overall “shape” of the heap Which important points exist How they are connected via paths Precisely captures disjointness/sharing/cycles Abstracts away Lengths of simple paths Many times not relevant for proving correctness of list-manipulating programs But sometimes is Refinements with numerical abstractions of lengths is needed

Abstract transformers for singly-linked lists

Abstract transformers Need transformers for program statements x = new List() x = null x = y x = y.n x.n = y assume x!=y assume x==y

Simplifying code transformations Code transformations that preserve semantics and simplify implementing transformers by eliminating annoying corner cases Precede all assignment to x by x=null E.g., replace x=new List() with x=null; x=new List() Replace x=x.n with t=x.n; x=t Replace x.n=x with t=x; x.n=t Replace x.n=t with x.n=null; x.n=t

x = new List()# Recall assumption that x==null before statement (simplifying transformation) Allocate new node and have x point to it Just like concrete semantics t y 1 >1 x null x null >1 x = new List()# >1 n t >1 y

x = t# Recall assumption that x==null before statement (simplifying transformation) t points to a concrete node Have x point to that node Just like concrete semantics x null >1 x null >1 x = t# >1 1 >1 1 t t >1 >1 y y

x = null# Case 1: x points to an interruption that remains an interruption even without x pointing to it Just have x point to null x >1 x null >1 x = null# >1 1 >1 1 t t >1 >1 y y

x = null# Case 2: x points to an interruption that ceases to be an interruption Have x point to null x >1 t 1 >1 x null x = null# >1 1 t

x = null# Case 2: x points to an interruption that ceases to be an interruption Step 1: Have x point to null Step 2: Abstract non-maximal list segments x >1 t 1 >1 x null x = null# >1 1 t

x = null# Case 2: x points to an interruption that ceases to be an interruption Step 1: Have x point to null Step 2: Abstract non-maximal list segments x >1 x null >1 x = null# >1 1 >1 t t

x = y.n# Recall assumption that x==null before statement (simplifying transformation) Case 1: y.n is an interruption (edge with abstract length 1) Just have x point to the successor of y x null >1 x >1 x = y.n# >1 1 >1 1 t t 1 1 y y

x = y.n# Recall assumption that x==null before statement (simplifying transformation) Case 2: y.n is not an interruption (edge with abstract length >1) Apply partial concretization and then do case 1 x null >1 x = y.n# >1 1 t >1 y What lengths does it represent?

x = y.n# Recall assumption that x==null before statement (simplifying transformation) Case 2: y.n is not an interruption (edge with abstract length >1) Apply partial concretization and then do case 1 x null >1 x = y.n# >1 1 t >1 y >1 = 2 or >2

x = y.n# Recall assumption that x==null before statement (simplifying transformation) Case 2: y.n is not an interruption (edge with abstract length >1) Apply partial concretization and then do case 1 x >1 1 t null x 1 1 ’ y >1 1 t null >1 y x >1 1 t null 1 >1 y

x = y.n# Recall assumption that x==null before statement (simplifying transformation) Case 2: y.n is not an interruption (edge with abstract length >1) Apply partial concretization and then do case 1 x >1 1 t null x 1 1 x = y.n ’ y >1 1 t null >1 y x >1 1 t null 1 >1 y

x.n = null# Can be used to detect memory leaks (Also x=null) Again two cases: x.n is a concrete node / x.n is a summary node

x.n = null# Can be used to detect memory leaks Again two cases: x.n is a concrete node / x.n is a summary node Case 1: x.n is a concrete node Follow concrete semantics Need to merge non-maximal segment x.n = null# >1 1 t null >1 1 t null 1 x 1 x

x.n = null# Can be used to detect memory leaks Again two cases: x.n is a concrete node / x.n is a summary node Case 1: x.n is a concrete node Follow concrete semantics x.n = null# >1 1 t null >1 t null 1 x 1 x

x.n = null# Can be used to detect memory leaks Again two cases: x.n is a concrete node / x.n is a summary node Case 2: x.n is a summary node Apply partial concretization and do case 1 x.n = null# >1 1 t … null >1 x What can go wrong here in C?

x.n = y# Trivial after x.n=null y t x null 1 >1 y x.n = y#

assume x==y# assume x!=y# How do we check these?

Checking assertions How do we check these? assertReach(x,y) assertDisjointLists(x,y) assertAcyclicList(x) assertCyclicList(x)

see you next time