Abstracting Definitional Interpreters

Similar documents
Abstracting Definitional Interpreters (Functional Pearl)

Computer Science 21b (Spring Term, 2015) Structure and Interpretation of Computer Programs. Lexical addressing

Functional Programming. Pure Functional Programming

Flow Analysis. Data-flow analysis, Control-flow analysis, Abstract interpretation, AAM

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

Parsing Scheme (+ (* 2 3) 1) * 1

Environments

Recap: Functions as first-class values

Control Flow Analysis with SAT Solvers

Denotational Semantics. Domain Theory

An Explicit-Continuation Metacircular Evaluator

Meanings of syntax. Norman Ramsey Geoffrey Mainland. COMP 105 Programming Languages Tufts University. January 26, 2015

Scheme in Scheme: The Metacircular Evaluator Eval and Apply

Logic-Flow Analysis of Higher-Order Programs

CS 6110 S11 Lecture 12 Naming and Scope 21 February 2011

Comp 311: Sample Midterm Examination

CSE 341, Spring 2011, Final Examination 9 June Please do not turn the page until everyone is ready.

Comp 411 Principles of Programming Languages Lecture 7 Meta-interpreters. Corky Cartwright January 26, 2018

Discussion 12 The MCE (solutions)

Accurate Step Counting

CSE 413 Midterm, May 6, 2011 Sample Solution Page 1 of 8

CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures. Dan Grossman Autumn 2018

CSE341, Spring 2013, Final Examination June 13, 2013

Lexical Addresses. let x = 1 y = 2 in let f = proc (x) +(x, y) in (f x) let _ = 1 _ = 2 in let _ = proc (_) +(<0,0>, <1,1>) in (<0,0> <1,0>)

6.184 Lecture 4. Interpretation. Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson

Designing Precise Pushdown Higher-Order Flow Analyses

Syntactic Sugar: Using the Metacircular Evaluator to Implement the Language You Want

Functions & First Class Function Values

Fall Semester, The Metacircular Evaluator. Today we shift perspective from that of a user of computer langugaes to that of a designer of

Higher-Order Functions

Verification of an ML compiler. Lecture 3: Closures, closure conversion and call optimisations

Variables and Bindings

Environments, Stores, and Interpreters

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

CS 275 Name Final Exam Solutions December 16, 2016

Code example: sfact SICP Explicit-control evaluator. Example register machine: instructions. Goal: a tail-recursive evaluator.

1 Introduction. 3 Syntax

Monad Transformers Step by Step

CSE 341 Section 7. Ethan Shea Autumn Adapted from slides by Nicholas Shahan, Dan Grossman, Tam Dang, and Eric Mullen

Verified Characteristic Formulae for CakeML. Armaël Guéneau, Magnus O. Myreen, Ramana Kumar, Michael Norrish April 27, 2017

CPSC 311, 2010W1 Midterm Exam #2

Streams, Delayed Evaluation and a Normal Order Interpreter. CS 550 Programming Languages Jeremy Johnson

Static Analysis of JavaScript. Ben Hardekopf

Mendler-style Recursion Schemes for Mixed-Variant Datatypes

News. CSE 130: Programming Languages. Environments & Closures. Functions are first-class values. Recap: Functions as first-class values

Konzepte von Programmiersprachen

CMSC 330: Organization of Programming Languages

INF 212 ANALYSIS OF PROG. LANGS FUNCTION COMPOSITION. Instructors: Crista Lopes Copyright Instructors.

Building a Modular Static Analysis Framework in Scala (Tool Paper)

Sémantique des Langages de Programmation (SemLP) DM : Region Types

CSE341 Spring 2017, Final Examination June 8, 2017

Agenda. CS301 Session 9. Abstract syntax: top level. Abstract syntax: expressions. The uscheme interpreter. Approaches to solving the homework problem

Overview. A normal-order language. Strictness. Recursion. Infinite data structures. Direct denotational semantics. Transition semantics

6.037 Lecture 7B. Scheme Variants Normal Order Lazy Evaluation Streams

Note that in this definition, n + m denotes the syntactic expression with three symbols n, +, and m, not to the number that is the sum of n and m.

First Class Rules and Generic Traversals for Program Transformation Languages

Compilers: The goal. Safe. e : ASM

Accurate Step Counting

Certified Memory Usage Analysis

Metaprogramming assignment 3

