libc_urandom_override.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "sandbox/linux/services/libc_urandom_override.h"
6
7#include <dlfcn.h>
8#include <pthread.h>
9#include <stdio.h>
10#include <sys/stat.h>
11#include <unistd.h>
12
13#include "base/logging.h"
14#include "base/posix/eintr_wrapper.h"
15#include "base/rand_util.h"
16
17// Note: this file is used by the zygote and nacl_helper.
18
19namespace sandbox {
20
21static bool g_override_urandom = false;
22
23void InitLibcUrandomOverrides() {
24  // Make sure /dev/urandom is open.
25  base::GetUrandomFD();
26  g_override_urandom = true;
27}
28
29// TODO(sergeyu): Currently this code doesn't work properly under ASAN
30// - it crashes content_unittests. Make sure it works properly and
31// enable it here. http://crbug.com/123263
32#if !defined(ADDRESS_SANITIZER)
33
34static const char kUrandomDevPath[] = "/dev/urandom";
35
36typedef FILE* (*FopenFunction)(const char* path, const char* mode);
37typedef int (*XstatFunction)(int version, const char *path, struct stat *buf);
38typedef int (*Xstat64Function)(int version, const char *path,
39                               struct stat64 *buf);
40
41static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
42static FopenFunction g_libc_fopen;
43static FopenFunction g_libc_fopen64;
44static XstatFunction g_libc_xstat;
45static Xstat64Function g_libc_xstat64;
46
47static void InitLibcFileIOFunctions() {
48  g_libc_fopen = reinterpret_cast<FopenFunction>(
49      dlsym(RTLD_NEXT, "fopen"));
50  g_libc_fopen64 = reinterpret_cast<FopenFunction>(
51      dlsym(RTLD_NEXT, "fopen64"));
52
53  if (!g_libc_fopen) {
54    LOG(FATAL) << "Failed to get fopen() from libc.";
55  } else if (!g_libc_fopen64) {
56#if !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
57    LOG(WARNING) << "Failed to get fopen64() from libc. Using fopen() instead.";
58#endif  // !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
59    g_libc_fopen64 = g_libc_fopen;
60  }
61
62  // TODO(sergeyu): This works only on systems with glibc. Fix it to
63  // work properly on other systems if necessary.
64  g_libc_xstat = reinterpret_cast<XstatFunction>(
65      dlsym(RTLD_NEXT, "__xstat"));
66  g_libc_xstat64 = reinterpret_cast<Xstat64Function>(
67      dlsym(RTLD_NEXT, "__xstat64"));
68
69  if (!g_libc_xstat) {
70    LOG(FATAL) << "Failed to get __xstat() from libc.";
71  }
72  if (!g_libc_xstat64) {
73    LOG(WARNING) << "Failed to get __xstat64() from libc.";
74  }
75}
76
77// fopen() and fopen64() are intercepted here so that NSS can open
78// /dev/urandom to seed its random number generator. NSS is used by
79// remoting in the sendbox.
80
81// fopen() call may be redirected to fopen64() in stdio.h using
82// __REDIRECT(), which sets asm name for fopen() to "fopen64". This
83// means that we cannot override fopen() directly here. Instead the
84// the code below defines fopen_override() function with asm name
85// "fopen", so that all references to fopen() will resolve to this
86// function.
87__attribute__ ((__visibility__("default")))
88FILE* fopen_override(const char* path, const char* mode)  __asm__ ("fopen");
89
90__attribute__ ((__visibility__("default")))
91FILE* fopen_override(const char* path, const char* mode) {
92  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
93    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
94    if (fd < 0) {
95      PLOG(ERROR) << "dup() failed.";
96      return NULL;
97    }
98    return fdopen(fd, mode);
99  } else {
100    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
101                             InitLibcFileIOFunctions));
102    return g_libc_fopen(path, mode);
103  }
104}
105
106__attribute__ ((__visibility__("default")))
107FILE* fopen64(const char* path, const char* mode) {
108  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
109    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
110    if (fd < 0) {
111      PLOG(ERROR) << "dup() failed.";
112      return NULL;
113    }
114    return fdopen(fd, mode);
115  } else {
116    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
117                             InitLibcFileIOFunctions));
118    return g_libc_fopen64(path, mode);
119  }
120}
121
122// stat() is subject to the same problem as fopen(), so we have to use
123// the same trick to override it.
124__attribute__ ((__visibility__("default")))
125int xstat_override(int version,
126                   const char *path,
127                   struct stat *buf)  __asm__ ("__xstat");
128
129__attribute__ ((__visibility__("default")))
130int xstat_override(int version, const char *path, struct stat *buf) {
131  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
132    int result = __fxstat(version, base::GetUrandomFD(), buf);
133    return result;
134  } else {
135    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
136                             InitLibcFileIOFunctions));
137    return g_libc_xstat(version, path, buf);
138  }
139}
140
141__attribute__ ((__visibility__("default")))
142int xstat64_override(int version,
143                     const char *path,
144                     struct stat64 *buf)  __asm__ ("__xstat64");
145
146__attribute__ ((__visibility__("default")))
147int xstat64_override(int version, const char *path, struct stat64 *buf) {
148  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
149    int result = __fxstat64(version, base::GetUrandomFD(), buf);
150    return result;
151  } else {
152    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
153                             InitLibcFileIOFunctions));
154    CHECK(g_libc_xstat64);
155    return g_libc_xstat64(version, path, buf);
156  }
157}
158
159#endif  // !ADDRESS_SANITIZER
160
161}  // namespace content
162