Slide 1

Slide 1 text

Static Code Analysis Using SSA

Slide 2

Slide 2 text

‘go’ toolchain is great!

Slide 3

Slide 3 text

go build go test go fmt go get go vet go tool cover go tool pprof go -race

Slide 4

Slide 4 text

What’s the next step?

Slide 5

Slide 5 text

Code generation

Slide 6

Slide 6 text

go/ast go/doc go/parser go/printer go/scanner go/token

Slide 7

Slide 7 text

Over 1,000 packages import go/ast

Slide 8

Slide 8 text

The AST only shows the structure of code

Slide 9

Slide 9 text

Static Single Assignment Intermediate Representation

Slide 10

Slide 10 text

SSA IR

Slide 11

Slide 11 text

SSA IR shows how code flows

Slide 12

Slide 12 text

golang.org/x/tools/go/ssa

Slide 13

Slide 13 text

golang.org/x/tools/go/ssa Primary Author: Alan Donovan

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Only 5 projects import golang.org/x/tools/go/ssa

Slide 16

Slide 16 text

github.com/DanielMorsing/stackcheck github.com/go-llvm/llgo github.com/motemen/go-typeswitch-gen github.com/stripe/safesql github.com/tardisgo/tardisgo

Slide 17

Slide 17 text

Intermediate Representation Go IR Machine Code

Slide 18

Slide 18 text

IR is high level

Slide 19

Slide 19 text

IR is portable

Slide 20

Slide 20 text

x := 1 y := 2 x = x + y Static Single Assignment

Slide 21

Slide 21 text

x := 1 y := 2 x = x + y Static Single Assignment

Slide 22

Slide 22 text

x1 := 1 y1 := 2 x2 = x1 + y1 Static Single Assignment

Slide 23

Slide 23 text

SSA makes analysis easier

Slide 24

Slide 24 text

Terminology

Slide 25

Slide 25 text

Program github.com/benbjohnson/foo github.com/benbjohnson/bar fmt os io

Slide 26

Slide 26 text

github.com/benbjohnson/foo type Bar type Baz func Bat() Package

Slide 27

Slide 27 text

func hello(name string) { if name == “” { fmt.Print(“name?”) name = readString() } fmt.Printf(“hello %s”, name) } Function

Slide 28

Slide 28 text

func hello(name string) { if name == “” { fmt.Print(“name?”) name = readString() } fmt.Printf(“hello %s”, name) } Basic Blocks

Slide 29

Slide 29 text

fmt.Print(“name?”) name = readString() Control Flow Graph (CFG) fmt.Printf(“hello %s”, name) false true if name == “”

Slide 30

Slide 30 text

func hello(name string) { if name == “” { fmt.Print(“name?”) name = readString() } fmt.Printf(“hello %s”, name) } Instructions

Slide 31

Slide 31 text

func hello(name string) { if name == “” { fmt.Print(“name?”) name = readString() } fmt.Printf(“hello %s”, name) } Values

Slide 32

Slide 32 text

SSA IR Primitives Alloc BinOp Builtin Call ChangeInterface ChangeType Const Convert DebugRef Defer Extract Field FieldAddr FreeVar Function Global Go If Index IndexAddr Jump Lookup MakeChan MakeClosure MakeInterface MakeMap MakeSlice MapUpdate NamedConst Next Panic Parameter Phi Range Return RunDefers Select Send Slice Store Type TypeAssert UnOp

Slide 33

Slide 33 text

What does it look like?

Slide 34

Slide 34 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }

Slide 35

Slide 35 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }

Slide 36

Slide 36 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }

Slide 37

Slide 37 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) } true

Slide 38

Slide 38 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) } false

Slide 39

Slide 39 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }

Slide 40

Slide 40 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }

Slide 41

Slide 41 text

package main import ( "flag" "fmt" "os" ) func main() { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }

Slide 42

Slide 42 text

$ ssadump -build=CPF -importbin .

Slide 43

Slide 43 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 44

Slide 44 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 45

Slide 45 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 46

Slide 46 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 47

Slide 47 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 48

Slide 48 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 49

Slide 49 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 50

Slide 50 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 51

Slide 51 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 52

Slide 52 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 53

Slide 53 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return true

Slide 54

Slide 54 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return false

Slide 55

Slide 55 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 56

Slide 56 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 57

Slide 57 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 58

Slide 58 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 59

Slide 59 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 60

Slide 60 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 61

Slide 61 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 62

Slide 62 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return ???

Slide 63

Slide 63 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 64

Slide 64 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 65

Slide 65 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 66

Slide 66 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 67

Slide 67 text

func main(): 0: entry P:0 S:2 t0 = flag.Parse() () t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return

Slide 68

Slide 68 text

Tools built on go/ssa

Slide 69

Slide 69 text

safesql

Slide 70

Slide 70 text

safesql • Written by Carl Jackson at Stripe. • Verifies that the query string arg is a compile time constant. • Helps to prevent SQL injection.

Slide 71

Slide 71 text

