Share
Embed
Inside the Go playground
A presentation on how the Go playground is built and how it runs safely untrusted Go code.
golang
Francesc Campoy Flores
September 12, 2014
Transcript
43 slides
-
Agenda What is the Go playground What could go wrong
What did we do to avoid it An animated ASCII train -
Executable slides These slides are driven by the p r
e s e n t Go tool g o g e t c o d e . g o o g l e . c o m / g o . t o o l s / c m d / p r e s e n t p a c k a g e m a i n i m p o r t " f m t " f u n c m a i n ( ) { f m t . P r i n t l n ( " H e l l o , g o p h e r s ! " ) } Run -
Exhausting memory on the stack s t a c k
o v e r f l o w The runtime catches the error and panics. p a c k a g e m a i n f u n c f o o ( a [ 1 0 0 0 ] b y t e ) { f o o ( a ) } f u n c m a i n ( ) { f o o ( [ 1 0 0 0 ] b y t e { } ) } Run -
Too much memory on the heap o u t o
f m e m o r y Again the runtime catches the error and panics. p a c k a g e m a i n t y p e l i s t s t r u c t { b u f [ 1 0 0 0 0 0 ] b y t e n e x t * l i s t } f u n c m a i n ( ) { v a r l * l i s t f o r { l = & l i s t { n e x t : l } } } Run -
Too much CPU time p a c k a g
e m a i n f u n c m a i n ( ) { f o r { } } Run -
Stealing resources by sleeping A sleeping program still consumes resources.
Easy way of having a Denial of Service attack. p a c k a g e m a i n i m p o r t ( " f m t " " t i m e " ) f u n c m a i n ( ) { f m t . P r i n t l n ( " G o o d n i g h t " ) t i m e . S l e e p ( 8 * t i m e . H o u r ) f m t . P r i n t l n ( " G o o d m o r n i n g " ) } Run -
File system access User code shouldn't be able to modify
the backend's file system. Reading sensitive information Installing backdoors General mayhem f u n c m a i n ( ) { e r r : = o s . R e m o v e A l l ( " / f o o " ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } } Run -
Network access f u n c m a i n
( ) { r e s , e r r : = h t t p . G e t ( " h t t p : / / a p i . o p e n w e a t h e r m a p . o r g / d a t a / 2 . 5 / w e a t h e r ? q = P o r t l a n d " ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } d e f e r r e s . B o d y . C l o s e ( ) v a r w s t r u c t { W e a t h e r [ ] s t r u c t { D e s c s t r i n g ` j s o n : " d e s c r i p t i o n " ` } ` j s o n : " w e a t h e r " ` } i f e r r : = j s o n . N e w D e c o d e r ( r e s . B o d y ) . D e c o d e ( & w ) ; e r r ! = n i l { l o g . F a t a l ( e r r ) } f m t . P r i n t f ( " N o n e e d t o r u s h o u t s i d e , w e h a v e % v . " , w . W e a t h e r [ 0 ] . D e s c ) } Run -
Restricting resource usage with ulimit Default limits are not safe
enough. u l i m i t could solve this. - d m a x i m u m s i z e o f d a t a s e g m e n t o r h e a p ( i n k b y t e s ) - s m a x i m u m s i z e o f s t a c k s e g m e n t ( i n k b y t e s ) - t m a x i m u m C P U t i m e ( i n s e c o n d s ) - v m a x i m u m s i z e o f v i r t u a l m e m o r y ( i n k b y t e s ) -
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 -
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 t i m e package. f u n c S l e e p ( d t i m e . D u r a t i o n ) { p a n i c ( " N o s l e e p i n g i n t h e p l a y g r o u n d " ) } -
Faking the file system The s y s c a
l l package is the only link between user code and the OS kernel. The playground runtime has a custom s y s c a l l package. File system operations operate on a fake in-memory file system. f u n c m a i n ( ) { c o n s t f i l e n a m e = " / t m p / f i l e . t x t " e r r : = i o u t i l . W r i t e F i l e ( f i l e n a m e , [ ] b y t e ( " H e l l o , f i l e s y s t e m \ n " ) , 0 6 4 4 ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } b , e r r : = i o u t i l . R e a d F i l e ( f i l e n a m e ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } f m t . P r i n t f ( " % s " , b ) } Run -
Faking the network All network operations also use the s
y s c a l l package. The network stack is also faked in-memory. f u n c m a i n ( ) { l , e r r : = n e t . L i s t e n ( " t c p " , " 1 2 7 . 0 . 0 . 1 : 4 0 0 0 " ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } d e f e r l . C l o s e ( ) g o d i a l ( ) c , e r r : = l . A c c e p t ( ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } d e f e r c . C l o s e ( ) i o . C o p y ( o s . S t d o u t , c ) } Run -
Faking the network (continued) f u n c d i
a l ( ) { c , e r r : = n e t . D i a l ( " t c p " , " 1 2 7 . 0 . 0 . 1 : 4 0 0 0 " ) i f e r r ! = n i l { l o g . F a t a l ( e r r ) } d e f e r c . C l o s e ( ) c . W r i t e ( [ ] b y t e ( " H e l l o , n e t w o r k \ n " ) ) } Run -
Sleeping in the playground Go is about concurrency. We need
to demonstrate concurrency in blog posts and talks. And demonstrating concurrency without t i m e is hard. -
Normal behavior There's a special goroutine managing timers T .
A goroutine G calls t i m e . S l e e p : 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 Many flavors of deadlocks. One common property: all
goroutines are asleep. p a c k a g e m a i n f u n c m a i n ( ) { c : = m a k e ( c h a n i n t ) < - c } Run -
New behavior A goroutine G calls t i m e
. S l e e p : 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. p a
c k a g e m a i n i m p o r t ( " f m t " " t i m e " ) f u n c m a i n ( ) { s t a r t : = t i m e . N o w ( ) f m t . P r i n t l n ( s t a r t ) f o r i : = 0 ; i < 1 0 ; i + + { t i m e . S l e e p ( t i m e . N a n o s e c o n d ) f m t . P r i n t l n ( t i m e . S i n c e ( s t a r t ) ) } } Run -
So there's no actual sleep? The playground's w r i
t e syscall inserts a timestamp before each write. The front end translates that into a series of "events" that the browser can play back. Returns directly { " E r r o r s " : " " , " E v e n t s " : [ { " M e s s a g e " : " G o o d n i g h t \ n " , " D e l a y " : 0 } , { " M e s s a g e " : " G o o d m o r n i n g \ n " , " D e l a y " : 2 8 8 0 0 0 0 0 0 0 0 0 0 0 } ] } f u n c m a i n ( ) { f m t . P r i n t l n ( " G o o d n i g h t " ) t i m e . S l e e p ( 8 * t i m e . H o u r ) f m t . P r i n t l n ( " G o o d m o r n i n g " ) } Run -
References These slides: talks.golang.org/2014/playground.slide (http://talks.golang.org/2014/playground.slide) More about the Go tour:
Inside the Go playground: blog.golang.org/playground (http://blog.golang.org/playground) The Go tour: tour.golang.org (http://tour.golang.org) More about Go on NaCl: Running Go under Native Client: code.google.com/p/go-wiki/wiki/NativeClient (https://code.google.com/p/go-wiki/wiki/NativeClient) Go 1.3 Native Client Support: golang.org/s/go13nacl (http://golang.org/s/go13nacl) -
Thank you Francesc Campoy Flores Developer Advocate, Gopher @francesc (http://twitter.com/francesc)
[email protected] (mailto:[email protected]) http://campoy.cat (http://campoy.cat)