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

Side by Side Diff: chrome/browser/zygote_main_linux.cc

Issue 165011: Linux: don't use GOT patching to intercept localtime(_r) (Closed)
Patch Set: ... Created 11 years, 4 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 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 <dlfcn.h>
5 #include <unistd.h> 6 #include <unistd.h>
6 #include <sys/epoll.h> 7 #include <sys/epoll.h>
7 #include <sys/types.h> 8 #include <sys/types.h>
8 #include <sys/socket.h> 9 #include <sys/socket.h>
9 #include <sys/signal.h> 10 #include <sys/signal.h>
10 #include <sys/prctl.h> 11 #include <sys/prctl.h>
11 #include <sys/wait.h> 12 #include <sys/wait.h>
12 13
13 #include "base/basictypes.h" 14 #include "base/basictypes.h"
14 #include "base/command_line.h" 15 #include "base/command_line.h"
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 199
199 error: 200 error:
200 LOG(WARNING) << "Error parsing fork request from browser"; 201 LOG(WARNING) << "Error parsing fork request from browser";
201 for (std::vector<int>::const_iterator 202 for (std::vector<int>::const_iterator
202 i = fds.begin(); i != fds.end(); ++i) 203 i = fds.begin(); i != fds.end(); ++i)
203 close(*i); 204 close(*i);
204 return false; 205 return false;
205 } 206 }
206 }; 207 };
207 208
208 // Patched dynamic symbol wrapper functions... 209 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output,
209 namespace sandbox_wrapper { 210 char* timezone_out,
210 211 size_t timezone_out_len) {
211 void do_localtime(time_t input, struct tm* output, char* timezone_out,
212 size_t timezone_out_len) {
213 Pickle request; 212 Pickle request;
214 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME); 213 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME);
215 request.WriteString( 214 request.WriteString(
216 std::string(reinterpret_cast<char*>(&input), sizeof(input))); 215 std::string(reinterpret_cast<char*>(&input), sizeof(input)));
217 216
218 uint8_t reply_buf[512]; 217 uint8_t reply_buf[512];
219 const ssize_t r = base::SendRecvMsg( 218 const ssize_t r = base::SendRecvMsg(
220 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, request); 219 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, request);
221 if (r == -1) { 220 if (r == -1) {
222 memset(output, 0, sizeof(struct tm)); 221 memset(output, 0, sizeof(struct tm));
(...skipping 14 matching lines...) Expand all
237 if (timezone_out_len) { 236 if (timezone_out_len) {
238 const size_t copy_len = std::min(timezone_out_len - 1, timezone.size()); 237 const size_t copy_len = std::min(timezone_out_len - 1, timezone.size());
239 memcpy(timezone_out, timezone.data(), copy_len); 238 memcpy(timezone_out, timezone.data(), copy_len);
240 timezone_out[copy_len] = 0; 239 timezone_out[copy_len] = 0;
241 output->tm_zone = timezone_out; 240 output->tm_zone = timezone_out;
242 } else { 241 } else {
243 output->tm_zone = NULL; 242 output->tm_zone = NULL;
244 } 243 }
245 } 244 }
246 245
246 static bool g_am_zygote_or_renderer = false;
247
248 // Sandbox interception of libc calls.
249 //
250 // Because we are running in a sandbox certain libc calls will fail (localtime
251 // being the motivating example - it needs to read /etc/localtime). We need to
252 // intercept these calls and proxy them to the browser. However, these calls
253 // may come from us or from our libraries. In some cases we can't just change
254 // our code.
255 //
256 // It's for these cases that we have the following setup:
257 //
258 // We define global functions for those functions which we wish to override.
259 // Since we will be first in the dynamic resolution order, the dynamic linker
260 // will point callers to our versions of these functions. However, we have the
261 // same binary for both the browser and the renderers, which means that our
262 // overrides will apply in the browser too.
263 //
264 // The global |g_am_zygote_or_renderer| is true iff we are in a zygote or
265 // renderer process. It's set in ZygoteMain and inherited by the renderers when
266 // they fork. (This means that it'll be incorrect for global constructor
267 // functions and before ZygoteMain is called - beware).
268 //
269 // Our replacement functions can check this global and either proxy
270 // the call to the browser over the sandbox IPC
271 // (http://code.google.com/p/chromium/wiki/LinuxSandboxIPC) or they can use
272 // dlsym with RTLD_NEXT to resolve the symbol, ignoring any symbols in the
273 // current module.
274 //
275 // Other avenues:
276 //
277 // Our first attempt involved some assembly to patch the GOT of the current
278 // module. This worked, but was platform specific and doesn't catch the case
279 // where a library makes a call rather than current module.
280 //
281 // We also considered patching the function in place, but this would again by
282 // platform specific and the above technique seems to work well enough.
283
247 struct tm* localtime(const time_t* timep) { 284 struct tm* localtime(const time_t* timep) {
248 static struct tm time_struct; 285 if (g_am_zygote_or_renderer) {
249 static char timezone_string[64]; 286 static struct tm time_struct;
250 do_localtime(*timep, &time_struct, timezone_string, sizeof(timezone_string)); 287 static char timezone_string[64];
251 return &time_struct; 288 ProxyLocaltimeCallToBrowser(*timep, &time_struct, timezone_string,
289 sizeof(timezone_string));
290 return &time_struct;
291 } else {
292 typedef struct tm* (*LocaltimeFunction)(const time_t* timep);
293 static LocaltimeFunction libc_localtime;
294 if (!libc_localtime)
295 libc_localtime = (LocaltimeFunction) dlsym(RTLD_NEXT, "localtime");
296
297 return libc_localtime(timep);
298 }
252 } 299 }
253 300
254 struct tm* localtime_r(const time_t* timep, struct tm* result) { 301 struct tm* localtime_r(const time_t* timep, struct tm* result) {
255 do_localtime(*timep, result, NULL, 0); 302 if (g_am_zygote_or_renderer) {
256 return result; 303 ProxyLocaltimeCallToBrowser(*timep, result, NULL, 0);
304 return result;
305 } else {
306 typedef struct tm* (*LocaltimeRFunction)(const time_t* timep,
307 struct tm* result);
308 static LocaltimeRFunction libc_localtime_r;
309 if (!libc_localtime_r)
310 libc_localtime_r = (LocaltimeRFunction) dlsym(RTLD_NEXT, "localtime_r");
311
312 return libc_localtime_r(timep, result);
313 }
257 } 314 }
258 315
259 } // namespace sandbox_wrapper
260
261 /* On IA-32, function calls which need to be resolved by the dynamic linker are
262 * directed to the producure linking table (PLT). Each PLT entry contains code
263 * which jumps (indirectly) via the global offset table (GOT):
264 * Dump of assembler code for function f@plt:
265 * 0x0804830c <f@plt+0>: jmp *0x804a004 # GOT indirect jump
266 * 0x08048312 <f@plt+6>: push $0x8
267 * 0x08048317 <f@plt+11>: jmp 0x80482ec <_init+48>
268 *
269 * At the beginning of a process's lifetime, the GOT entry jumps back to
270 * <f@plt+6> end then enters the dynamic linker. Once the symbol has been
271 * resolved, the GOT entry is patched so that future calls go directly to the
272 * resolved function.
273 *
274 * This macro finds the PLT entry for a given symbol, |symbol|, and reads the
275 * GOT entry address from the first instruction. It then patches that address
276 * with the address of a replacement function, |replacement|.
277 */
278 #define PATCH_GLOBAL_OFFSET_TABLE(symbol, replacement) \
279 /* First, get the current instruction pointer since the PLT address */ \
280 /* is IP relative */ \
281 asm ("call 0f\n" \
282 "0: pop %%ecx\n" \
283 /* Move the IP relative address of the PLT entry into EAX */ \
284 "mov $" #symbol "@plt,%%eax\n" \
285 /* Add EAX to ECX to get an absolute entry */ \
286 "add %%eax,%%ecx\n" \
287 /* The value in ECX was relative to the add instruction, however, */ \
288 /* the IP value was that of the pop. The pop and mov take 6 */ \
289 /* bytes, so adding 6 gets us the correct address for the PLT. The */ \
290 /* first instruction at the PLT is FF 25 <abs address>, so we skip 2 */ \
291 /* bytes to get to the address. 6 + 2 = 8: */ \
292 "movl 8(%%ecx),%%ecx\n" \
293 /* Now ECX contains the address of the GOT entry, we poke our */ \
294 /* replacement function in there: */ \
295 "movl %0,(%%ecx)\n" \
296 : /* no output */ \
297 : "r" (replacement) \
298 : "memory", "%eax", "%ecx");
299
300 static bool MaybeEnterChroot() { 316 static bool MaybeEnterChroot() {
301 const char* const sandbox_fd_string = getenv("SBX_D"); 317 const char* const sandbox_fd_string = getenv("SBX_D");
302 if (sandbox_fd_string) { 318 if (sandbox_fd_string) {
303 // The SUID sandbox sets this environment variable to a file descriptor 319 // The SUID sandbox sets this environment variable to a file descriptor
304 // over which we can signal that we have completed our startup and can be 320 // over which we can signal that we have completed our startup and can be
305 // chrooted. 321 // chrooted.
306 322
307 char* endptr; 323 char* endptr;
308 const long fd_long = strtol(sandbox_fd_string, &endptr, 10); 324 const long fd_long = strtol(sandbox_fd_string, &endptr, 10);
309 if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) 325 if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX)
310 return false; 326 return false;
311 const int fd = fd_long; 327 const int fd = fd_long;
312 328
313 // Before entering the sandbox, "prime" any systems that need to open 329 // Before entering the sandbox, "prime" any systems that need to open
314 // files and cache the results or the descriptors. 330 // files and cache the results or the descriptors.
315 base::RandUint64(); 331 base::RandUint64();
316 332
317 base::SysInfo::MaxSharedMemorySize(); 333 base::SysInfo::MaxSharedMemorySize();
318 334
319 // To make wcstombs/mbstowcs work in a renderer, setlocale() has to be 335 // To make wcstombs/mbstowcs work in a renderer, setlocale() has to be
320 // called before the sandbox is triggered. It's possible to avoid calling 336 // called before the sandbox is triggered. It's possible to avoid calling
321 // setlocale() by pulling out the conversion between FilePath and 337 // setlocale() by pulling out the conversion between FilePath and
322 // WebCore String out of the renderer and using string16 in place of 338 // WebCore String out of the renderer and using string16 in place of
323 // FilePath for IPC. 339 // FilePath for IPC.
324 const char* locale = setlocale(LC_ALL, ""); 340 const char* locale = setlocale(LC_ALL, "");
325 LOG_IF(WARNING, locale == NULL) << "setlocale failed."; 341 LOG_IF(WARNING, locale == NULL) << "setlocale failed.";
326 342
327 #if defined(ARCH_CPU_X86)
328 PATCH_GLOBAL_OFFSET_TABLE(localtime, sandbox_wrapper::localtime);
329 PATCH_GLOBAL_OFFSET_TABLE(localtime_r, sandbox_wrapper::localtime_r);
330 #endif
331
332 FilePath module_path; 343 FilePath module_path;
333 if (PathService::Get(base::DIR_MODULE, &module_path)) 344 if (PathService::Get(base::DIR_MODULE, &module_path))
334 media::InitializeMediaLibrary(module_path); 345 media::InitializeMediaLibrary(module_path);
335 346
336 static const char kChrootMe = 'C'; 347 static const char kChrootMe = 'C';
337 static const char kChrootMeSuccess = 'O'; 348 static const char kChrootMeSuccess = 'O';
338 349
339 if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) { 350 if (HANDLE_EINTR(write(fd, &kChrootMe, 1)) != 1) {
340 LOG(ERROR) << "Failed to write to chroot pipe: " << errno; 351 LOG(ERROR) << "Failed to write to chroot pipe: " << errno;
341 return false; 352 return false;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
383 } 394 }
384 } 395 }
385 } else { 396 } else {
386 SkiaFontConfigUseDirectImplementation(); 397 SkiaFontConfigUseDirectImplementation();
387 } 398 }
388 399
389 return true; 400 return true;
390 } 401 }
391 402
392 bool ZygoteMain(const MainFunctionParams& params) { 403 bool ZygoteMain(const MainFunctionParams& params) {
404 g_am_zygote_or_renderer = true;
405
393 if (!MaybeEnterChroot()) { 406 if (!MaybeEnterChroot()) {
394 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " 407 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: "
395 << errno << ")"; 408 << errno << ")";
396 return false; 409 return false;
397 } 410 }
398 411
399 Zygote zygote; 412 Zygote zygote;
400 return zygote.ProcessRequests(); 413 return zygote.ProcessRequests();
401 } 414 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698