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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/zygote_main_linux.cc
diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc
index 6752a068c7af28bf43bfb1cbd3a084007acf6708..2592209b4b9f9d7131e47702fb907e25a65ccebe 100644
--- a/chrome/browser/zygote_main_linux.cc
+++ b/chrome/browser/zygote_main_linux.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <dlfcn.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
@@ -205,11 +206,9 @@ class Zygote {
}
};
-// Patched dynamic symbol wrapper functions...
-namespace sandbox_wrapper {
-
-void do_localtime(time_t input, struct tm* output, char* timezone_out,
- size_t timezone_out_len) {
+static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output,
+ char* timezone_out,
+ size_t timezone_out_len) {
Pickle request;
request.WriteInt(LinuxSandbox::METHOD_LOCALTIME);
request.WriteString(
@@ -244,58 +243,75 @@ void do_localtime(time_t input, struct tm* output, char* timezone_out,
}
}
+static bool g_am_zygote_or_renderer = false;
+
+// Sandbox interception of libc calls.
+//
+// Because we are running in a sandbox certain libc calls will fail (localtime
+// being the motivating example - it needs to read /etc/localtime). We need to
+// intercept these calls and proxy them to the browser. However, these calls
+// may come from us or from our libraries. In some cases we can't just change
+// our code.
+//
+// It's for these cases that we have the following setup:
+//
+// We define global functions for those functions which we wish to override.
+// Since we will be first in the dynamic resolution order, the dynamic linker
+// will point callers to our versions of these functions. However, we have the
+// same binary for both the browser and the renderers, which means that our
+// overrides will apply in the browser too.
+//
+// The global |g_am_zygote_or_renderer| is true iff we are in a zygote or
+// renderer process. It's set in ZygoteMain and inherited by the renderers when
+// they fork. (This means that it'll be incorrect for global constructor
+// functions and before ZygoteMain is called - beware).
+//
+// Our replacement functions can check this global and either proxy
+// the call to the browser over the sandbox IPC
+// (http://code.google.com/p/chromium/wiki/LinuxSandboxIPC) or they can use
+// dlsym with RTLD_NEXT to resolve the symbol, ignoring any symbols in the
+// current module.
+//
+// Other avenues:
+//
+// Our first attempt involved some assembly to patch the GOT of the current
+// module. This worked, but was platform specific and doesn't catch the case
+// where a library makes a call rather than current module.
+//
+// We also considered patching the function in place, but this would again by
+// platform specific and the above technique seems to work well enough.
+
struct tm* localtime(const time_t* timep) {
- static struct tm time_struct;
- static char timezone_string[64];
- do_localtime(*timep, &time_struct, timezone_string, sizeof(timezone_string));
- return &time_struct;
+ if (g_am_zygote_or_renderer) {
+ static struct tm time_struct;
+ static char timezone_string[64];
+ ProxyLocaltimeCallToBrowser(*timep, &time_struct, timezone_string,
+ sizeof(timezone_string));
+ return &time_struct;
+ } else {
+ typedef struct tm* (*LocaltimeFunction)(const time_t* timep);
+ static LocaltimeFunction libc_localtime;
+ if (!libc_localtime)
+ libc_localtime = (LocaltimeFunction) dlsym(RTLD_NEXT, "localtime");
+
+ return libc_localtime(timep);
+ }
}
struct tm* localtime_r(const time_t* timep, struct tm* result) {
- do_localtime(*timep, result, NULL, 0);
- return result;
-}
+ if (g_am_zygote_or_renderer) {
+ ProxyLocaltimeCallToBrowser(*timep, result, NULL, 0);
+ return result;
+ } else {
+ typedef struct tm* (*LocaltimeRFunction)(const time_t* timep,
+ struct tm* result);
+ static LocaltimeRFunction libc_localtime_r;
+ if (!libc_localtime_r)
+ libc_localtime_r = (LocaltimeRFunction) dlsym(RTLD_NEXT, "localtime_r");
-} // namespace sandbox_wrapper
-
-/* On IA-32, function calls which need to be resolved by the dynamic linker are
- * directed to the producure linking table (PLT). Each PLT entry contains code
- * which jumps (indirectly) via the global offset table (GOT):
- * Dump of assembler code for function f@plt:
- * 0x0804830c <f@plt+0>: jmp *0x804a004 # GOT indirect jump
- * 0x08048312 <f@plt+6>: push $0x8
- * 0x08048317 <f@plt+11>: jmp 0x80482ec <_init+48>
- *
- * At the beginning of a process's lifetime, the GOT entry jumps back to
- * <f@plt+6> end then enters the dynamic linker. Once the symbol has been
- * resolved, the GOT entry is patched so that future calls go directly to the
- * resolved function.
- *
- * This macro finds the PLT entry for a given symbol, |symbol|, and reads the
- * GOT entry address from the first instruction. It then patches that address
- * with the address of a replacement function, |replacement|.
- */
-#define PATCH_GLOBAL_OFFSET_TABLE(symbol, replacement) \
- /* First, get the current instruction pointer since the PLT address */ \
- /* is IP relative */ \
- asm ("call 0f\n" \
- "0: pop %%ecx\n" \
- /* Move the IP relative address of the PLT entry into EAX */ \
- "mov $" #symbol "@plt,%%eax\n" \
- /* Add EAX to ECX to get an absolute entry */ \
- "add %%eax,%%ecx\n" \
- /* The value in ECX was relative to the add instruction, however, */ \
- /* the IP value was that of the pop. The pop and mov take 6 */ \
- /* bytes, so adding 6 gets us the correct address for the PLT. The */ \
- /* first instruction at the PLT is FF 25 <abs address>, so we skip 2 */ \
- /* bytes to get to the address. 6 + 2 = 8: */ \
- "movl 8(%%ecx),%%ecx\n" \
- /* Now ECX contains the address of the GOT entry, we poke our */ \
- /* replacement function in there: */ \
- "movl %0,(%%ecx)\n" \
- : /* no output */ \
- : "r" (replacement) \
- : "memory", "%eax", "%ecx");
+ return libc_localtime_r(timep, result);
+ }
+}
static bool MaybeEnterChroot() {
const char* const sandbox_fd_string = getenv("SBX_D");
@@ -324,11 +340,6 @@ static bool MaybeEnterChroot() {
const char* locale = setlocale(LC_ALL, "");
LOG_IF(WARNING, locale == NULL) << "setlocale failed.";
-#if defined(ARCH_CPU_X86)
- PATCH_GLOBAL_OFFSET_TABLE(localtime, sandbox_wrapper::localtime);
- PATCH_GLOBAL_OFFSET_TABLE(localtime_r, sandbox_wrapper::localtime_r);
-#endif
-
FilePath module_path;
if (PathService::Get(base::DIR_MODULE, &module_path))
media::InitializeMediaLibrary(module_path);
@@ -390,6 +401,8 @@ static bool MaybeEnterChroot() {
}
bool ZygoteMain(const MainFunctionParams& params) {
+ g_am_zygote_or_renderer = true;
+
if (!MaybeEnterChroot()) {
LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: "
<< errno << ")";
« 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