INF 102 CONCEPTS OF PROG. LANGS FUNCTIONAL COMPOSITION. Instructors: James Jones Copyright Instructors.

CS 6110 S11 Lecture 25 Typed λ-calculus 6 April 2011

Turtles All The Way Down

Recap. Recap. If-then-else expressions. If-then-else expressions. If-then-else expressions. If-then-else expressions

CS 360 Programming Languages Interpreters

Binary Code Analysis: Concepts and Perspectives

From Syntactic Sugar to the Syntactic Meth Lab:

Begin at the beginning

A Small Interpreted Language

Freeing memory is a pain. Need to decide on a protocol (who frees what?) Pollutes interfaces Errors hard to track down

6.037 Lecture 4. Interpretation. What is an interpreter? Why do we need an interpreter? Stages of an interpreter. Role of each part of the interpreter

Principles of Programming Languages

Lecture 6. Abstract Interpretation

CS2500 Exam 2 Fall 2011

Towards a Practical, Verified Kernel

Programming Languages

CSE 341 Section 7. Eric Mullen Spring Adapted from slides by Nicholas Shahan, Dan Grossman, and Tam Dang

A New Verified Compiler Backend for CakeML. Yong Kiam Tan* Magnus O. Myreen Ramana Kumar Anthony Fox Scott Owens Michael Norrish

Homework 3 COSE212, Fall 2018

CVO103: Programming Languages. Lecture 5 Design and Implementation of PLs (1) Expressions

Quick announcement. Midterm date is Wednesday Oct 24, 11-12pm.

Implementing Continuations

CSE341 Spring 2017, Final Examination June 8, 2017

Typical workflow. CSE341: Programming Languages. Lecture 17 Implementing Languages Including Closures. Reality more complicated

Running Probabilistic. Programs Backwards

PROGRAM ANALYSIS & SYNTHESIS

Mid-Term 2 Grades

Why do we need an interpreter? SICP Interpretation part 1. Role of each part of the interpreter. 1. Arithmetic calculator.

Lecture 12: Conditional Expressions and Local Binding

Writing code that I'm not smart enough to write. A funny thing happened at Lambda Jam

Static Analysis of Dynamically Typed Languages made Easy

Compositional Soundness Proofs of Abstract Interpreters

Semantics of programming languages

Intro. Scheme Basics. scm> 5 5. scm>

CSE P 501 Compilers. Static Semantics Hal Perkins Winter /22/ Hal Perkins & UW CSE I-1

Side note: Tail Recursion. Begin at the beginning. Side note: Tail Recursion. Base Types. Base Type: int. Base Type: int

Principles of Programming Languages

Tail Recursion: Factorial. Begin at the beginning. How does it execute? Tail recursion. Tail recursive factorial. Tail recursive factorial

CFA2: Pushdown Flow Analysis for Higher-Order Languages

Functional Programming. Pure Functional Languages

Transcription:

Abstracting Definitional Interpreters David Darais University of Maryland Nicholas Labich University of Maryland Phúc C. Nguyễn University of Maryland David Van Horn University of Maryland

Does my program cause a runtime error? Does my program allocate too much? Does my program sanitize all untrusted inputs? Is this proof object computationally relevant?

My PL Doesn t Have a Program Analyzer

Should I Write My Own Program Analyzer?

Writing Your Own Program Analyzer is Easy If you know how to write an interpreter

Abstracting Definitional Interpreters Interpreter => Analyzer Sound Terminating Precise Extensible

Context: Abstracting Abstract Machines (AAM): [ICFP 10] Sound + Terminating + Easy Based on low-level Abstract Machines

Context: Abstracting Abstract Machines (AAM): [ICFP 10] Sound + Terminating + Easy Based on low-level Abstract Machines This Paper: Abstracting Definitional Interpreters (ADI): [ICFP 17] Sound + Terminating + Extra Precision + Even Easier Based on high-level Definitional Interpreters

Inheriting Precision Reynolds - Inheriting properties from defining language [1972] This work - Inherit analysis precision from the metalanguage Result - pushdown analysis Many papers on pushdown precision; we get it for free

Key Challenges Soundness: AAM: A single (parameterized) machine recovers both concrete and abstract semantics ADI: A single (parameterized) interpreter recovers both concrete and abstract semantics

