Inside the Go playground Francesc Campoy Flores Developer Advocate, Gopher @francesc campoy@golang.org http://campoy.cat * Agenda - What is the Go playground - What could go wrong - What did we do to avoid it - An animated ASCII train * The Go playground .image playground/img/play.png 500 _ .caption [[/play/][go.dev/play]] * De facto pastebin of the Go community .image playground/img/share.png 500 _ .caption [[/play/p/bJYnajZ6Kp][go.dev/play/p/bJYnajZ6Kp]] * The Go tour .image playground/img/tour.png 500 _ .caption [[/tour/][go.dev/tour]] * Executable examples on documentation .image playground/img/examples.png 500 _ .caption [[/pkg/strings/#example_Fields][go.dev/pkg/strings]] * Executable code on blog posts .image playground/img/blog.png 500 _ .caption [[/blog/slices][go.dev/blog/slices]] * Executable slides .play playground/hello.go These slides are driven by the `present` Go tool go get code.google.com/go.tools/cmd/present * Naive implementation * Architecture .image playground/img/arch.png 500 _ * Backend Let's start with something simple - receive code - compile it - run it * What could go wrong? .image playground/img/areyousure.png 500 _ ########### ## Issues # ########### * Resource exhaustion * Exhausting memory on the stack `stack`overflow` .play playground/stack.go The runtime catches the error and panics. * Too much memory on the heap `out`of`memory` .play playground/heap.go Again the runtime catches the error and panics. * Too much CPU time .play playground/loop.go * Stealing resources by sleeping .play playground/sleep.go A sleeping program still consumes resources. Easy way of having a Denial of Service attack. * Accessing things you shouldn't * File system access User code shouldn't be able to modify the backend's file system. - Reading sensitive information - Installing backdoors - General mayhem .play playground/removeall.go /func main/,/^}/ * Network access .play playground/http.go /func main/,/^}/ * Use your imagination .image playground/img/cat.jpg 500 _ ################### # Countermeasures # ################### * Countermeasures * Restricting resource usage with ulimit Default limits are not safe enough. `ulimit` could solve this. -d maximum size of data segment or heap (in kbytes) -s maximum size of stack segment (in kbytes) -t maximum CPU time (in seconds) -v maximum size of virtual memory (in kbytes) * Native Client Originally designed to execute native code in Chrome safely. NaCl defines restrictions on the binaries being executed. The code runs in a sandbox isolated from the underlying OS. - No file access - No network access .image playground/img/nacl.png 300 _ * Isolating process execution with NaCl We use NaCl to: - limit CPU time - limit memory - isolate from the filesystem - isolate from the network Process can only write to stdout/stderr. * Limiting user time "No sleeping in the playground." Custom runtime with a fake `time` package. func Sleep(d time.Duration) { panic("No sleeping in the playground") } * Restoring functionality * Faking the file system The `syscall` package is the only link between user code and the OS kernel. The playground runtime has a custom `syscall` package. File system operations operate on a fake in-memory file system. .play playground/file.go /func main/, * Faking the network All network operations also use the `syscall` package. The network stack is also faked in-memory. .play playground/net.go /func main/,/^}/ * Faking the network (continued) .play playground/net.go /func dial/,/^}/ ########## ## TIME ## ########## * Sleeping in the playground Go is about concurrency. We need to demonstrate concurrency in blog posts and talks. And demonstrating concurrency without `time` is hard. * What to do if an open source project lacks a feature? .image playground/img/gopherbw.png 500 _ * File a bug! .image playground/img/bug.png 500 _ .caption [[https://code.google.com/p/go/issues/detail?id=4280][bug 4280]] * Normal behavior There's a special goroutine managing timers `T`. A goroutine `G` calls `time.Sleep`: 1. `G` adds a timer to the timer heap. 2. `G` puts itself to sleep. 3. `T` tells the OS to wake it when the next timer expires and puts itself to sleep. 4. When `T` is woken up it looks at the timer on the top of the heap, and wakes the corresponding goroutine. * Intermission: deadlocks .play playground/deadlock.go Many flavors of deadlocks. One common property: all goroutines are asleep. * New behavior A goroutine `G` calls `time.Sleep`: 1. `G` adds a timer to the timer heap. 2. `G` puts itself to sleep. 3. The scheduler detects a deadlock, checks the timer heap for pending timers. 4. The internal clock is advanced to the next timer expiration. 5. The corresponding goroutines are woken up. * Sleeping fast Faking time allows precise sleep durations. .play playground/sleepfast.go * So there's no actual sleep? The playground's `write` syscall inserts a timestamp before each write. The front end translates that into a series of "events" that the browser can play back. .play playground/sleep.go /func main/, Returns directly { "Errors":"", "Events":[ {"Message":"Good night\n","Delay":0}, {"Message":"Good morning\n","Delay":28800000000000} ] } * So the bug was fixed .image playground/img/andrew.png _ 1000 .caption [[/play/p/3fv0L3-z0s][go.dev/play/p/3fv0L3-z0s]] * And people were happy .image playground/img/brad.png _ 1000 .caption [[/play/p/rX_3WcpUOZ][go.dev/play/p/rX_3WcpUOZ]] * Very happy .image playground/img/jan.png _ 1000 .caption [[/play/p/P-Dk0NH_vf][go.dev/play/p/P-Dk0NH_vf]] .image playground/img/mattn.png _ 1000 .caption [[/play/p/NOycgN2i6b][go.dev/play/p/NOycgN2i6b]] * References These slides: [[/talks/2014/playground.slide]] More about the Go tour: - Inside the Go playground: [[/blog/playground][go.dev/blog/playground]] - The Go tour: [[/tour/][go.dev/tour]] More about Go on NaCl: - Running Go under Native Client: [[https://code.google.com/p/go-wiki/wiki/NativeClient]] - Go 1.3 Native Client Support: [[/s/go13nacl]]