safesql func main() { // Open database. db, err := sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Retrieve color from CLI args. color := flag.Arg(0) // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + color + "'") }

Slide 72

Slide 72 text

safesql func main() { // Open database. db, err := sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Retrieve color from CLI args. color := flag.Arg(0) // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + color + "'") } User Input

Slide 73

Slide 73 text

safesql func main() { // Open database. db, err := sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Retrieve color from CLI args. color := flag.Arg(0) // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + color + "'") } Ah! SQL Injection!

Slide 74

Slide 74 text

safesql $ safesql . Found 1 potentially unsafe SQL statements: - main.go:13:10 Please ensure that all SQL queries you use are compile-time constants. You should always use parameterized queries or prepared statements instead of building queries from strings.

Slide 75

Slide 75 text

safesql const FavColor = “purple” func main() { // Open database. db, err := sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + FavColor + "'") }

Slide 76

Slide 76 text

safesql $ safesql . You're safe from SQL injection! Yay \o/

Slide 77

Slide 77 text

oracle

Slide 78

Slide 78 text

oracle • Provides insights about your code. • Even works with interfaces! • Uses pointer analysis to scan your whole program

Slide 79

Slide 79 text

oracle • What code calls a function • What code is called by a function • Trace possible paths to a function call from main() • Determine what implements an interface • Find who sends to & receives from a channel • Determine possible values of a pointer • Find other identifiers that refer to the same value

Slide 80

Slide 80 text

oracle callees

Slide 81

Slide 81 text

type I interface { Foo() } type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} x.Foo() if condition { x = &V{} } x.Foo() }

Slide 82

Slide 82 text

$ oracle -pos main.go:#168 callees main.go main.go:24:7: this dynamic method call dispatches to: main.go:7:11: (*main.T).Foo

Slide 83

Slide 83 text

type I interface { Foo() } type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} x.Foo() if condition { x = &V{} } x.Foo() }

Slide 84

Slide 84 text

$ oracle -pos main.go:#210 callees main.go main.go:24:7: this dynamic method call dispatches to: main.go:7:11: (*main.T).Foo main.go:11:11: (*main.V).Foo

Slide 85

Slide 85 text

oracle callers

Slide 86

Slide 86 text

type I interface { Foo() } type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} if condition { x = &V{} x.Foo() } }

Slide 87

Slide 87 text

type I interface { Foo() } type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} if condition { x = &V{} x.Foo() } }

Slide 88

Slide 88 text

$ oracle -pos main.go:#61 callers main.go main.go:7:11: (*main.T).Foo is not reachable in this program.

Slide 89

Slide 89 text

type I interface { Foo() } type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} if condition { x = &V{} x.Foo() } }

Slide 90

Slide 90 text

$ oracle -pos main.go:#98 callers main.go main.go:11:11: (*main.V).Foo is called from these 1 sites: main.go:22:8: dynamic method call from main.main

Slide 91

Slide 91 text

oracle pointsto

Slide 92

Slide 92 text

type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }

Slide 93

Slide 93 text

type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }

Slide 94

Slide 94 text

$ oracle -pos main.go:#168 pointsto main.go main.go:21:6: this I may contain these dynamic types: main.go:8:6: *T, may point to: main.go:21:14: complit

Slide 95

Slide 95 text

type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }

Slide 96

Slide 96 text

$ oracle -pos main.go:#206 pointsto main.go main.go:24:3: this I may contain these dynamic types: main.go:12:6: *V, may point to: main.go:24:9: complit

Slide 97

Slide 97 text

type T struct{} func (*T) Foo() {} type V struct{} func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }

Slide 98

Slide 98 text

$ oracle -pos main.go:#244 pointsto main.go main.go:27:26: this I may contain these dynamic types: main.go:8:6: *T, may point to: main.go:21:14: complit main.go:12:6: *V, may point to: main.go:24:9: complit

Slide 99

Slide 99 text

gorename

Slide 100

Slide 100 text

gorename • Safely renames identifiers • Renames the declaration as well as references. • Even works across packages in GOPATH • Just uses go/types but it’s still cool • Intelligently prevents ambiguity and shadowing conflicts.

Slide 101

Slide 101 text

gorename func doSomething() { var x int if y := rand.Intn(10); y == 0 { x = 1 } fmt.Printf("x is %d\n", x) } Shadowing Conflict

Slide 102

Slide 102 text

gorename $ gorename -from mypkg.doSomething::x -to y Shadowing Conflict

Slide 103

Slide 103 text

gorename mypkg.go:2:6: renaming this var "x" to "y" mypkg.go:4:3: would cause this reference to become shadowed mypkg.go:5:5: by this intervening var definition Shadowing Conflict

Slide 104

Slide 104 text

gorename package mypkg type Widget struct { Name string } ... type Woojit struct { Widget Floojit string } Ambiguity Conflict

Slide 105

Slide 105 text

gorename $ gorename -from mypkg.Widget -to Floojit Ambiguity Conflict

Slide 106

Slide 106 text

gorename main.go:8:2: renaming this field "Widget" to "Floojit" main.go:9:2: would conflict with this field Ambiguity Conflict

Slide 107

Slide 107 text

golang.org/x/tools

Slide 108

Slide 108 text

It’s just the beginning! There is so much to build!

Slide 109

Slide 109 text

Q&A @benbjohnson