Key Challenges Soundness: AAM: A single (parameterized) machine recovers both concrete and abstract semantics ADI: A single (parameterized) interpreter recovers both concrete and abstract semantics Termination: AAM: Iterating a transition system with finite state space ADI: Caching fixpoint algorithm for unfixed interpreters

Concrete Interpreter Partial Abstract Interpreter Total Abstract Interpreter

Concrete Interpreter 1. Store-allocation style for argument binding 2. Monadic environment and state 3. Parameters for primitive operators and allocation 4. Unfixed style

; m is monad ; m is monad-reader[env] env var addr ; m is monad-state[store] store addr val ; ev : exp m(val) (define (ev e) (match e [(num n) [(vbl x) (return n)] (do ρ ask-env (find (lookup x ρ)))] [(if0 e₁ e₂ e₃) (do v (ev e₁) z? (zero? v) (ev (if z? e₂ e₃)))] [(op2 o e₁ e₂) (do v₁ (ev e₁) v₂ (ev e₂) (δ o v₁ v₂)) [(lam x e) [(app e₁ e₂) (do ρ ask-env (return (cons (lam x e) ρ))) (do (cons (lam x e ) ρ ) (ev e₁) v₂ (ev e₂) a (alloc x) (ext a v₂) (local-env (update x a ρ ) (ev e )))]))

; m is monad ; m is monad-reader[env] env var addr ; m is monad-state[store] store addr val ; ev : exp m(val) (define (ev e) (match e [(num n) [(vbl x) (return n)] (do ρ ask-env (find (lookup x ρ)))] [(if0 e₁ e₂ e₃) (do v (ev e₁) z? (zero? v) (ev (if z? e₂ e₃)))] [(op2 o e₁ e₂) (do v₁ (ev e₁) v₂ (ev e₂) (δ o v₁ v₂)) [(lam x e) [(app e₁ e₂) (do ρ ask-env (return (cons (lam x e) ρ))) (do (cons (lam x e ) ρ ) (ev e₁) v₂ (ev e₂) a (alloc x) (ext a v₂) (local-env (update x a ρ ) (ev e )))]))

; m is monad ; m is monad-reader[env] env var addr ; m is monad-state[store] store addr val ; ev : exp m(val) (define (ev e) (match e [(num n) [(vbl x) (return n)] (do ρ ask-env (find (lookup x ρ)))] [(if0 e₁ e₂ e₃) (do v (ev e₁) z? (zero? v) (ev (if z? e₂ e₃)))] [(op2 o e₁ e₂) (do v₁ (ev e₁) v₂ (ev e₂) (δ o v₁ v₂)) [(lam x e) [(app e₁ e₂) (do ρ ask-env (return (cons (lam x e) ρ))) (do (cons (lam x e ) ρ ) (ev e₁) v₂ (ev e₂) a (alloc x) (ext a v₂) (local-env (update x a ρ ) (ev e )))]))

; m is monad ; m is monad-reader[env] env var addr ; m is monad-state[store] store addr val ; ev : exp m(val) (define (ev e) (match e [(num n) [(vbl x) (return n)] (do ρ ask-env (find (lookup x ρ)))] [(if0 e₁ e₂ e₃) (do v (ev e₁) z? (zero? v) (ev (if z? e₂ e₃)))] [(op2 o e₁ e₂) (do v₁ (ev e₁) v₂ (ev e₂) (δ o v₁ v₂)) [(lam x e) [(app e₁ e₂) (do ρ ask-env (return (cons (lam x e) ρ))) (do (cons (lam x e ) ρ ) (ev e₁) v₂ (ev e₂) a (alloc x) (ext a v₂) (local-env (update x a ρ ) (ev e )))]))

; m is monad ; m is monad-reader[env] env var addr ; m is monad-state[store] store addr val ; ev : (exp m(val)) exp m(val) (define ((ev ev ) e) (match e [(num n) [(vbl x) (return n)] (do ρ ask-env (find (lookup x ρ)))] [(if0 e₁ e₂ e₃) (do v (ev e₁) z? (zero? v) (ev (if z? e₂ e₃)))] [(op2 o e₁ e₂) (do v₁ (ev e₁) v₂ (ev e₂) (δ o v₁ v₂)) [(lam x e) [(app e₁ e₂) (do ρ ask-env (return (cons (lam x e) ρ))) (do (cons (lam x e ) ρ ) (ev e₁) v₂ (ev e₂) a (alloc x) (ext a v₂) (local-env (update x a ρ ) (ev e )))]))

Running The Interpreter ; Y : ((a m(b)) a m(b)) a m(b) (define ((Y f) x) ((f (Y f)) x)) ; eval : exp val store (use-monad (ReaderT env (StateT store ID))) (define (eval e) (mrun ((Y ev) e))) > ((λ (x) (λ (y) x)) 4) '(((λ (y) x). ((x. 0))). ((0. 4)))

Running The Interpreter ; Y : ((a m(b)) a m(b)) a m(b) (define ((Y f) x) ((f (Y f)) x)) ; eval : exp val store (use-monad (ReaderT env (StateT store ID))) (define (eval e) (mrun ((Y ev) e))) > ((λ (x) (λ (y) x)) 4) '(((λ (y) x). ((x. 0))). ((0. 4)))

Interpreter Extensions Intercept recursive calls in the interpreter Change monad parameters Change primitive operators and allocation

E.G., A Tracing Analysis ; m is monad ; m is monad-reader[env] ; m is monad-state[store] ; m is monad-writer[config] ; ev-trace : ((exp m(val)) exp m(val)) (exp m(val)) exp m(val) (define (((ev-trace ev) ev ) e) (do ρ ask-env σ get-store (tell (list e ρ σ)) ((ev ev ) e)))

Running the Analysis ; eval : exp (val store) list(config) (use-monad (ReaderT env (WriterT list (StateT store ID)))) (define (eval e) (mrun ((Y (ev-trace ev)) e))) > (* (+ 3 4) 9) '((63. ()) ((* (+ 3 4) 9) () ()) ((+ 3 4) () ()) (3 () ()) (4 () ()) (9 () ()))

Running the Analysis ; eval : exp (val store) list(config) (use-monad (ReaderT env (WriterT list (StateT store ID)))) (define (eval e) (mrun ((Y (ev-trace ev)) e))) > (* (+ 3 4) 9) '((63. ()) ((* (+ 3 4) 9) () ()) ((+ 3 4) () ()) (3 () ()) (4 () ()) (9 () ()))

Concrete Interpreter Partial Abstract Interpreter Total Abstract Interpreter

Partial Abstract Interpreter 1. Abstracting Primitive Operations 2. Abstracting Allocation The Game: "Abstract" = finite

Abstracting Numbers ; m is monad-failure ; m is monad-nondeterminism ; num Z {'N} ; δ : op num num m(num) (define (δ o n₁ n₂) (match o ['+ (return 'N)] ['/ (do z? (zero? n₂) (if z? fail (return 'N)))])) ; zero? : num m(bool) (define (zero? v) (match v ['N (mplus (return #t) (return #f))] [_ (return (= v 0))]))

Abstracting Numbers ; m is monad-failure ; m is monad-nondeterminism ; num Z {'N} ; δ : op num num m(num) (define (δ o n₁ n₂) (match o ['+ (return 'N)] ['/ (do z? (zero? n₂) (if z? fail (return 'N)))])) ; zero? : num m(bool) (define (zero? v) (match v ['N (mplus (return #t) (return #f))] [_ (return (= v 0))]))

Abstracting Addresses ; alloc : var m(addr) (define (alloc x) (return x)) ; ext : addr val m(unit) (define (ext a v) (do σ get-store (put-store (union σ (dict a (set v)))))

Abstracting Addresses ; alloc : var m(addr) (define (alloc x) (return x)) ; ext : addr val m(unit) (define (ext a v) (do σ get-store (put-store (union σ (dict a (set v)))))

Running the Analysis ; eval : exp (option(val) store) (use-monad (ReaderT env (FailT (StateT store (NondetT ID)))) (define (eval e) (mrun ((Y ev) e))) > (let ((f (λ (x) x))) (f 1) (f 2)) '(set 1 2) > (letrec ((loop (λ (x) (loop x)))) (loop 1)) TIMEOUT

Running the Analysis ; eval : exp (option(val) store) (use-monad (ReaderT env (FailT (StateT store (NondetT ID)))) (define (eval e) (mrun ((Y ev) e))) > (let ((f (λ (x) x))) (f 1) (f 2)) '(set 1 2) > (letrec ((loop (λ (x) (loop x)))) (loop 1)) TIMEOUT

Running the Analysis ; eval : exp (option(val) store) (use-monad (ReaderT env (FailT (StateT store (NondetT ID)))) (define (eval e) (mrun ((Y ev) e))) > (let ((f (λ (x) x))) (f 1) (f 2)) '(set 1 2) > (letrec ((loop (λ (x) (loop x)))) (loop 1)) TIMEOUT

Concrete Interpreter Partial Abstract Interpreter Total Abstract Interpreter

(loop 1) (loop 1)

Total Abstract Interpreters 1. Remember visited configurations

(loop 1) I ve already seen that config (loop 1)

(loop 1) I ve already seen that config (loop 1)

Total Abstract Interpreters 1. Remember visited configurations (Sufficient for termination) (Unsound for abstraction)

(fact 'N) (if (zero? 'N) 1 (* 'N (fact (- 'N 1))))

(fact 'N) (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1)))

(fact 'N) (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1))) (* 'N (fact 'N)) 'N (fact 'N)

(fact 'N) (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1))) (* 'N (fact 'N)) 'N (fact 'N) I ve already seen that config

(fact 'N) = {1} (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1))) (* 'N (fact 'N)) 'N (fact 'N) I ve already seen that config

(fact 'N) = {1} (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1))) (* 'N (fact 'N)) 'N (fact 'N) I ve already seen that config

Total Abstract Interpreters 1. Remember visited configurations 2. Bottom out to a cached result

(fact 'N) (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1))) (* 'N (fact 'N)) 'N (fact 'N)

(fact 'N) (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1))) (* 'N (fact 'N)) 'N $[(fact 'N)]

(fact 'N) = {1} {'N $[(fact 'N)]} (if (zero? 'N) 1 (* 'N (fact (- 'N 1)))) 1 (* 'N (fact (- 'N 1))) (* 'N (fact 'N)) 'N $[(fact 'N)]

(fact 'N) = {1} {'N $[(fact 'N)]} Q: How to compute $[(fact 'N)]?

(fact 'N) = {1} {'N $[(fact 'N)]} Q: How to compute $[(fact 'N)]? $[(fact 'N)] (fact 'N)

(fact 'N) = {1} {'N $[(fact 'N)]} Q: How to compute $[(fact 'N)]? $[(fact 'N)] (fact 'N) A: Compute least-fixpoint of equations

(define (eval e) (mrun ((fix-cache (Y (ev-cache ev))) e))) > (letrec ((loop (λ (x) (loop x)))) (loop 1)) (set) > (letrec ((fact (λ (x) > (if0 x > 1 > (* x (fact (- x 1))))))) > (fact 6)) to call the cache (set 'N) Intercepts recursion

(define (eval e) (mrun ((fix-cache (Y (ev-cache ev))) e))) > (letrec ((loop (λ (x) (loop x)))) (loop 1)) (set) > (letrec ((fact (λ (x) > (if0 x > Computes 1 the > (* x (fact (- x 1))))))) > (fact least-fixpoint 6)) (set 'N)

(define (eval e) (mrun ((fix-cache (Y (ev-cache ev))) e))) > (letrec ((loop (λ (x) (loop x)))) (loop 1)) (set) > (letrec ((fact (λ (x) > (if0 x > 1 > (* x (fact (- x 1))))))) > (fact 6)) (set 'N)

(define (eval e) (mrun ((fix-cache (Y (ev-cache ev))) e))) > (letrec ((loop (λ (x) (loop x)))) (loop 1)) (set) > (letrec ((fact (λ (x) > (if0 x > 1 > (* x (fact (- x 1))))))) > (fact 6)) (set 'N)

Total Abstract Interpreters 1. Remember visited configurations 2. Bottom out to a cached result 3. Compute least-fixpoint of the cache (See full caching algorithm in the paper)

Extra Precision We ve actually recovered pushdown 0CFA There is no approximation for stack frames Call/return semantics is implemented by the metalanguage (Racket) Precise call/return semantics = pushdown precision

What Else is in the Paper? Pushdown analysis Global store-widening A more precise arithmetic abstraction (Sound) Symbolic execution Abstract garbage collection Proof of soundness via big-step reachability semantics (supp. material)

Go and Write Your Own Program Analyzer It s just a slightly fancy interpreter

Abstracting Definitional Interpreters Interpreter => Analyzer Sound Terminating Precise Extensible