Typing Local Control and

nostalgicisolatedSoftware and s/w Development

Nov 4, 2013 (3 years and 5 months ago)

38 views

Typing Local Control and
State Using Flow Analysis

Arjun Guha, Claudiu Saftoiu, and
Shriram Krishnamurthi

1

why add types?

2


JavaScript [Anderson ’05, Heidegger ‘09
]


LISP [Reynolds ’68, Cartwright ’75]


Ruby [
Furr

’09
]


Scheme
[Tobin
-
Hochstadt

‘06
]


Smalltalk [Suzuki ’80
]


Thorn [Bloom ’09]


etc.

why we want types for JavaScript

3

verifying security
-
critical
code

documentation

Caja

FBJS

untyped

code in the wild

4




function
fromOrigin
(p) {


if (
typeof

p === "object") {


return
Math.sqrt
(
p.x

*
p.x

+
p.y

*
p.y
);


} else {


return Math.abs(p);


}

}

5

Object with numeric

x

and
y

fields

Number

Union of the two

T =
Num
|
B
ool

|
String |
Undef


| T


T | Any


| T






T
-
>
T


| {field: T, …}

6

Explicit

type

annotations

(primarily on

functions)




function
fromOrigin
(p) {


if (
typeof

p === "object
"
) {


return
Math.sqrt
(
p.x

*
p.x

+
p.y

*
p.y
);


} else {


return Math.abs(p);


}

}

7

p:

{x: Num, y: Num}

p:
Num


p: {x:
N
um, y: Num}




乵N


Control Operators


if
,
?:


while
,
for
,
do
,
for in


switch
,
continue
,
break
,
label:


&&
,
||
,
throw
,
try catch finally

8


if (
val

=== null)


{ return "null"; }




var

fields = [ ];


for (
var

p in
val
) {


var

v = serialize(
val
[p]);


if (
typeof

v === "string") {


fields.push
(p + ": " + v);


}


}


return "{ " +


fields.join
(", ") +


" }";

}

