490 likes | 600 Views
Dependent Types for JavaScript. Ravi Chugh Ranjit Jhala. University of California, San Diego. David Herman. Mozilla Research. Dependent Types for JavaScript. A Large Subset of. Goal: Precise and Flexible Reasoning for Fine-Grained Security. But hard even for simple type invariants!.
E N D
DependentTypes for JavaScript Ravi ChughRanjitJhala University of California, San Diego David Herman Mozilla Research
DependentTypes for JavaScript A Large Subset of Goal:Precise and Flexible Reasoningfor Fine-Grained Security But hard even for simple type invariants!
Outline • Challenges • Tour of DJS • Security Predicates
Challenges: Unions and Mutation varreadLinks= function (doc, max) { if (!max) max = 10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) // read at most 5 links … readLinks(document) // … or 10 by default integer or undefined…
Challenges: Unions and Mutation varreadLinks= function (doc, max) { if (!max) max = 10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) integer or undefined…
Challenges: Unions and Mutation varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) integer or undefined… … but now definitely an integer
Challenge: Objects varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) prototype inheritance, mutability, dynamic keys
Challenge: Arrays varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } readLinks(document,5) readLinks(document) • “length”, “holes”, • non-integer keys, prototypes
DependentJavaScript[OOPSLA ’12] • System D[POPL ’12] “Usability” refinement types F≤ ∨, ∧ + nested refinements DJS occurrence types … Coq dependent types syntactic types JS Expressivity
Outline • Challenges • Tour of DJS • Security Predicates
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Refinement Types {x|p} Bool Num Int Top {b|tag(b)=“boolean”} {n|tag(n)=“number”} {i|tag(i)=“number”integer(i)} {x|true} “value x such that formula p is true”
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Refinement Types {x|p} {x|p} 3 :: 3 :: 3 :: 3 :: {i|i>0} Int Num {i|i=3} “value x such that formula p is true” “value x such that formula p is true”
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Subtyping is Implication* {i|i=3}<:{i|i>0}<:Int <:Num i=3 i>0 tag(i)=“number”integer(i) tag(i)=“number”
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Nested Refinements • McCarthy’s decidable theory of arrays {d|Bool(sel(d,“f”))∧ sel(d,k)::IntInt} • uninterpreted System D “has-type” predicatenests typing relation inside formulas
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Nested Refinements SyntacticSubtyping + Subtyping is Implication* UninterpretedValidty Implication* =
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Path and Flow Sensitivity varreadLinks= function (doc, max) { if (!max) { max =10 } else{ } … /*: readLinks :: (Ref(~doc), Int?)Top */ max0:Int? max1:{v|v=10} (max0≠undefined) max2:{v|v=max0} max12:Int T?{x|T(x)x=undefined}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Path and Flow Sensitivity varreadLinks= function (doc, max) { if (!max) { max =10 } else{ } … /*: readLinks :: (Ref(~doc), Int?)Top */ max0:Int? max12:Int T?{x|T(x)x=undefined}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Path and Flow Sensitivity x1:{v|v=upd(x0,k,7)} x0:Empty varx = { } x[k]=7 Strong updates to singleton objects Weak updates to collections of objects
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Track types, “packedness,” and lengthof arrays where possible {a|a::Arr(A) {a|packed(a) {a|len(a)=10} • … • … • … • A? • -1 • 0 • A? • A? • 1 • 2 • A? • A? • len(a) • A? A?{x|A(x)x=undefined} X{x|x=undefined} • … • … • … • X • A • A • A • A • X
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes • x:T1/H1T2/H2 • (Quick Detour) • Function types includelocal heap pre- and post-conditionsà la separation logic input type output heap input heap output type
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes extern getIdx :: All A. (a:Ref, i:Int) / (a0:Arr(A)) {a1| (a1 :: A ∨ a1 = undefined)∧ 1(packed(a0) ite (0 <= i < len(a0)) 11 (a1 :: A)) 11(a1 = undefined)} / same input type / heap ite p q1 q2 (pq1) ∧ (pq2) (a1:{v|v=a0}) output type / heap
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes extern setIdx :: All A. (a:Ref, i:Int, y:A) / (a0:Arr(A)) Top / (a1:{a1:: Arr(A) ∧ 1(packed(a0) ∧0 <= i < len(a0) packed(a1) ∧len(a1) = len(a0)) ∧ 1 (packed(a0) ∧i = len(a0) packed(a1) ∧len(a1) = len(a0) + 1)})
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes extern __ArrayProto :: {v|sel(v,“pop”)::… ∧ sel(v,“push”)::… ∧ …}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } maxinv:Int iinv:{v|v>=0} eltsinv:{a|a=elts0} Elt.protoinv:{d|…} Heap invariants before and after each iteration Type checker infers heap for common cases
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes DJS handles prototypes…
13 benchmarksmainly from SunSpider and JSGP • 300 unannotated LOC • 35% annotation overhead • 11 benchmarks run in <1s • 2 benchmarks run in 2-6s DJSProgram DesugarerBased on Guha et al.[ECOOP ’10] Z3 SMTSolver Type Checker Desugared Program
Outline • Challenges • Tour of DJS • Security Predicates
Browser Extension Security • Can we track fine-grained policies directly in JS? “Degree of JavaScript” ? ADsafetyPolitz et al.[SEC ’11] DJS JS IBEX • Guha et al.[SP ’11] ML Granularityof Invariants fine coarse
/*: readLinks :: (Ref(~doc), Int?)Top */ varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } • Allow extension to read this attribute?
/*: assume forall (e d) (eltTagName e “a” ∧ eltInDoc e d ∧ docDomain d “newyorker.com”) canReadAttr e “href”*/ • IBEX-style security policy /*: readLinks :: (Ref(~doc), Int?)Top */ varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } • Type check against IBEX-style DOM API
extern Elt.prototype.getAttr :: (this:Ref(~elt), k:Str) Str
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k}) {s|Str(s)∧attrOfElt this k s}
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k) {s|Str(s)∧attrOfElt this k s} extern Doc.prototype.domain :: (this:Ref(~doc)) Str
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k) {s|Str(s)∧attrOfElt this k s} extern Doc.prototype.domain :: (this:Ref(~doc)) {s|Str(s)∧docDomain this s}
extern Elt.prototype.getAttr :: (this:Ref(~elt),{k|Str(k)∧canReadAttr this k) {s|Str(s)∧attrOfElt this k s} extern Doc.prototype.domain :: (this:Ref(~doc)) {s|Str(s)∧docDomain this s} extern Doc.prototype.getEltsByTagName :: …
/*: assume forall (e d) (eltTagName e “a” ∧ eltInDoc e d ∧ docDomain d “newyorker.com”) canReadAttr e “href”*/ • IBEX-style security policy /*: readLinks :: (Ref(~doc), Int?)Top */ varreadLinks= function (doc, max) { if (!max) max =10 if (doc.domain()== “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) for (var i = 0; i < elts.length && i <= max; i++) { elts[i].getAttr(“href”) } } } ✓ • Type check against IBEX-style DOM API
Current Status 9 of 17 IBEX examples ported to DJS Total running time ~3s Invariants translate directly (so far)
Conclusion DJS able to tracksimple type invariants, and security predicatesseem within reach
Thanks! D ravichugh.com/djs :: • github.com/ravichugh/djs
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes vargrandpa= …, parent = Object.create(grandpa), child = Object.create(parent), b= kin child, • Key Membership via Prototype Chain Unrolling b :: {v|v=trueiff (has(child,k) (has(parent,k) (has(grandpa,k) (HeapHas(H,great,k))} child parent great H (Rest of Heap) grandpa
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes vargrandpa= …, parent = Object.create(grandpa), child = Object.create(parent), b= kin child, x= child[k] • Key Lookup viaPrototype Chain Unrolling x :: {v|ifhas(child,k) then v=sel(child,k) {v|elifhas(parent,k) then v=sel(parent,k) {v|elifhas(grandpa,k) then v=sel(grandpa,k) {v|elifHeapHas(H,great,k)) then v=HeapSel(H,great,k)) {v|elsev=undefined}
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Key Idea Reduce prototype semantics to decidable theory of arrays via flow-sensitivity and unrolling
Refinements • Path and Flow Sensitivity • Arrays • Loops • Prototypes Encode tuplesas arrays vartup= [5, “guten abend!”] {a|a::Arr(Top) {a|packed(a)len(a)=2 {a|Int(a[0]) {a|Str(a[1])}
varreadLinks= function (doc, max) { if (!max) max = 10 if (doc.domain() == “newyorker.com”) { var elts = doc.getEltsByTagName(“a”) var i = 0 var loop = function loop () { if (i < elts.length && i <= max) { elts[i].getAttr(“href”) i++ loop() } else { undefined } } loop() } } Desugared Loop max:-Int i :-{v|v>=0} elts:-{a|a=elts0} Elt.proto:-{d|…}
{p}{v|p} /*: x:NumOrBool {iteNum(x) Num(v) Bool(v)} */ function negate(x) { x=(typeofx==“number”)? 0 – x : !x returnx } /*: x:Any{vifffalsy(x)} */ function negate(x) { x=(typeofx==“number”)? 0 – x : !x returnx }
Function Types and Objects • x:T1/H1T2/H2 input type output heap input heap output type ObjHas(d,k,H,d’)has(d,k)HeapHas(H,d’,k) /*: x:Ref / [x |-> d:Dict |> ^x] {viffObjHas(d,”f”,curHeap,^x)} / sameHeap */ function hasF(x) { return“f”in x }
Function Types and Objects • x:T1/H1T2/H2 input type output heap input heap output type ObjSel(d,k,H,d’) itehas(d,k) sel(d,k) HeapSel(H,d’,k) /*: x:Ref / [x |-> d:Dict |> ^x] {v=ObjSel(d,”f”,curHeap,^x)} / sameHeap */ function readF(x) { returnx.f }