summaryrefslogtreecommitdiff
path: root/chromium/sandbox/linux/services/libc_urandom_override.cc
blob: 6e8e12995a4a4952653e2c7987414c6f2a0ddca7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sandbox/linux/services/libc_urandom_override.h"

#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/rand_util.h"

// Note: this file is used by the zygote and nacl_helper.

#if !defined(HAVE_XSTAT) && defined(LIBC_GLIBC)
#define HAVE_XSTAT 1
#endif

namespace sandbox {

static bool g_override_urandom = false;

// TODO(sergeyu): Currently InitLibcUrandomOverrides() doesn't work properly
// under ASAN - it crashes content_unittests. Make sure it works properly and
// enable it here. http://crbug.com/123263
#if !defined(ADDRESS_SANITIZER)
static void InitLibcFileIOFunctions();
static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
#endif

void InitLibcUrandomOverrides() {
  // Make sure /dev/urandom is open.
  base::GetUrandomFD();
  g_override_urandom = true;

#if !defined(ADDRESS_SANITIZER)
  CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
                           InitLibcFileIOFunctions));
#endif
}

#if !defined(ADDRESS_SANITIZER)

static const char kUrandomDevPath[] = "/dev/urandom";

typedef FILE* (*FopenFunction)(const char* path, const char* mode);

static FopenFunction g_libc_fopen = NULL;
static FopenFunction g_libc_fopen64 = NULL;

#if HAVE_XSTAT
typedef int (*XstatFunction)(int version, const char *path, struct stat *buf);
typedef int (*Xstat64Function)(int version, const char *path,
                               struct stat64 *buf);

static XstatFunction g_libc_xstat = NULL;
static Xstat64Function g_libc_xstat64 = NULL;
#else
typedef int (*StatFunction)(const char *path, struct stat *buf);
typedef int (*Stat64Function)(const char *path, struct stat64 *buf);

static StatFunction g_libc_stat = NULL;
static Stat64Function g_libc_stat64 = NULL;
#endif  // HAVE_XSTAT

// Find the libc's real fopen* and *stat* functions. This should only be
// called once, and should be guarded by g_libc_file_io_funcs_guard.
static void InitLibcFileIOFunctions() {
  g_libc_fopen = reinterpret_cast<FopenFunction>(
      dlsym(RTLD_NEXT, "fopen"));
  g_libc_fopen64 = reinterpret_cast<FopenFunction>(
      dlsym(RTLD_NEXT, "fopen64"));

  if (!g_libc_fopen) {
    LOG(FATAL) << "Failed to get fopen() from libc.";
  } else if (!g_libc_fopen64) {
#if !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
    LOG(WARNING) << "Failed to get fopen64() from libc. Using fopen() instead.";
#endif  // !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
    g_libc_fopen64 = g_libc_fopen;
  }

#if HAVE_XSTAT
  g_libc_xstat = reinterpret_cast<XstatFunction>(
      dlsym(RTLD_NEXT, "__xstat"));
  g_libc_xstat64 = reinterpret_cast<Xstat64Function>(
      dlsym(RTLD_NEXT, "__xstat64"));

  if (!g_libc_xstat) {
    LOG(FATAL) << "Failed to get __xstat() from libc.";
  }
  if (!g_libc_xstat64) {
    LOG(FATAL) << "Failed to get __xstat64() from libc.";
  }
#else
  g_libc_stat = reinterpret_cast<StatFunction>(
      dlsym(RTLD_NEXT, "stat"));
  g_libc_stat64 = reinterpret_cast<Stat64Function>(
      dlsym(RTLD_NEXT, "stat64"));

  if (!g_libc_stat) {
    LOG(FATAL) << "Failed to get stat() from libc.";
  }
  if (!g_libc_stat64) {
    LOG(FATAL) << "Failed to get stat64() from libc.";
  }
#endif  // HAVE_XSTAT
}

// fopen() and fopen64() are intercepted here so that NSS can open
// /dev/urandom to seed its random number generator. NSS is used by
// remoting in the sendbox.

// fopen() call may be redirected to fopen64() in stdio.h using
// __REDIRECT(), which sets asm name for fopen() to "fopen64". This
// means that we cannot override fopen() directly here. Instead the
// the code below defines fopen_override() function with asm name
// "fopen", so that all references to fopen() will resolve to this
// function.

__attribute__ ((__visibility__("default")))
FILE* fopen_override(const char* path, const char* mode) {
  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
    if (fd < 0) {
      PLOG(ERROR) << "dup() failed.";
      return NULL;
    }
    return fdopen(fd, mode);
  } else {
    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
                             InitLibcFileIOFunctions));
    return g_libc_fopen(path, mode);
  }
}

__attribute__ ((__visibility__("default")))
FILE* fopen64_override(const char* path, const char* mode) {
  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
    int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
    if (fd < 0) {
      PLOG(ERROR) << "dup() failed.";
      return NULL;
    }
    return fdopen(fd, mode);
  } else {
    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
                             InitLibcFileIOFunctions));
    return g_libc_fopen64(path, mode);
  }
}

// The stat() family of functions are subject to the same problem as
// fopen(), so we have to use the same trick to override them.

#if HAVE_XSTAT

__attribute__ ((__visibility__("default")))
int xstat_override(int version, const char *path, struct stat *buf) {
  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
    int result = __fxstat(version, base::GetUrandomFD(), buf);
    return result;
  } else {
    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
                             InitLibcFileIOFunctions));
    return g_libc_xstat(version, path, buf);
  }
}

__attribute__ ((__visibility__("default")))
int xstat64_override(int version, const char *path, struct stat64 *buf) {
  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
    int result = __fxstat64(version, base::GetUrandomFD(), buf);
    return result;
  } else {
    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
                             InitLibcFileIOFunctions));
    return g_libc_xstat64(version, path, buf);
  }
}

#else

__attribute__ ((__visibility__("default")))
int stat_override(const char *path, struct stat *buf) {
  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
    int result = fstat(base::GetUrandomFD(), buf);
    return result;
  } else {
    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
                             InitLibcFileIOFunctions));
    return g_libc_stat(path, buf);
  }
}

__attribute__ ((__visibility__("default")))
int stat64_override(const char *path, struct stat64 *buf) {
  if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
    int result = fstat64(base::GetUrandomFD(), buf);
    return result;
  } else {
    CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
                             InitLibcFileIOFunctions));
    return g_libc_stat64(path, buf);
  }
}

#endif  // HAVE_XSTAT

#endif  // !defined(ADDRESS_SANITIZER)

}  // namespace content