function serialize(
val
) {


switch (
typeof

val
) {


case "undefined":


case "function":


return false;


case "
boolean
":


return
val

? "true" :


"false";


case "number":


return "" +
val
;


case "string":


return
val
;


}

9

Any


String


Bool

where is
case
"
object"
?

function serialize(
val
) {


switch (
typeof

val
) {


case "undefined":


case "function":


return false;


case "
boolean
":


return
val

? "true" :


"false";


case "number":


return "" +
val
;


case "string":


return
val
;


}


if (
val

=== null)


{ return "null"; }




var

fields = [ ];


for (
var

p in
val
) {


var

v = serialize(
val
[p]);


if (
typeof

v === "string") {


fields.push
(p + ": " + v);


}


}


return "{ " +


fields.join
(", ") +


" }";

}

10

Any


却物湧S


䉯潬

implicit

case "object"

var

slice = function (
arr
, start, stop) {



var

result = [];



for
(
var

i = 0
;
i
<= stop
-

start; i++)
{


result[i
] =
arr
[start + i];



}


return
result;

}



slice([5, 7, 11, 13], 0, 2)


[5, 7, 11]


slice([5, 7, 11, 13], 2)

11

a
rity

mismatch
error?

var

slice = function (
arr
, start, stop) {



if (
typeof

stop === "undefined") {


stop =
arr.length



1;


}



var

result = [];


for (
var

i = 0; i <= stop
-

start; i++) {


result[i] =
arr
[start + i];


}


return result;

}



slice([5, 7, 11, 13], 0, 2)


[5, 7, 11]


slice([5, 7, 11, 13], 2)



[11, 13]

12

stop:
Undef

stop:
Num

stop:
Num



啮摥U

獴潰㨠
乵N

獴慲琺s
乵N

慲a
㨠䅲牡礼愾

Checks For

JS
Gadgets

ADsafe

Python
stdlib

Ruby
stdlib

Django

Rails

LOC

617,766

2,000

313,938

190,002

91,999

294,807

undefined/null

3,298

0

1,686

538

868

712

instanceof

17

45

613

1,730

647

764

typeof

474

40

381

N/A

4

N/A

field
-
presence

unknown

unknown

504

171

348

719

Total Checks

3,789

95

3,184

2,439

1,867

2,195

13

pervasive runtime checks in
various scripting languages



Moral

“Scripting language”
programmers

use state and

non
-
trivial control flow

to refine types


14

A T
YPE

S
YSTEM

FOR

J
AVA
S
CRIPT

15

16

Soundness

[
Preservation
]

If
e

: T and
e



e’
,


e’

: T

[
Progress
]

If
e

: T,


e

is a value, or




e’

.
e



e’

17

This

is

true…

but

not very useful
!




function
fromOrigin
(p) {


/* {x: Num, y: Num}


乵洠
-
㸠乵>




if (
typeof

p === "object
"
) {


return
Math.sqrt
(
p.x

*
p.x

+
p.y

*
p.y
);


} else {


return Math.abs(p);


}

}

18

This is
not

typeable
!

19

function
fromOrigin
(p) {


/* {x: Num, y: Num}


乵洠
-
㸠乵>




楦

瑹灥潦

瀠㴽㴠≯扪散p
"
⤠)

††
癡v

pt = cast p …;


return
Math.sqrt
(
pt.x

*
pt.x

+
pt.y

*
pt.y
);


} else {


var

pf

= cast p …;


return Math.abs(
pf
);


}

}

Casting

Casting is an operation between
types


But JavaScript (and Scheme, Python, …)

have only
tags


Tag = "number
"

| "string" | …



| {field: T, …}

"object
"




| T




-
> T

"
function"





20

Should really be

called
tagof


21

function
fromOrigin
(p) {


/* {x: Num, y: Num}


乵洠
-
㸠乵>




楦

瑹灥潦

瀠㴽㴠≯扪散p
"
⤠)

††
癡v

pt = cast p …;


return
Math.sqrt
(
pt.x

*
pt.x

+
pt.y

*
pt.y
);


} else {


var

pf

= cast p …;


return Math.abs(
pf
);


}

}

function
fromOrigin
(p) {


/* {x: Num, y: Num}


乵洠
-
㸠乵>




楦

瑹灥潦

瀠㴽㴠≯扪散p
"
⤠)

††
癡v

灴‽p
瑡杣桥捫
⡳整⠢潢橥捴
"
⤬⁰⤻

††
牥瑵牮r
䵡瑨⹳煲M
(
灴⹸


灴⹸


灴⹹


灴⹹



素敬獥⁻


var

pf

=
tagcheck
(set("number"), p);


return Math.abs(
pf
);


}

}

22

Introducing
tagcheck

tagcheck

R

e




Reduce
e

to value
v

Let
t

be the
typeof

(i.e., the tag of)
v

If
t

is in
R
, return
v
; else error (
tagerr
)

23

Set of
tags

An

expression

24

runtime
: Type


2
Tag






static
:
2
Tag


Type

static
:
2
Tag


Type


Type

25

static
:
2
Tag


Type


Type




static
(set("string
"
,
"
bool
"
),



Str



乵洠


䉯潬
)


却S



䉯潬

static
(set("object"),


{x: Num, y: Num}


乵洩N


筸㨠{洬⁹㨠m浽

Given a set
of tags…

…and a
type…

…pick out the parts

of the type that
correspond to the tags

26

Determine

static type,

i
dentify
presumed
run
-
time
tags,

…t
hat’s the
resulting
static type

narrow type
based on
these two…

27

Num


却S

set("string
"
)

Str

static
(set("string"),


Num



却S
)

function
fromOrigin
(p) {


/* {x: Num, y: Num}


乵洠
-
㸠乵>




楦

瑹灥潦

瀠㴽㴠≯扪散琢⤠p

††
癡v

灴‽p
瑡杣桥捫
⡳整⠢潢橥捴
"
⤬⁰⤻

††
牥瑵牮r
䵡瑨⹳煲M
(
灴⹸


灴⹸


灴⹹


灴⹹



素敬獥⁻


var

pf

=
tagcheck
(set("number"), p);


return Math.abs(
pf
);


}

}

28

tagcheck

Failure Modes

29

tag set
R

is
inconsistent with
the type
S

resulting type
T

is

inconsistent with

the context’s needs

actual run
-
time tag
is simply not

contained in
R

Soundness

[
Preservation
]

If
e

: T and
e



e’
,


e’

: T

[
Progress
]

If
e

: T,


e

is a value, or




e’

.
e



e’
, or


e

= E[
tagerr

]

30

31

1.
How to prevent programs from resulting in
the new run
-
time error (
tagerr
)?

2.
Who’s going to write
tagcheck
s
?

F
LOW

A
NALYSIS

1.
Value sets are about
tags
, not
values

2.
Only inside procedures; halts at calls

3.
Intermediate representation (CPS, ANF,
SSA, …)

32

function
fromOrigin
(p) {


/* {x: Num, y: Num }


乵洠
-

乵N




var

t1 =
typeof

p;


var

t2 = (t1 === "object");


if (t2) {


var

t3 =
p.x
;
var

t4 =
p.x
;



var

t5 = t3 * t4;


var

t6 =
p.y
;
var

t7 =
p.y
;



var

t8 = t6 * t7;


var

t9 = t5 + t8;


return
Math.sqrt
(t9);


} else {


return Math.abs(p);


}

}

33

p = {"object"}

t1 =
typeof

p

t2 = true

p = {"number"}

t1 =
typeof

p

t2 = false

p = runtime(
{ x:
Num
, y:
Num

}



乵N
)

瀠㴠笢潢橥捴∬•湵浢敲≽

p = {"object", "number"}

t1 =
typeof

p

t2 = t1 === "object"

function
fromOrigin
(p) {


/* {x: Num, y: Num }


乵洠
-

乵N




var

t1 =
typeof

p;


var

t2 = (t1 === "object");


if (t2) {




var

t3 =
p.x
;
var

t4 =
p.x
;



var

t5 = t3 * t4;


var

t6 =
p.y
;
var

t7 =
p.y
;



var

t8 = t6 * t7;


var

t9 = t5 + t8;


return
Math.sqrt
(t9);


} else {




return Math.abs(p);


}

}

34

p = {"object"}

t1 =
typeof

p

t2 = true

p = {"number"}

t1 =
typeof

p

t2 = false

35

p = {"object"}

t1 =
typeof

p

t2 = true

p = {"number"}

t1 =
typeof

p

t2 = false

function
fromOrigin
(p) {


/* {x: Num, y: Num }


乵洠
-

乵N




var

t1 =
typeof

p;


var

t2 = (t1 === "object");


if (t2) {


var

pt

=
tagcheck
(set("object"),
p
);


var

t3 =
pt.x
;
var

t4 =
pt.x
;



var

t5 = t3 * t4;


var

t6 =
pt.y
;
var

t7 =
pt.y
;



var

t8 = t6 * t7;


var

t9 = t5 + t8;


return
Math.sqrt
(t9);


} else {


var

pf

=
tagcheck
(set
("number"),
p);


return
Math.abs
(
pf
);


}

}

function
fromOrigin
(p) {



/* { x:
Num
, y:
Num

}



Num

-
>
Num

*/



...

}


fromOrigin
(500)

fromOrigin
({ x : 20, y: 900 })


fromOrigin
("invalid argument")

36

actual arguments ignored by
flows

therefore, invalid arguments
ignored too!

p = runtime(
{ x:
Num
, y:
Num

}



乵N
)



㴠笢潢橥捴∬•湵浢敲≽

[
Type Pres.
]

If
e

: T and
e



e’
,


e’

: T

[
Type
Prog
.
]

If
e

: T,


e

is a value, or




e’

.
e



e’
, or


e

= E[
tagerr
]

37

[
Flow Soundness
]

If
e

: ok and
e



e’
,


e’

: ok, or


e is a
β
v
redex

(
func
(x) : T


… { return e })(v)


tagof
(v)

runtime(T)

Combined Soundness for
Types and Flows

Our Recipe

Simple type checker (not quite enough)

Add
tagcheck

(breaks progress)

Simple flow analysis (w/ preservation broken)


“Types on the outside, flows on the inside”

The composition is sound

The performance is great (seconds on
netbook
)

38

39

JavaScript Semantics (ECOOP 2010)

Typing Local Control and State (ESOP 2011)

Typing Objects (in progress)

Verifying Web Sandboxes

Types as Documentation
for JavaScript programs