Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 ci et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PoisonIOInterposer.h"
8 #include "mach_override.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/Assertions.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/IOInterposer.h"
14 #include "mozilla/Mutex.h"
15 #include "mozilla/ProcessedStack.h"
16 #include "mozilla/Scoped.h"
17 #include "mozilla/Telemetry.h"
18 #include "nsPrintfCString.h"
19 #include "nsStackWalk.h"
20 #include "nsTraceRefcnt.h"
21 #include "plstr.h"
22 #include "prio.h"
24 #include <algorithm>
25 #include <vector>
27 #include <sys/param.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/uio.h>
31 #include <aio.h>
32 #include <dlfcn.h>
33 #include <fcntl.h>
35 namespace {
37 using namespace mozilla;
39 // Bit tracking if poisoned writes are enabled
40 static bool sIsEnabled = false;
42 // Check if writes are dirty before reporting IO
43 static bool sOnlyReportDirtyWrites = false;
45 // Routines for write validation
46 bool IsValidWrite(int fd, const void *wbuf, size_t count);
47 bool IsIPCWrite(int fd, const struct stat &buf);
49 /******************************** IO AutoTimer ********************************/
51 /**
52 * RAII class for timing the duration of an I/O call and reporting the result
53 * to the IOInterposeObserver API.
54 */
55 class MacIOAutoObservation : public IOInterposeObserver::Observation
56 {
57 public:
58 MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd)
59 : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
60 !IsDebugFile(aFd))
61 , mFd(aFd)
62 , mHasQueriedFilename(false)
63 , mFilename(nullptr)
64 {
65 }
67 MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd,
68 const void *aBuf, size_t aCount)
69 : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
70 !IsDebugFile(aFd) &&
71 IsValidWrite(aFd, aBuf, aCount))
72 , mFd(aFd)
73 , mHasQueriedFilename(false)
74 , mFilename(nullptr)
75 {
76 }
78 // Custom implementation of IOInterposeObserver::Observation::Filename
79 const char16_t* Filename() MOZ_OVERRIDE;
81 ~MacIOAutoObservation()
82 {
83 Report();
84 if (mFilename) {
85 NS_Free(mFilename);
86 mFilename = nullptr;
87 }
88 }
90 private:
91 int mFd;
92 bool mHasQueriedFilename;
93 char16_t* mFilename;
94 static const char* sReference;
95 };
97 const char* MacIOAutoObservation::sReference = "PoisonIOInterposer";
99 // Get filename for this observation
100 const char16_t* MacIOAutoObservation::Filename()
101 {
102 // If mHasQueriedFilename is true, then we already have it
103 if (mHasQueriedFilename) {
104 return mFilename;
105 }
106 char filename[MAXPATHLEN];
107 if (fcntl(mFd, F_GETPATH, filename) != -1) {
108 mFilename = UTF8ToNewUnicode(nsDependentCString(filename));
109 } else {
110 mFilename = nullptr;
111 }
112 mHasQueriedFilename = true;
114 // Return filename
115 return mFilename;
116 }
118 /****************************** Write Validation ******************************/
120 // We want to detect "actual" writes, not IPC. Some IPC mechanisms are
121 // implemented with file descriptors, so filter them out.
122 bool IsIPCWrite(int fd, const struct stat &buf) {
123 if ((buf.st_mode & S_IFMT) == S_IFIFO) {
124 return true;
125 }
127 if ((buf.st_mode & S_IFMT) != S_IFSOCK) {
128 return false;
129 }
131 sockaddr_storage address;
132 socklen_t len = sizeof(address);
133 if (getsockname(fd, (sockaddr*) &address, &len) != 0) {
134 return true; // Ignore the fd if we can't find what it is.
135 }
137 return address.ss_family == AF_UNIX;
138 }
140 // We want to report actual disk IO not things that don't move bits on the disk
141 bool IsValidWrite(int fd, const void *wbuf, size_t count)
142 {
143 // Ignore writes of zero bytes, Firefox does some during shutdown.
144 if (count == 0) {
145 return false;
146 }
148 {
149 struct stat buf;
150 int rv = fstat(fd, &buf);
151 if (rv != 0) {
152 return true;
153 }
155 if (IsIPCWrite(fd, buf)) {
156 return false;
157 }
158 }
160 // For writev we pass a nullptr wbuf. We should only get here from
161 // dbm, and it uses write, so assert that we have wbuf.
162 if (!wbuf) {
163 return true;
164 }
166 // Break, here if we're allowed to report non-dirty writes
167 if(!sOnlyReportDirtyWrites) {
168 return true;
169 }
171 // As a really bad hack, accept writes that don't change the on disk
172 // content. This is needed because dbm doesn't keep track of dirty bits
173 // and can end up writing the same data to disk twice. Once when the
174 // user (nss) asks it to sync and once when closing the database.
175 ScopedFreePtr<void> wbuf2(malloc(count));
176 if (!wbuf2) {
177 return true;
178 }
179 off_t pos = lseek(fd, 0, SEEK_CUR);
180 if (pos == -1) {
181 return true;
182 }
183 ssize_t r = read(fd, wbuf2, count);
184 if (r < 0 || (size_t)r != count) {
185 return true;
186 }
187 int cmp = memcmp(wbuf, wbuf2, count);
188 if (cmp != 0) {
189 return true;
190 }
191 off_t pos2 = lseek(fd, pos, SEEK_SET);
192 if (pos2 != pos) {
193 return true;
194 }
196 // Otherwise this is not a valid write
197 return false;
198 }
200 /*************************** Function Interception ***************************/
202 /** Structure for declaration of function override */
203 struct FuncData {
204 const char *Name; // Name of the function for the ones we use dlsym
205 const void *Wrapper; // The function that we will replace 'Function' with
206 void *Function; // The function that will be replaced with 'Wrapper'
207 void *Buffer; // Will point to the jump buffer that lets us call
208 // 'Function' after it has been replaced.
209 };
211 // Wrap aio_write. We have not seen it before, so just assert/report it.
212 typedef ssize_t (*aio_write_t)(struct aiocb *aiocbp);
213 ssize_t wrap_aio_write(struct aiocb *aiocbp);
214 FuncData aio_write_data = { 0, (void*) wrap_aio_write, (void*) aio_write };
215 ssize_t wrap_aio_write(struct aiocb *aiocbp) {
216 MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aiocbp->aio_fildes);
218 aio_write_t old_write = (aio_write_t) aio_write_data.Buffer;
219 return old_write(aiocbp);
220 }
222 // Wrap pwrite-like functions.
223 // We have not seen them before, so just assert/report it.
224 typedef ssize_t (*pwrite_t)(int fd, const void *buf, size_t nbyte, off_t offset);
225 template<FuncData &foo>
226 ssize_t wrap_pwrite_temp(int fd, const void *buf, size_t nbyte, off_t offset) {
227 MacIOAutoObservation timer(IOInterposeObserver::OpWrite, fd);
228 pwrite_t old_write = (pwrite_t) foo.Buffer;
229 return old_write(fd, buf, nbyte, offset);
230 }
232 // Define a FuncData for a pwrite-like functions.
233 #define DEFINE_PWRITE_DATA(X, NAME) \
234 FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp<X ## _data> }; \
236 // This exists everywhere.
237 DEFINE_PWRITE_DATA(pwrite, "pwrite")
238 // These exist on 32 bit OS X
239 DEFINE_PWRITE_DATA(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003");
240 DEFINE_PWRITE_DATA(pwrite_UNIX2003, "pwrite$UNIX2003");
241 // This exists on 64 bit OS X
242 DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL");
245 typedef ssize_t (*writev_t)(int fd, const struct iovec *iov, int iovcnt);
246 template<FuncData &foo>
247 ssize_t wrap_writev_temp(int fd, const struct iovec *iov, int iovcnt) {
248 MacIOAutoObservation timer(IOInterposeObserver::OpWrite, fd, nullptr, iovcnt);
249 writev_t old_write = (writev_t) foo.Buffer;
250 return old_write(fd, iov, iovcnt);
251 }
253 // Define a FuncData for a writev-like functions.
254 #define DEFINE_WRITEV_DATA(X, NAME) \
255 FuncData X ## _data = { NAME, (void*) wrap_writev_temp<X ## _data> }; \
257 // This exists everywhere.
258 DEFINE_WRITEV_DATA(writev, "writev");
259 // These exist on 32 bit OS X
260 DEFINE_WRITEV_DATA(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003");
261 DEFINE_WRITEV_DATA(writev_UNIX2003, "writev$UNIX2003");
262 // This exists on 64 bit OS X
263 DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL");
265 typedef ssize_t (*write_t)(int fd, const void *buf, size_t count);
266 template<FuncData &foo>
267 ssize_t wrap_write_temp(int fd, const void *buf, size_t count) {
268 MacIOAutoObservation timer(IOInterposeObserver::OpWrite, fd, buf, count);
269 write_t old_write = (write_t) foo.Buffer;
270 return old_write(fd, buf, count);
271 }
273 // Define a FuncData for a write-like functions.
274 #define DEFINE_WRITE_DATA(X, NAME) \
275 FuncData X ## _data = { NAME, (void*) wrap_write_temp<X ## _data> }; \
277 // This exists everywhere.
278 DEFINE_WRITE_DATA(write, "write");
279 // These exist on 32 bit OS X
280 DEFINE_WRITE_DATA(write_NOCANCEL_UNIX2003, "write$NOCANCEL$UNIX2003");
281 DEFINE_WRITE_DATA(write_UNIX2003, "write$UNIX2003");
282 // This exists on 64 bit OS X
283 DEFINE_WRITE_DATA(write_NOCANCEL, "write$NOCANCEL");
285 FuncData *Functions[] = { &aio_write_data,
287 &pwrite_data,
288 &pwrite_NOCANCEL_UNIX2003_data,
289 &pwrite_UNIX2003_data,
290 &pwrite_NOCANCEL_data,
292 &write_data,
293 &write_NOCANCEL_UNIX2003_data,
294 &write_UNIX2003_data,
295 &write_NOCANCEL_data,
297 &writev_data,
298 &writev_NOCANCEL_UNIX2003_data,
299 &writev_UNIX2003_data,
300 &writev_NOCANCEL_data};
302 const int NumFunctions = ArrayLength(Functions);
304 } // anonymous namespace
306 /******************************** IO Poisoning ********************************/
308 namespace mozilla {
310 void InitPoisonIOInterposer() {
311 // Enable reporting from poisoned write methods
312 sIsEnabled = true;
314 // Make sure we only poison writes once!
315 static bool WritesArePoisoned = false;
316 if (WritesArePoisoned) {
317 return;
318 }
319 WritesArePoisoned = true;
321 // stdout and stderr are OK.
322 MozillaRegisterDebugFD(1);
323 MozillaRegisterDebugFD(2);
325 for (int i = 0; i < NumFunctions; ++i) {
326 FuncData *d = Functions[i];
327 if (!d->Function) {
328 d->Function = dlsym(RTLD_DEFAULT, d->Name);
329 }
330 if (!d->Function) {
331 continue;
332 }
333 DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper,
334 &d->Buffer);
335 MOZ_ASSERT(t == err_none);
336 }
337 }
339 void OnlyReportDirtyWrites() {
340 sOnlyReportDirtyWrites = true;
341 }
343 void ClearPoisonIOInterposer() {
344 // Not sure how or if we can unpoison the functions. Would be nice, but no
345 // worries we won't need to do this anyway.
346 sIsEnabled = false;
347 }
349 } // namespace mozilla