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

Side by Side Diff: content/common/sandbox_linux/sandbox_bpf_gpu_policy_linux.cc

Issue 99133015: Linux Sandbox: split the GPU policies to their own file. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup InitGpuBrokerProcess. Created 7 years 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <asm/unistd.h> 5 #include "content/common/sandbox_linux/sandbox_bpf_gpu_policy_linux.h"
6
6 #include <dlfcn.h> 7 #include <dlfcn.h>
7 #include <errno.h> 8 #include <errno.h>
8 #include <fcntl.h> 9 #include <fcntl.h>
9 #include <linux/net.h>
10 #include <signal.h>
11 #include <string.h>
12 #include <sys/ioctl.h>
13 #include <sys/mman.h>
14 #include <sys/prctl.h>
15 #include <sys/socket.h> 10 #include <sys/socket.h>
16 #include <sys/stat.h> 11 #include <sys/stat.h>
17 #include <sys/types.h> 12 #include <sys/types.h>
18 #include <ucontext.h>
19 #include <unistd.h> 13 #include <unistd.h>
20 14
15 #include <string>
21 #include <vector> 16 #include <vector>
22 17
23 #include "base/basictypes.h"
24 #include "base/command_line.h" 18 #include "base/command_line.h"
19 #include "base/compiler_specific.h"
25 #include "base/logging.h" 20 #include "base/logging.h"
26 #include "build/build_config.h" 21 #include "base/memory/scoped_ptr.h"
27 #include "content/common/sandbox_linux.h" 22 #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h"
28 #include "content/common/sandbox_seccomp_bpf_linux.h" 23 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
29 #include "content/public/common/content_switches.h" 24 #include "content/public/common/content_switches.h"
30 #include "sandbox/linux/services/broker_process.h"
31
32 // These are the only architectures supported for now.
33 #if defined(__i386__) || defined(__x86_64__) || \
34 (defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__)))
35 #define SECCOMP_BPF_SANDBOX
36 #endif
37
38 #if defined(SECCOMP_BPF_SANDBOX)
39 #include "base/posix/eintr_wrapper.h"
40 #include "content/common/sandbox_bpf_base_policy_linux.h"
41 #include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
42 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
43 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
44 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" 25 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
45 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" 26 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
46 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" 27 #include "sandbox/linux/services/broker_process.h"
47 #include "sandbox/linux/services/linux_syscalls.h" 28 #include "sandbox/linux/services/linux_syscalls.h"
48 29
49 using sandbox::BaselinePolicy;
50 using sandbox::BrokerProcess; 30 using sandbox::BrokerProcess;
51 using sandbox::ErrorCode; 31 using sandbox::ErrorCode;
52 using sandbox::SandboxBPF; 32 using sandbox::SandboxBPF;
53 using sandbox::SyscallSets; 33 using sandbox::SyscallSets;
54 using sandbox::arch_seccomp_data; 34 using sandbox::arch_seccomp_data;
55 35
56 namespace content { 36 namespace content {
57 37
58 namespace { 38 namespace {
59 39
60 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy); 40 void InitGpuBrokerProcess(bool (*broker_sandboxer_callback)(void),
41 bool for_chromeos_arm,
42 const std::vector<std::string>& read_whitelist_extra,
43 const std::vector<std::string>& write_whitelist_extra,
44 BrokerProcess** broker_process);
45
46 void AddArmGpuWhitelist(std::vector<std::string>* read_whitelist,
47 std::vector<std::string>* write_whitelist);
48 bool EnableGpuBrokerPolicyCallback();
49 bool EnableArmGpuBrokerPolicyCallback();
61 50
62 inline bool IsChromeOS() { 51 inline bool IsChromeOS() {
63 #if defined(OS_CHROMEOS) 52 #if defined(OS_CHROMEOS)
64 return true; 53 return true;
65 #else 54 #else
66 return false; 55 return false;
67 #endif 56 #endif
68 } 57 }
69 58
70 inline bool IsArchitectureX86_64() { 59 inline bool IsArchitectureX86_64() {
(...skipping 13 matching lines...) Expand all
84 } 73 }
85 74
86 inline bool IsArchitectureArm() { 75 inline bool IsArchitectureArm() {
87 #if defined(__arm__) 76 #if defined(__arm__)
88 return true; 77 return true;
89 #else 78 #else
90 return false; 79 return false;
91 #endif 80 #endif
92 } 81 }
93 82
94 inline bool IsUsingToolKitGtk() {
95 #if defined(TOOLKIT_GTK)
96 return true;
97 #else
98 return false;
99 #endif
100 }
101
102 // Policies for the GPU process.
103 // TODO(jln): move to gpu/
104
105 bool IsAcceleratedVideoDecodeEnabled() { 83 bool IsAcceleratedVideoDecodeEnabled() {
106 // Accelerated video decode is currently enabled on Chrome OS, 84 // Accelerated video decode is currently enabled on Chrome OS,
107 // but not on Linux: crbug.com/137247. 85 // but not on Linux: crbug.com/137247.
108 bool is_enabled = IsChromeOS(); 86 bool is_enabled = IsChromeOS();
109 87
110 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 88 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
111 is_enabled = is_enabled && 89 is_enabled = is_enabled &&
112 !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode); 90 !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode);
113 91
114 return is_enabled; 92 return is_enabled;
(...skipping 21 matching lines...) Expand all
136 return -EPERM; 114 return -EPERM;
137 } 115 }
138 default: 116 default:
139 RAW_CHECK(false); 117 RAW_CHECK(false);
140 return -ENOSYS; 118 return -ENOSYS;
141 } 119 }
142 } 120 }
143 121
144 class GpuProcessPolicy : public SandboxBPFBasePolicy { 122 class GpuProcessPolicy : public SandboxBPFBasePolicy {
145 public: 123 public:
146 explicit GpuProcessPolicy(void* broker_process) 124 GpuProcessPolicy() : broker_process_(NULL) {}
147 : broker_process_(broker_process) {}
148 virtual ~GpuProcessPolicy() {} 125 virtual ~GpuProcessPolicy() {}
149 126
150 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, 127 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
151 int system_call_number) const OVERRIDE; 128 int system_call_number) const OVERRIDE;
129 virtual bool PreSandboxHook() OVERRIDE;
130
131 protected:
132 BrokerProcess* broker_process() { return broker_process_; }
133 void set_broker_process(BrokerProcess* broker_process) {
Jorge Lucangeli Obes 2013/12/12 21:37:21 Is this the correct style for the setter?
Robert Sesek 2013/12/12 21:41:43 Yes, simple accessors use unix_hacker_style and ge
jln (very slow on Chromium) 2013/12/12 22:15:14 Done.
jln (very slow on Chromium) 2013/12/12 22:15:14 Done.
134 broker_process_ = broker_process;
135 }
152 136
153 private: 137 private:
154 const void* broker_process_; // Non-owning pointer. 138 // A BrokerProcess is a helper that is started before the sandbox is engaged
139 // and will serve requests to access files over an IPC. The client of this
Jorge Lucangeli Obes 2013/12/12 21:37:21 I would say "over IPC" or "over an IPC channel".
jln (very slow on Chromium) 2013/12/12 22:15:14 Done.
140 // runs from a SIGSYS handler triggered by the seccomp-bpf sandbox.
141 // This should never be destroyed, as after the sandbox is started it is
142 // vital to the process.
143 // This is allocated by PreSandboxHook(), which executes iff the sandbox
144 // is going to be enabled afterwards.
145 BrokerProcess* broker_process_;
155 DISALLOW_COPY_AND_ASSIGN(GpuProcessPolicy); 146 DISALLOW_COPY_AND_ASSIGN(GpuProcessPolicy);
156 }; 147 };
157 148
158 // Main policy for x86_64/i386. Extended by ArmGpuProcessPolicy. 149 // Main policy for x86_64/i386. Extended by CrosArmGpuProcessPolicy.
159 ErrorCode GpuProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, 150 ErrorCode GpuProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox,
160 int sysno) const { 151 int sysno) const {
161 switch (sysno) { 152 switch (sysno) {
162 case __NR_ioctl: 153 case __NR_ioctl:
163 #if defined(__i386__) || defined(__x86_64__) 154 #if defined(__i386__) || defined(__x86_64__)
164 // The Nvidia driver uses flags not in the baseline policy 155 // The Nvidia driver uses flags not in the baseline policy
165 // (MAP_LOCKED | MAP_EXECUTABLE | MAP_32BIT) 156 // (MAP_LOCKED | MAP_EXECUTABLE | MAP_32BIT)
166 case __NR_mmap: 157 case __NR_mmap:
167 #endif 158 #endif
168 // We also hit this on the linux_chromeos bot but don't yet know what 159 // We also hit this on the linux_chromeos bot but don't yet know what
169 // weird flags were involved. 160 // weird flags were involved.
170 case __NR_mprotect: 161 case __NR_mprotect:
171 case __NR_sched_getaffinity: 162 case __NR_sched_getaffinity:
172 case __NR_sched_setaffinity: 163 case __NR_sched_setaffinity:
173 case __NR_setpriority: 164 case __NR_setpriority:
174 return ErrorCode(ErrorCode::ERR_ALLOWED); 165 return ErrorCode(ErrorCode::ERR_ALLOWED);
175 case __NR_access: 166 case __NR_access:
176 case __NR_open: 167 case __NR_open:
177 case __NR_openat: 168 case __NR_openat:
169 DCHECK(broker_process_);
178 return sandbox->Trap(GpuSIGSYS_Handler, broker_process_); 170 return sandbox->Trap(GpuSIGSYS_Handler, broker_process_);
179 default: 171 default:
180 if (SyscallSets::IsEventFd(sysno)) 172 if (SyscallSets::IsEventFd(sysno))
181 return ErrorCode(ErrorCode::ERR_ALLOWED); 173 return ErrorCode(ErrorCode::ERR_ALLOWED);
182 174
183 // Default on the baseline policy. 175 // Default on the baseline policy.
184 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno); 176 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno);
185 } 177 }
186 } 178 }
187 179
180 bool GpuProcessPolicy::PreSandboxHook() {
181 // Warm up resources needed by the policy we're about to enable and
182 // eventually start a broker process.
183 const bool chromeos_arm_gpu = IsChromeOS() && IsArchitectureArm();
184 DCHECK(!chromeos_arm_gpu);
Jorge Lucangeli Obes 2013/12/12 21:37:21 Maybe add a comment reminding the reader "This pol
jln (very slow on Chromium) 2013/12/12 22:15:14 Done, but not that it's also used on ARM desktop.
185
186 BrokerProcess* broker_process_temp = NULL;
187 DCHECK(!broker_process());
188 // Create a new broker process.
189 InitGpuBrokerProcess(
190 EnableGpuBrokerPolicyCallback,
191 false /* not for ChromeOS ARM */,
192 std::vector<std::string>(), // No extra files in whitelist.
193 std::vector<std::string>(),
194 &broker_process_temp);
195 set_broker_process(broker_process_temp);
196
197 if (IsArchitectureX86_64() || IsArchitectureI386()) {
198 // Accelerated video decode dlopen()'s some shared objects
199 // inside the sandbox, so preload them now.
200 if (IsAcceleratedVideoDecodeEnabled()) {
201 const char* I965DrvVideoPath = NULL;
202
203 if (IsArchitectureX86_64()) {
204 I965DrvVideoPath = "/usr/lib64/va/drivers/i965_drv_video.so";
205 } else if (IsArchitectureI386()) {
206 I965DrvVideoPath = "/usr/lib/va/drivers/i965_drv_video.so";
207 }
208
209 dlopen(I965DrvVideoPath, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
210 dlopen("libva.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
211 dlopen("libva-x11.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
212 }
213 }
214
215 return true;
216 }
217
188 class GpuBrokerProcessPolicy : public GpuProcessPolicy { 218 class GpuBrokerProcessPolicy : public GpuProcessPolicy {
189 public: 219 public:
190 GpuBrokerProcessPolicy() : GpuProcessPolicy(NULL) {} 220 GpuBrokerProcessPolicy() {}
191 virtual ~GpuBrokerProcessPolicy() {} 221 virtual ~GpuBrokerProcessPolicy() {}
192 222
193 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, 223 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
194 int system_call_number) const OVERRIDE; 224 int system_call_number) const OVERRIDE;
195 225
196 private: 226 private:
197 DISALLOW_COPY_AND_ASSIGN(GpuBrokerProcessPolicy); 227 DISALLOW_COPY_AND_ASSIGN(GpuBrokerProcessPolicy);
198 }; 228 };
199 229
200 // x86_64/i386. 230 // x86_64/i386 or desktop ARM.
201 // A GPU broker policy is the same as a GPU policy with open and 231 // A GPU broker policy is the same as a GPU policy with open and
202 // openat allowed. 232 // openat allowed.
203 ErrorCode GpuBrokerProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, 233 ErrorCode GpuBrokerProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox,
204 int sysno) const { 234 int sysno) const {
205 switch (sysno) { 235 switch (sysno) {
206 case __NR_access: 236 case __NR_access:
207 case __NR_open: 237 case __NR_open:
208 case __NR_openat: 238 case __NR_openat:
209 return ErrorCode(ErrorCode::ERR_ALLOWED); 239 return ErrorCode(ErrorCode::ERR_ALLOWED);
210 default: 240 default:
211 return GpuProcessPolicy::EvaluateSyscall(sandbox, sysno); 241 return GpuProcessPolicy::EvaluateSyscall(sandbox, sysno);
212 } 242 }
213 } 243 }
214 244
215 class ArmGpuProcessPolicy : public GpuProcessPolicy { 245 // This policy is for Chrome OS ARM.
246 class CrosArmGpuProcessPolicy : public GpuProcessPolicy {
216 public: 247 public:
217 explicit ArmGpuProcessPolicy(void* broker_process, bool allow_shmat) 248 explicit CrosArmGpuProcessPolicy(bool allow_shmat)
218 : GpuProcessPolicy(broker_process), allow_shmat_(allow_shmat) {} 249 : allow_shmat_(allow_shmat) {}
219 virtual ~ArmGpuProcessPolicy() {} 250 virtual ~CrosArmGpuProcessPolicy() {}
220 251
221 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, 252 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
222 int system_call_number) const OVERRIDE; 253 int system_call_number) const OVERRIDE;
254 virtual bool PreSandboxHook() OVERRIDE;
223 255
224 private: 256 private:
225 const bool allow_shmat_; // Allow shmat(2). 257 const bool allow_shmat_; // Allow shmat(2).
226 DISALLOW_COPY_AND_ASSIGN(ArmGpuProcessPolicy); 258 DISALLOW_COPY_AND_ASSIGN(CrosArmGpuProcessPolicy);
227 }; 259 };
228 260
229 // Generic ARM GPU process sandbox, inheriting from GpuProcessPolicy. 261 // Generic ARM GPU process sandbox, inheriting from GpuProcessPolicy.
230 ErrorCode ArmGpuProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, 262 ErrorCode CrosArmGpuProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox,
231 int sysno) const { 263 int sysno) const {
232 #if defined(__arm__) 264 #if defined(__arm__)
233 if (allow_shmat_ && sysno == __NR_shmat) 265 if (allow_shmat_ && sysno == __NR_shmat)
234 return ErrorCode(ErrorCode::ERR_ALLOWED); 266 return ErrorCode(ErrorCode::ERR_ALLOWED);
235 #endif // defined(__arm__) 267 #endif // defined(__arm__)
236 268
237 switch (sysno) { 269 switch (sysno) {
238 #if defined(__arm__) 270 #if defined(__arm__)
239 // ARM GPU sandbox is started earlier so we need to allow networking 271 // ARM GPU sandbox is started earlier so we need to allow networking
240 // in the sandbox. 272 // in the sandbox.
241 case __NR_connect: 273 case __NR_connect:
(...skipping 12 matching lines...) Expand all
254 #endif // defined(__arm__) 286 #endif // defined(__arm__)
255 default: 287 default:
256 if (SyscallSets::IsAdvancedScheduler(sysno)) 288 if (SyscallSets::IsAdvancedScheduler(sysno))
257 return ErrorCode(ErrorCode::ERR_ALLOWED); 289 return ErrorCode(ErrorCode::ERR_ALLOWED);
258 290
259 // Default to the generic GPU policy. 291 // Default to the generic GPU policy.
260 return GpuProcessPolicy::EvaluateSyscall(sandbox, sysno); 292 return GpuProcessPolicy::EvaluateSyscall(sandbox, sysno);
261 } 293 }
262 } 294 }
263 295
264 class ArmGpuBrokerProcessPolicy : public ArmGpuProcessPolicy { 296 bool CrosArmGpuProcessPolicy::PreSandboxHook() {
297 DCHECK(IsChromeOS() && IsArchitectureArm());
298 // Create a new broker process.
299 BrokerProcess* broker_process_temp = NULL;
300 DCHECK(!broker_process());
301
302 std::vector<std::string> read_whitelist_extra;
303 std::vector<std::string> write_whitelist_extra;
304 // Add arm specific files to whitelist in the broker.
Jorge Lucangeli Obes 2013/12/12 21:37:21 ARM-specific
jln (very slow on Chromium) 2013/12/12 22:15:14 Done.
305
306 AddArmGpuWhitelist(&read_whitelist_extra, &write_whitelist_extra);
307 InitGpuBrokerProcess(EnableArmGpuBrokerPolicyCallback,
308 true /* for ChromeOS ARM */,
309 read_whitelist_extra,
310 write_whitelist_extra,
311 &broker_process_temp);
312 set_broker_process(broker_process_temp);
313
314 // Preload the Mali library.
315 dlopen("/usr/lib/libmali.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
316
317 // Preload the Tegra libraries.
318 dlopen("/usr/lib/libnvrm.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
319 dlopen("/usr/lib/libnvrm_graphics.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
320 dlopen("/usr/lib/libnvos.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
321 dlopen("/usr/lib/libnvddk_2d.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
322 dlopen("/usr/lib/libardrv_dynamic.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
323 dlopen("/usr/lib/libnvwsi.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
324 dlopen("/usr/lib/libnvglsi.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
325 dlopen("/usr/lib/libcgdrv.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
326
327 return true;
328 }
329
330 class CrosArmGpuBrokerProcessPolicy : public CrosArmGpuProcessPolicy {
265 public: 331 public:
266 ArmGpuBrokerProcessPolicy() : ArmGpuProcessPolicy(NULL, false) {} 332 CrosArmGpuBrokerProcessPolicy() : CrosArmGpuProcessPolicy(false) {}
267 virtual ~ArmGpuBrokerProcessPolicy() {} 333 virtual ~CrosArmGpuBrokerProcessPolicy() {}
268 334
269 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, 335 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
270 int system_call_number) const OVERRIDE; 336 int system_call_number) const OVERRIDE;
271 337
272 private: 338 private:
273 DISALLOW_COPY_AND_ASSIGN(ArmGpuBrokerProcessPolicy); 339 DISALLOW_COPY_AND_ASSIGN(CrosArmGpuBrokerProcessPolicy);
274 }; 340 };
275 341
276 // A GPU broker policy is the same as a GPU policy with open and 342 // A GPU broker policy is the same as a GPU policy with open and
277 // openat allowed. 343 // openat allowed.
278 ErrorCode ArmGpuBrokerProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, 344 ErrorCode CrosArmGpuBrokerProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox,
279 int sysno) const { 345 int sysno) const {
280 switch (sysno) { 346 switch (sysno) {
281 case __NR_access: 347 case __NR_access:
282 case __NR_open: 348 case __NR_open:
283 case __NR_openat: 349 case __NR_openat:
284 return ErrorCode(ErrorCode::ERR_ALLOWED); 350 return ErrorCode(ErrorCode::ERR_ALLOWED);
285 default: 351 default:
286 return ArmGpuProcessPolicy::EvaluateSyscall(sandbox, sysno); 352 return CrosArmGpuProcessPolicy::EvaluateSyscall(sandbox, sysno);
287 }
288 }
289
290 // Policy for renderer and worker processes.
291 // TODO(jln): move to renderer/
292
293 class RendererOrWorkerProcessPolicy : public SandboxBPFBasePolicy {
294 public:
295 RendererOrWorkerProcessPolicy() {}
296 virtual ~RendererOrWorkerProcessPolicy() {}
297
298 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
299 int system_call_number) const OVERRIDE;
300
301 private:
302 DISALLOW_COPY_AND_ASSIGN(RendererOrWorkerProcessPolicy);
303 };
304
305 ErrorCode RendererOrWorkerProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox,
306 int sysno) const {
307 switch (sysno) {
308 case __NR_clone:
309 return sandbox::RestrictCloneToThreadsAndEPERMFork(sandbox);
310 case __NR_ioctl:
311 return sandbox::RestrictIoctl(sandbox);
312 case __NR_prctl:
313 return sandbox::RestrictPrctl(sandbox);
314 // Allow the system calls below.
315 case __NR_fdatasync:
316 case __NR_fsync:
317 case __NR_getpriority:
318 #if defined(__i386__) || defined(__x86_64__)
319 case __NR_getrlimit:
320 #endif
321 #if defined(__i386__) || defined(__arm__)
322 case __NR_ugetrlimit:
323 #endif
324 case __NR_mremap: // See crbug.com/149834.
325 case __NR_pread64:
326 case __NR_pwrite64:
327 case __NR_sched_getaffinity:
328 case __NR_sched_get_priority_max:
329 case __NR_sched_get_priority_min:
330 case __NR_sched_getparam:
331 case __NR_sched_getscheduler:
332 case __NR_sched_setscheduler:
333 case __NR_setpriority:
334 case __NR_sysinfo:
335 case __NR_times:
336 case __NR_uname:
337 return ErrorCode(ErrorCode::ERR_ALLOWED);
338 case __NR_prlimit64:
339 return ErrorCode(EPERM); // See crbug.com/160157.
340 default:
341 if (IsUsingToolKitGtk()) {
342 #if defined(__x86_64__) || defined(__arm__)
343 if (SyscallSets::IsSystemVSharedMemory(sysno))
344 return ErrorCode(ErrorCode::ERR_ALLOWED);
345 #endif
346 #if defined(__i386__)
347 if (SyscallSets::IsSystemVIpc(sysno))
348 return ErrorCode(ErrorCode::ERR_ALLOWED);
349 #endif
350 }
351
352 // Default on the content baseline policy.
353 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno);
354 }
355 }
356
357 // Policy for PPAPI plugins.
358 // TODO(jln): move to ppapi_plugin/.
359 class FlashProcessPolicy : public SandboxBPFBasePolicy {
360 public:
361 FlashProcessPolicy() {}
362 virtual ~FlashProcessPolicy() {}
363
364 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
365 int system_call_number) const OVERRIDE;
366
367 private:
368 DISALLOW_COPY_AND_ASSIGN(FlashProcessPolicy);
369 };
370
371 ErrorCode FlashProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox,
372 int sysno) const {
373 switch (sysno) {
374 case __NR_clone:
375 return sandbox::RestrictCloneToThreadsAndEPERMFork(sandbox);
376 case __NR_pread64:
377 case __NR_pwrite64:
378 case __NR_sched_get_priority_max:
379 case __NR_sched_get_priority_min:
380 case __NR_sched_getaffinity:
381 case __NR_sched_getparam:
382 case __NR_sched_getscheduler:
383 case __NR_sched_setscheduler:
384 case __NR_times:
385 return ErrorCode(ErrorCode::ERR_ALLOWED);
386 case __NR_ioctl:
387 return ErrorCode(ENOTTY); // Flash Access.
388 default:
389 if (IsUsingToolKitGtk()) {
390 #if defined(__x86_64__) || defined(__arm__)
391 if (SyscallSets::IsSystemVSharedMemory(sysno))
392 return ErrorCode(ErrorCode::ERR_ALLOWED);
393 #endif
394 #if defined(__i386__)
395 if (SyscallSets::IsSystemVIpc(sysno))
396 return ErrorCode(ErrorCode::ERR_ALLOWED);
397 #endif
398 }
399
400 // Default on the baseline policy.
401 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno);
402 }
403 }
404
405 class BlacklistDebugAndNumaPolicy : public SandboxBPFBasePolicy {
406 public:
407 BlacklistDebugAndNumaPolicy() {}
408 virtual ~BlacklistDebugAndNumaPolicy() {}
409
410 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
411 int system_call_number) const OVERRIDE;
412
413 private:
414 DISALLOW_COPY_AND_ASSIGN(BlacklistDebugAndNumaPolicy);
415 };
416
417 ErrorCode BlacklistDebugAndNumaPolicy::EvaluateSyscall(SandboxBPF* sandbox,
418 int sysno) const {
419 if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
420 // TODO(jln) we should not have to do that in a trivial policy.
421 return ErrorCode(ENOSYS);
422 }
423 if (SyscallSets::IsDebug(sysno) || SyscallSets::IsNuma(sysno))
424 return sandbox->Trap(sandbox::CrashSIGSYS_Handler, NULL);
425
426 return ErrorCode(ErrorCode::ERR_ALLOWED);
427 }
428
429 class AllowAllPolicy : public SandboxBPFBasePolicy {
430 public:
431 AllowAllPolicy() {}
432 virtual ~AllowAllPolicy() {}
433
434 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
435 int system_call_number) const OVERRIDE;
436
437 private:
438 DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
439 };
440
441 // Allow all syscalls.
442 // This will still deny x32 or IA32 calls in 64 bits mode or
443 // 64 bits system calls in compatibility mode.
444 ErrorCode AllowAllPolicy::EvaluateSyscall(SandboxBPF*, int sysno) const {
445 if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
446 // TODO(jln) we should not have to do that in a trivial policy.
447 return ErrorCode(ENOSYS);
448 } else {
449 return ErrorCode(ErrorCode::ERR_ALLOWED);
450 }
451 }
452
453 // If a BPF policy is engaged for |process_type|, run a few sanity checks.
454 void RunSandboxSanityChecks(const std::string& process_type) {
455 if (process_type == switches::kRendererProcess ||
456 process_type == switches::kWorkerProcess ||
457 process_type == switches::kGpuProcess ||
458 process_type == switches::kPpapiPluginProcess) {
459 int syscall_ret;
460 errno = 0;
461
462 // Without the sandbox, this would EBADF.
463 syscall_ret = fchmod(-1, 07777);
464 CHECK_EQ(-1, syscall_ret);
465 CHECK_EQ(EPERM, errno);
466
467 // Run most of the sanity checks only in DEBUG mode to avoid a perf.
468 // impact.
469 #if !defined(NDEBUG)
470 // open() must be restricted.
471 syscall_ret = open("/etc/passwd", O_RDONLY);
472 CHECK_EQ(-1, syscall_ret);
473 CHECK_EQ(SandboxBPFBasePolicy::GetFSDeniedErrno(), errno);
474
475 // We should never allow the creation of netlink sockets.
476 syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0);
477 CHECK_EQ(-1, syscall_ret);
478 CHECK_EQ(EPERM, errno);
479 #endif // !defined(NDEBUG)
480 } 353 }
481 } 354 }
482 355
483 bool EnableGpuBrokerPolicyCallback() { 356 bool EnableGpuBrokerPolicyCallback() {
484 StartSandboxWithPolicy(new GpuBrokerProcessPolicy); 357 return SandboxSeccompBPF::StartSandboxWithExternalPolicy(
485 return true; 358 scoped_ptr<sandbox::SandboxBPFPolicy>(new GpuBrokerProcessPolicy));
486 } 359 }
487 360
488 bool EnableArmGpuBrokerPolicyCallback() { 361 bool EnableArmGpuBrokerPolicyCallback() {
489 StartSandboxWithPolicy(new ArmGpuBrokerProcessPolicy); 362 return SandboxSeccompBPF::StartSandboxWithExternalPolicy(
490 return true; 363 scoped_ptr<sandbox::SandboxBPFPolicy>(new CrosArmGpuBrokerProcessPolicy));
491 } 364 }
492 365
493 // Files needed by the ARM GPU userspace.
494 static const char kLibGlesPath[] = "/usr/lib/libGLESv2.so.2";
495 static const char kLibEglPath[] = "/usr/lib/libEGL.so.1";
496
497 void AddArmMaliGpuWhitelist(std::vector<std::string>* read_whitelist, 366 void AddArmMaliGpuWhitelist(std::vector<std::string>* read_whitelist,
498 std::vector<std::string>* write_whitelist) { 367 std::vector<std::string>* write_whitelist) {
499 // Device file needed by the ARM GPU userspace. 368 // Device file needed by the ARM GPU userspace.
500 static const char kMali0Path[] = "/dev/mali0"; 369 static const char kMali0Path[] = "/dev/mali0";
501 370
502 // Devices needed for video decode acceleration on ARM. 371 // Devices needed for video decode acceleration on ARM.
503 static const char kDevMfcDecPath[] = "/dev/mfc-dec"; 372 static const char kDevMfcDecPath[] = "/dev/mfc-dec";
504 static const char kDevGsc1Path[] = "/dev/gsc1"; 373 static const char kDevGsc1Path[] = "/dev/gsc1";
505 374
506 // Devices needed for video encode acceleration on ARM. 375 // Devices needed for video encode acceleration on ARM.
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
545 write_whitelist->push_back(kDevTegraSemaPath); 414 write_whitelist->push_back(kDevTegraSemaPath);
546 } 415 }
547 416
548 void AddArmGpuWhitelist(std::vector<std::string>* read_whitelist, 417 void AddArmGpuWhitelist(std::vector<std::string>* read_whitelist,
549 std::vector<std::string>* write_whitelist) { 418 std::vector<std::string>* write_whitelist) {
550 // On ARM we're enabling the sandbox before the X connection is made, 419 // On ARM we're enabling the sandbox before the X connection is made,
551 // so we need to allow access to |.Xauthority|. 420 // so we need to allow access to |.Xauthority|.
552 static const char kXAuthorityPath[] = "/home/chronos/.Xauthority"; 421 static const char kXAuthorityPath[] = "/home/chronos/.Xauthority";
553 static const char kLdSoCache[] = "/etc/ld.so.cache"; 422 static const char kLdSoCache[] = "/etc/ld.so.cache";
554 423
424 // Files needed by the ARM GPU userspace.
425 static const char kLibGlesPath[] = "/usr/lib/libGLESv2.so.2";
426 static const char kLibEglPath[] = "/usr/lib/libEGL.so.1";
427
555 read_whitelist->push_back(kXAuthorityPath); 428 read_whitelist->push_back(kXAuthorityPath);
556 read_whitelist->push_back(kLdSoCache); 429 read_whitelist->push_back(kLdSoCache);
557 read_whitelist->push_back(kLibGlesPath); 430 read_whitelist->push_back(kLibGlesPath);
558 read_whitelist->push_back(kLibEglPath); 431 read_whitelist->push_back(kLibEglPath);
559 432
560 AddArmMaliGpuWhitelist(read_whitelist, write_whitelist); 433 AddArmMaliGpuWhitelist(read_whitelist, write_whitelist);
561 AddArmTegraGpuWhitelist(read_whitelist, write_whitelist); 434 AddArmTegraGpuWhitelist(read_whitelist, write_whitelist);
562 } 435 }
563 436
564 // Start a broker process to handle open() inside the sandbox. 437 // Start a broker process to handle open() inside the sandbox.
565 void InitGpuBrokerProcess(bool for_chromeos_arm, 438 // |broker_sandboxer_callback| is a callback that will enable a suitable
439 // sandbox for the broker process itself.
440 // |read_whitelist_extra| and |write_whitelist_extra| are lists of file
441 // names that should be whitelisted by the broker process, in addition to
442 // the basic ones.
443 void InitGpuBrokerProcess(bool (*broker_sandboxer_callback)(void),
444 bool for_chromeos_arm,
445 const std::vector<std::string>& read_whitelist_extra,
446 const std::vector<std::string>& write_whitelist_extra,
566 BrokerProcess** broker_process) { 447 BrokerProcess** broker_process) {
567 static const char kDriRcPath[] = "/etc/drirc"; 448 static const char kDriRcPath[] = "/etc/drirc";
568 static const char kDriCard0Path[] = "/dev/dri/card0"; 449 static const char kDriCard0Path[] = "/dev/dri/card0";
569 450
570 CHECK(broker_process); 451 CHECK(broker_process);
571 CHECK(*broker_process == NULL); 452 CHECK(*broker_process == NULL);
572 453
573 bool (*sandbox_callback)(void) = NULL;
574
575 // All GPU process policies need these files brokered out. 454 // All GPU process policies need these files brokered out.
576 std::vector<std::string> read_whitelist; 455 std::vector<std::string> read_whitelist;
577 read_whitelist.push_back(kDriCard0Path); 456 read_whitelist.push_back(kDriCard0Path);
578 read_whitelist.push_back(kDriRcPath); 457 read_whitelist.push_back(kDriRcPath);
458 // Add eventual extra files from read_whitelist_extra.
459 read_whitelist.insert(read_whitelist.end(),
460 read_whitelist_extra.begin(),
461 read_whitelist_extra.end());
579 462
580 std::vector<std::string> write_whitelist; 463 std::vector<std::string> write_whitelist;
581 write_whitelist.push_back(kDriCard0Path); 464 write_whitelist.push_back(kDriCard0Path);
582 465 // Add eventual extra files from write_whitelist_extra.
583 if (for_chromeos_arm) { 466 write_whitelist.insert(write_whitelist.end(),
584 // We shouldn't be using this policy on non-ARM architectures. 467 write_whitelist_extra.begin(),
585 DCHECK(IsArchitectureArm()); 468 write_whitelist_extra.end());
586 AddArmGpuWhitelist(&read_whitelist, &write_whitelist);
587 sandbox_callback = EnableArmGpuBrokerPolicyCallback;
588 } else {
589 sandbox_callback = EnableGpuBrokerPolicyCallback;
590 }
591 469
592 *broker_process = new BrokerProcess(SandboxBPFBasePolicy::GetFSDeniedErrno(), 470 *broker_process = new BrokerProcess(SandboxBPFBasePolicy::GetFSDeniedErrno(),
593 read_whitelist, 471 read_whitelist,
594 write_whitelist); 472 write_whitelist);
595 // Initialize the broker process and give it a sandbox callback. 473 // Initialize the broker process and give it a sandbox callback.
596 CHECK((*broker_process)->Init(sandbox_callback)); 474 CHECK((*broker_process)->Init(broker_sandboxer_callback));
597 }
598
599 // Warms up/preloads resources needed by the policies.
600 // Eventually start a broker process and return it in broker_process.
601 void WarmupPolicy(bool chromeos_arm_gpu,
602 BrokerProcess** broker_process) {
603 if (!chromeos_arm_gpu) {
604 // Create a new broker process.
605 InitGpuBrokerProcess(false /* not for ChromeOS ARM */, broker_process);
606
607 if (IsArchitectureX86_64() || IsArchitectureI386()) {
608 // Accelerated video decode dlopen()'s some shared objects
609 // inside the sandbox, so preload them now.
610 if (IsAcceleratedVideoDecodeEnabled()) {
611 const char* I965DrvVideoPath = NULL;
612
613 if (IsArchitectureX86_64()) {
614 I965DrvVideoPath = "/usr/lib64/va/drivers/i965_drv_video.so";
615 } else if (IsArchitectureI386()) {
616 I965DrvVideoPath = "/usr/lib/va/drivers/i965_drv_video.so";
617 }
618
619 dlopen(I965DrvVideoPath, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
620 dlopen("libva.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
621 dlopen("libva-x11.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
622 }
623 }
624 } else {
625 // ChromeOS ARM GPU policy.
626 // Create a new broker process.
627 InitGpuBrokerProcess(true /* for ChromeOS ARM */, broker_process);
628
629 // Preload the Mali library.
630 dlopen("/usr/lib/libmali.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
631
632 // Preload the Tegra libraries.
633 dlopen("/usr/lib/libnvrm.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
634 dlopen("/usr/lib/libnvrm_graphics.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
635 dlopen("/usr/lib/libnvos.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
636 dlopen("/usr/lib/libnvddk_2d.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
637 dlopen("/usr/lib/libardrv_dynamic.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
638 dlopen("/usr/lib/libnvwsi.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
639 dlopen("/usr/lib/libnvglsi.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
640 dlopen("/usr/lib/libcgdrv.so", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE);
641 }
642 }
643
644 void StartGpuProcessSandbox(const CommandLine& command_line,
645 const std::string& process_type) {
646 bool chromeos_arm_gpu = false;
647 bool allow_sysv_shm = false;
648
649 if (process_type == switches::kGpuProcess) {
650 // On Chrome OS ARM, we need a specific GPU process policy.
651 if (IsChromeOS() && IsArchitectureArm()) {
652 chromeos_arm_gpu = true;
653 if (command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm)) {
654 allow_sysv_shm = true;
655 }
656 }
657 }
658
659 // This should never be destroyed, as after the sandbox is started it is
660 // vital to the process. Ownership is transfered to the policies and then to
661 // the BPF sandbox which will keep it around to service SIGSYS traps from the
662 // kernel.
663 BrokerProcess* broker_process = NULL;
664 // Warm up resources needed by the policy we're about to enable and
665 // eventually start a broker process.
666 WarmupPolicy(chromeos_arm_gpu, &broker_process);
667
668 scoped_ptr<SandboxBPFBasePolicy> gpu_policy;
669 if (chromeos_arm_gpu) {
670 gpu_policy.reset(new ArmGpuProcessPolicy(broker_process, allow_sysv_shm));
671 } else {
672 gpu_policy.reset(new GpuProcessPolicy(broker_process));
673 }
674 StartSandboxWithPolicy(gpu_policy.release());
675 }
676
677 // This function takes ownership of |policy|.
678 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy) {
679 // Starting the sandbox is a one-way operation. The kernel doesn't allow
680 // us to unload a sandbox policy after it has been started. Nonetheless,
681 // in order to make the use of the "Sandbox" object easier, we allow for
682 // the object to be destroyed after the sandbox has been started. Note that
683 // doing so does not stop the sandbox.
684 SandboxBPF sandbox;
685 sandbox.SetSandboxPolicy(policy);
686 sandbox.StartSandbox();
687 }
688
689 void StartNonGpuSandbox(const std::string& process_type) {
690 scoped_ptr<SandboxBPFBasePolicy> policy;
691
692 if (process_type == switches::kRendererProcess ||
693 process_type == switches::kWorkerProcess) {
694 policy.reset(new RendererOrWorkerProcessPolicy);
695 } else if (process_type == switches::kPpapiPluginProcess) {
696 policy.reset(new FlashProcessPolicy);
697 } else if (process_type == switches::kUtilityProcess) {
698 policy.reset(new BlacklistDebugAndNumaPolicy);
699 } else {
700 NOTREACHED();
701 policy.reset(new AllowAllPolicy);
702 }
703
704 StartSandboxWithPolicy(policy.release());
705 }
706
707 // Initialize the seccomp-bpf sandbox.
708 bool StartBPFSandbox(const CommandLine& command_line,
709 const std::string& process_type) {
710
711 if (process_type == switches::kGpuProcess) {
712 StartGpuProcessSandbox(command_line, process_type);
713 } else {
714 StartNonGpuSandbox(process_type);
715 }
716
717 RunSandboxSanityChecks(process_type);
718 return true;
719 } 475 }
720 476
721 } // namespace 477 } // namespace
722 478
723 #endif // SECCOMP_BPF_SANDBOX 479 scoped_ptr<SandboxBPFBasePolicy> GetGpuProcessSandbox() {
480 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
481 bool allow_sysv_shm = false;
482 if (command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm)) {
483 DCHECK(IsArchitectureArm());
484 allow_sysv_shm = true;
485 }
724 486
725 // Is seccomp BPF globally enabled? 487 if (IsChromeOS() && IsArchitectureArm()) {
726 bool SandboxSeccompBPF::IsSeccompBPFDesired() { 488 return scoped_ptr<SandboxBPFBasePolicy>(
727 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 489 new CrosArmGpuProcessPolicy(allow_sysv_shm));
728 if (!command_line.HasSwitch(switches::kNoSandbox) &&
729 !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) {
730 return true;
731 } else { 490 } else {
732 return false; 491 return scoped_ptr<SandboxBPFBasePolicy>(new GpuProcessPolicy);
733 } 492 }
734 } 493 }
735 494
736 bool SandboxSeccompBPF::ShouldEnableSeccompBPF(
737 const std::string& process_type) {
738 #if defined(SECCOMP_BPF_SANDBOX)
739 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
740 if (process_type == switches::kGpuProcess)
741 return !command_line.HasSwitch(switches::kDisableGpuSandbox);
742
743 return true;
744 #endif // SECCOMP_BPF_SANDBOX
745 return false;
746 }
747
748 bool SandboxSeccompBPF::SupportsSandbox() {
749 #if defined(SECCOMP_BPF_SANDBOX)
750 // TODO(jln): pass the saved proc_fd_ from the LinuxSandbox singleton
751 // here.
752 SandboxBPF::SandboxStatus bpf_sandbox_status =
753 SandboxBPF::SupportsSeccompSandbox(-1);
754 // Kernel support is what we are interested in here. Other status
755 // such as STATUS_UNAVAILABLE (has threads) still indicate kernel support.
756 // We make this a negative check, since if there is a bug, we would rather
757 // "fail closed" (expect a sandbox to be available and try to start it).
758 if (bpf_sandbox_status != SandboxBPF::STATUS_UNSUPPORTED) {
759 return true;
760 }
761 #endif
762 return false;
763 }
764
765 bool SandboxSeccompBPF::StartSandbox(const std::string& process_type) {
766 #if defined(SECCOMP_BPF_SANDBOX)
767 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
768
769 if (IsSeccompBPFDesired() && // Global switches policy.
770 ShouldEnableSeccompBPF(process_type) && // Process-specific policy.
771 SupportsSandbox()) {
772 // If the kernel supports the sandbox, and if the command line says we
773 // should enable it, enable it or die.
774 bool started_sandbox = StartBPFSandbox(command_line, process_type);
775 CHECK(started_sandbox);
776 return true;
777 }
778 #endif
779 return false;
780 }
781
782 bool SandboxSeccompBPF::StartSandboxWithExternalPolicy(
783 scoped_ptr<sandbox::SandboxBPFPolicy> policy) {
784 #if defined(SECCOMP_BPF_SANDBOX)
785 if (IsSeccompBPFDesired() && SupportsSandbox()) {
786 CHECK(policy);
787 StartSandboxWithPolicy(policy.release());
788 return true;
789 }
790 #endif // defined(SECCOMP_BPF_SANDBOX)
791 return false;
792 }
793
794 scoped_ptr<sandbox::SandboxBPFPolicy>
795 SandboxSeccompBPF::GetBaselinePolicy() {
796 #if defined(SECCOMP_BPF_SANDBOX)
797 return scoped_ptr<sandbox::SandboxBPFPolicy>(new BaselinePolicy);
798 #else
799 return scoped_ptr<sandbox::SandboxBPFPolicy>();
800 #endif // defined(SECCOMP_BPF_SANDBOX)
801 }
802
803 } // namespace content 495 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698