Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Static Code Analysis Using SSA

Static Code Analysis Using SSA

Presented at GopherCon 2015

benbjohnson

July 09, 2015
Tweet

More Decks by benbjohnson

Other Decks in Technology

Transcript

  1. Static Code Analysis
    Using SSA

    View Slide

  2. ‘go’ toolchain is great!

    View Slide

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

    View Slide

  4. What’s the next step?

    View Slide

  5. Code generation

    View Slide

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

    View Slide

  7. Over 1,000 packages import go/ast

    View Slide

  8. The AST only shows the
    structure of code

    View Slide

  9. Static Single Assignment
    Intermediate Representation

    View Slide

  10. SSA
    IR

    View Slide

  11. SSA IR shows
    how code flows

    View Slide

  12. golang.org/x/tools/go/ssa

    View Slide

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

    View Slide

  14. View Slide

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

    View Slide

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

    View Slide

  17. Intermediate Representation
    Go
    IR
    Machine Code

    View Slide

  18. IR is high level

    View Slide

  19. IR is portable

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. SSA makes
    analysis easier

    View Slide

  24. Terminology

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. 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

    View Slide

  33. What does it look like?

    View Slide

  34. 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)
    }

    View Slide

  35. 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)
    }

    View Slide

  36. 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)
    }

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. 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)
    }

    View Slide

  40. 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)
    }

    View Slide

  41. 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)
    }

    View Slide

  42. $ ssadump -build=CPF -importbin .

    View Slide

  43. 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

    View Slide

  44. 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

    View Slide

  45. 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

    View Slide

  46. 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

    View Slide

  47. 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

    View Slide

  48. 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

    View Slide

  49. 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

    View Slide

  50. 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

    View Slide

  51. 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

    View Slide

  52. 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

    View Slide

  53. 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

    View Slide

  54. 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

    View Slide

  55. 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

    View Slide

  56. 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

    View Slide

  57. 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

    View Slide

  58. 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

    View Slide

  59. 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

    View Slide

  60. 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

    View Slide

  61. 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

    View Slide

  62. 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
    ???

    View Slide

  63. 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

    View Slide

  64. 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

    View Slide

  65. 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

    View Slide

  66. 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

    View Slide

  67. 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

    View Slide

  68. Tools built on go/ssa

    View Slide

  69. safesql

    View Slide

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

    View Slide

  71. 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 + "'")
    }

    View Slide

  72. 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

    View Slide

  73. 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!

    View Slide

  74. 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.

    View Slide

  75. 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 + "'")
    }

    View Slide

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

    View Slide

  77. oracle

    View Slide

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

    View Slide

  79. 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

    View Slide

  80. oracle
    callees

    View Slide

  81. 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()
    }

    View Slide

  82. $ 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

    View Slide

  83. 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()
    }

    View Slide

  84. $ 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

    View Slide

  85. oracle
    callers

    View Slide

  86. 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()
    }
    }

    View Slide

  87. 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()
    }
    }

    View Slide

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

    View Slide

  89. 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()
    }
    }

    View Slide

  90. $ 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

    View Slide

  91. oracle
    pointsto

    View Slide

  92. 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)
    }

    View Slide

  93. 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)
    }

    View Slide

  94. $ 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

    View Slide

  95. 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)
    }

    View Slide

  96. $ 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

    View Slide

  97. 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)
    }

    View Slide

  98. $ 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

    View Slide

  99. gorename

    View Slide

  100. 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.

    View Slide

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

    View Slide

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

    View Slide

  103. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  107. golang.org/x/tools

    View Slide

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

    View Slide

  109. Q&A
    @benbjohnson

    View Slide