Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Unified Diff: sandbox/linux/seccomp-bpf/sandbox_bpf.h

Issue 12223109: SECCOMP-BPF: Refactor the BPF sandbox API to use fewer "static" fields and methods. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Make sure unnamed namespaces are always top-level Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: sandbox/linux/seccomp-bpf/sandbox_bpf.h
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
index 3e2152799c5dc4f0189f8a124e9663bd33deb6f7..159d4aacd46d1ae212d6d9f2760821ae3fa61505 100644
--- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -5,33 +5,9 @@
#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-// #include <linux/audit.h>
-#include <linux/filter.h>
-// #include <linux/seccomp.h>
-#include <linux/unistd.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
-#include <sched.h>
-#include <signal.h>
#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/ipc.h>
-#include <sys/mman.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/uio.h>
#include <sys/wait.h>
-#include <time.h>
-#include <unistd.h>
#include <algorithm>
#include <limits>
@@ -40,212 +16,11 @@
#include <utility>
#include <vector>
-#if !defined(SECCOMP_BPF_STANDALONE)
-#include "base/basictypes.h"
-#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
-#endif
-
-
-// The Seccomp2 kernel ABI is not part of older versions of glibc.
-// As we can't break compilation with these versions of the library,
-// we explicitly define all missing symbols.
-
-// For audit.h
-#ifndef EM_ARM
-#define EM_ARM 40
-#endif
-#ifndef EM_386
-#define EM_386 3
-#endif
-#ifndef EM_X86_64
-#define EM_X86_64 62
-#endif
-
-#ifndef __AUDIT_ARCH_64BIT
-#define __AUDIT_ARCH_64BIT 0x80000000
-#endif
-#ifndef __AUDIT_ARCH_LE
-#define __AUDIT_ARCH_LE 0x40000000
-#endif
-#ifndef AUDIT_ARCH_ARM
-#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
-#endif
-#ifndef AUDIT_ARCH_I386
-#define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
-#endif
-#ifndef AUDIT_ARCH_X86_64
-#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
-#endif
-
-// For prctl.h
-#ifndef PR_SET_SECCOMP
-#define PR_SET_SECCOMP 22
-#define PR_GET_SECCOMP 21
-#endif
-#ifndef PR_SET_NO_NEW_PRIVS
-#define PR_SET_NO_NEW_PRIVS 38
-#define PR_GET_NO_NEW_PRIVS 39
-#endif
-#ifndef IPC_64
-#define IPC_64 0x0100
-#endif
-
-#ifndef BPF_MOD
-#define BPF_MOD 0x90
-#endif
-#ifndef BPF_XOR
-#define BPF_XOR 0xA0
-#endif
-
-// In order to build will older tool chains, we currently have to avoid
-// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
-// our own definitions of the seccomp kernel ABI.
-#ifndef SECCOMP_MODE_FILTER
-#define SECCOMP_MODE_DISABLED 0
-#define SECCOMP_MODE_STRICT 1
-#define SECCOMP_MODE_FILTER 2 // User user-supplied filter
-#endif
-
-#ifndef SECCOMP_RET_KILL
-// Return values supported for BPF filter programs. Please note that the
-// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
-// ever be used internally, and would result in the kernel killing our process.
-#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
-#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
-#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
-#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
-#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
-#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
-#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
-#define SECCOMP_RET_DATA 0x0000ffffU // sections
-#else
-#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
-#endif
-
-#ifndef SYS_SECCOMP
-#define SYS_SECCOMP 1
-#endif
-
-// Impose some reasonable maximum BPF program size. Realistically, the
-// kernel probably has much lower limits. But by limiting to less than
-// 30 bits, we can ease requirements on some of our data types.
-#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
-
-#if defined(__i386__)
-#define MIN_SYSCALL 0u
-#define MAX_PUBLIC_SYSCALL 1024u
-#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
-#define SECCOMP_ARCH AUDIT_ARCH_I386
-
-#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
-#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_EAX)
-#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_EAX)
-#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_EIP)
-#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_EBX)
-#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_ECX)
-#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_EDX)
-#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_ESI)
-#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_EDI)
-#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP)
-#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
-#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
-#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 4)
-#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 0)
-#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 4)
-#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 0)
-
-#elif defined(__x86_64__)
-#define MIN_SYSCALL 0u
-#define MAX_PUBLIC_SYSCALL 1024u
-#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
-#define SECCOMP_ARCH AUDIT_ARCH_X86_64
-
-#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
-#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX)
-#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX)
-#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP)
-#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI)
-#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI)
-#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX)
-#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10)
-#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8)
-#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9)
-#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
-#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
-#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 4)
-#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 0)
-#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 4)
-#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 0)
-
-#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
-// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
-// and a "ghost syscall private to the kernel", cmpxchg,
-// at |__ARM_NR_BASE+0x00fff0|.
-// See </arch/arm/include/asm/unistd.h> in the Linux kernel.
-#define MIN_SYSCALL ((unsigned int)__NR_SYSCALL_BASE)
-#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u)
-#define MIN_PRIVATE_SYSCALL ((unsigned int)__ARM_NR_BASE)
-#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
-#define MIN_GHOST_SYSCALL ((unsigned int)__ARM_NR_BASE + 0xfff0u)
-#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u)
-
-#define SECCOMP_ARCH AUDIT_ARCH_ARM
-
-// ARM sigcontext_t is different from i386/x86_64.
-// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
-#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
-// ARM EABI syscall convention.
-#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, r0)
-#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, r7)
-#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, pc)
-#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, r0)
-#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, r1)
-#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, r2)
-#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, r3)
-#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, r4)
-#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, r5)
-#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
-#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
-#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 4)
-#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
- instruction_pointer) + 0)
-#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 4)
-#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
- 8*(nr) + 0)
-
-#else
-#error Unsupported target platform
-
-#endif
-
-#if defined(SECCOMP_BPF_STANDALONE)
-#define arraysize(x) (sizeof(x)/sizeof(*(x)))
-#define HANDLE_EINTR TEMP_FAILURE_RETRY
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
- TypeName(); \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
-template <bool>
-struct CompileAssert {
-};
-#define COMPILE_ASSERT(expr, msg) \
- typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
-#endif
-
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
+
namespace playground2 {
@@ -281,13 +56,17 @@ class Sandbox {
// each time a call is made through an EvaluateSyscall function pointer.
// One common use case would be to pass the "aux" pointer as an argument
// to Trap() functions.
- typedef ErrorCode (*EvaluateSyscall)(int sysnum, void *aux);
+ typedef ErrorCode (*EvaluateSyscall)(Sandbox *sb, int sysnum, void *aux);
typedef std::vector<std::pair<EvaluateSyscall, void *> >Evaluators;
// A vector of BPF instructions that need to be installed as a filter
// program in the kernel.
typedef std::vector<struct sock_filter> Program;
+ // Constructors and destructors.
+ Sandbox();
jln (very slow on Chromium) 2013/02/20 01:35:49 It's not clear what the lifetime of this object is
+ ~Sandbox();
+
// Checks whether a particular system call number is valid on the current
// architecture. E.g. on ARM there's a non-contiguous range of private
// system calls.
@@ -304,8 +83,8 @@ class Sandbox {
// directory is not accessible when "startSandbox()" gets called, the caller
// can provide an already opened file descriptor by calling "set_proc_fd()".
// The sandbox becomes the new owner of this file descriptor and will
- // eventually close it when "startSandbox()" executes.
- static void set_proc_fd(int proc_fd);
+ // eventually close it when "StartSandbox()" executes.
+ void set_proc_fd(int proc_fd);
// The system call evaluator function is called with the system
// call number. It can decide to allow the system call unconditionally
@@ -318,15 +97,15 @@ class Sandbox {
// handler. In this case, of course, the data that is pointed to must remain
// valid for the entire time that Trap() handlers can be called; typically,
// this would be the lifetime of the program.
- static void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
+ void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
// We can use ErrorCode to request calling of a trap handler. This method
// performs the required wrapping of the callback function into an
// ErrorCode object.
// The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
- // for a description of how to pass data from setSandboxPolicy() to a Trap()
+ // for a description of how to pass data from SetSandboxPolicy() to a Trap()
// handler.
- static ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
+ ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
// Calls a user-space trap handler and disables all sandboxing for system
// calls made from this trap handler.
@@ -338,7 +117,7 @@ class Sandbox {
// very useful to diagnose code that is incompatible with the sandbox.
// If even a single system call returns "UnsafeTrap", the security of
// entire sandbox should be considered compromised.
- static ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
+ ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
// From within an UnsafeTrap() it is often useful to be able to execute
// the system call that triggered the trap. The ForwardSyscall() method
@@ -360,18 +139,26 @@ class Sandbox {
// If it is outside this range, the sandbox treats the system call just
// the same as any other ABI violation (i.e. it aborts with an error
// message).
- static ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
- ErrorCode::Operation op,
- uint64_t value, const ErrorCode& passed,
- const ErrorCode& failed);
+ ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
+ ErrorCode::Operation op,
+ uint64_t value, const ErrorCode& passed,
+ const ErrorCode& failed);
// Kill the program and print an error message.
- static ErrorCode Kill(const char *msg);
+ ErrorCode Kill(const char *msg);
// This is the main public entry point. It finds all system calls that
// need rewriting, sets up the resources needed by the sandbox, and
// enters Seccomp mode.
- static void StartSandbox() { StartSandboxInternal(false); }
+ // It is possible to stack multiple sandboxes by creating separate "Sandbox"
+ // objects and calling "StartSandbox()" on each of them. Please note, that
+ // this requires special care, though, as newly stacked sandboxes can never
+ // relax restrictions imposed by earlier sandboxes. Furthermore, installing
+ // a new policy requires making system calls, that might already be
+ // disallowed.
+ // Finally, stacking does add more kernel overhead than having a single
+ // combined policy. So, it should only be used if there are no alternatives.
+ void StartSandbox();
// Assembles a BPF filter program from the current policy. After calling this
// function, you must not call any other sandboxing function.
@@ -381,14 +168,17 @@ class Sandbox {
// through the verifier, iff the program was built in debug mode.
// But by setting "force_verification", the caller can request that the
// verifier is run unconditionally. This is useful for unittests.
- static Program *AssembleFilter(bool force_verification);
+ Program *AssembleFilter(bool force_verification);
+
+ // Returns the fatal ErrorCode that is used to indicate that somebody
+ // attempted to pass a 64bit value in a 32bit system call argument.
+ // This method is primarily needed for testing purposes.
+ ErrorCode Unexpected64bitArgument();
private:
friend class CodeGen;
friend class SandboxUnittestHelper;
friend class ErrorCode;
- friend class Util;
- friend class Verifier;
struct Range {
Range(uint32_t f, uint32_t t, const ErrorCode& e)
@@ -404,91 +194,65 @@ class Sandbox {
typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
// Get a file descriptor pointing to "/proc", if currently available.
- static int proc_fd() { return proc_fd_; }
-
- static ErrorCode ProbeEvaluator(int sysnum, void *) __attribute__((const));
- static void ProbeProcess(void);
- static ErrorCode AllowAllEvaluator(int sysnum, void *aux);
- static void TryVsyscallProcess(void);
- static bool KernelSupportSeccompBPF(int proc_fd);
- static bool RunFunctionInPolicy(void (*function)(),
- EvaluateSyscall syscall_evaluator,
- void *aux,
- int proc_fd);
- static void StartSandboxInternal(bool quiet);
- static bool IsSingleThreaded(int proc_fd);
- static bool IsDenied(const ErrorCode& code);
- static bool DisableFilesystem();
- static void PolicySanityChecks(EvaluateSyscall syscall_evaluator,
- void *aux);
-
- // Function that can be passed as a callback function to CodeGen::Traverse().
- // Checks whether the "insn" returns an UnsafeTrap() ErrorCode. If so, it
- // sets the "bool" variable pointed to by "aux".
- static void CheckForUnsafeErrorCodes(Instruction *insn, void *aux);
-
- // Function that can be passed as a callback function to CodeGen::Traverse().
- // Checks whether the "insn" returns an errno value from a BPF filter. If so,
- // it rewrites the instruction to instead call a Trap() handler that does
- // the same thing. "aux" is ignored.
- static void RedirectToUserspace(Instruction *insn, void *aux);
-
- // Stackable wrapper around an Evaluators handler. Changes ErrorCodes
- // returned by a system call evaluator to match the changes made by
- // RedirectToUserspace(). "aux" should be pointer to wrapped system call
- // evaluator.
- static ErrorCode RedirectToUserspaceEvalWrapper(int sysnum, void *aux);
+ int proc_fd() { return proc_fd_; }
+
+ // Creates a subprocess and runs "code_in_sandbox" inside of the specified
+ // policy. The caller has to make sure that "this" has not yet been
+ // initialized with any other policies.
+ bool RunFunctionInPolicy(void (*code_in_sandbox)(),
+ EvaluateSyscall syscall_evaluator, void *aux);
+
+ // Performs a couple of sanity checks to verify that the kernel supports the
+ // features that we need for successful sandboxing.
+ // The caller has to make sure that "this" has not yet been initialized with
+ // any other policies.
+ bool KernelSupportSeccompBPF();
+
+ // Verify that the current policy passes some basic sanity checks.
+ void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux);
// Assembles and installs a filter based on the policy that has previously
// been configured with SetSandboxPolicy().
- static void InstallFilter(bool quiet);
+ void InstallFilter();
// Verify the correctness of a compiled program by comparing it against the
// current policy. This function should only ever be called by unit tests and
// by the sandbox internals. It should not be used by production code.
- static void VerifyProgram(const Program& program, bool has_unsafe_traps);
+ void VerifyProgram(const Program& program, bool has_unsafe_traps);
// Finds all the ranges of system calls that need to be handled. Ranges are
// sorted in ascending order of system call numbers. There are no gaps in the
// ranges. System calls with identical ErrorCodes are coalesced into a single
// range.
- static void FindRanges(Ranges *ranges);
+ void FindRanges(Ranges *ranges);
// Returns a BPF program snippet that implements a jump table for the
// given range of system call numbers. This function runs recursively.
- static Instruction *AssembleJumpTable(CodeGen *gen,
- Ranges::const_iterator start,
- Ranges::const_iterator stop);
+ Instruction *AssembleJumpTable(CodeGen *gen,
+ Ranges::const_iterator start,
+ Ranges::const_iterator stop);
// Returns a BPF program snippet that makes the BPF filter program exit
// with the given ErrorCode "err". N.B. the ErrorCode may very well be a
// conditional expression; if so, this function will recursively call
// CondExpression() and possibly RetExpression() to build a complex set of
// instructions.
- static Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
+ Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
// Returns a BPF program that evaluates the conditional expression in
// "cond" and returns the appropriate value from the BPF filter program.
// This function recursively calls RetExpression(); it should only ever be
// called from RetExpression().
- static Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
-
- // Returns the fatal ErrorCode that is used to indicate that somebody
- // attempted to pass a 64bit value in a 32bit system call argument.
- static ErrorCode Unexpected64bitArgument();
-
- // A Trap() handler that returns an "errno" value. The value is encoded
- // in the "aux" parameter.
- static intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux);
-
- static intptr_t BpfFailure(const struct arch_seccomp_data& data, void *aux);
+ Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
static SandboxStatus status_;
- static int proc_fd_;
- static Evaluators evaluators_;
- static Conds conds_;
- DISALLOW_IMPLICIT_CONSTRUCTORS(Sandbox);
+ bool quiet_;
+ int proc_fd_;
+ Evaluators *evaluators_;
+ Conds *conds_;
+
+ DISALLOW_COPY_AND_ASSIGN(Sandbox);
};
} // namespace

Powered by Google App Engine
This is Rietveld 408576698