Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * ChromeWorker Object subprocess.jsm on Unix-like systems (Linux, Mac OS X, ...) |
michael@0 | 8 | * to process stdin/stdout/stderr on separate threads. |
michael@0 | 9 | * |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | // Being a ChromeWorker object, implicitly uses the following: |
michael@0 | 13 | // Components.utils.import("resource://gre/modules/ctypes.jsm"); |
michael@0 | 14 | |
michael@0 | 15 | 'use strict'; |
michael@0 | 16 | |
michael@0 | 17 | const BufferSize = 1024; |
michael@0 | 18 | |
michael@0 | 19 | var libc = null; |
michael@0 | 20 | var libcFunc = {}; |
michael@0 | 21 | |
michael@0 | 22 | |
michael@0 | 23 | /* |
michael@0 | 24 | struct pollfd { |
michael@0 | 25 | int fd; // file descriptor |
michael@0 | 26 | short events; // events to look for |
michael@0 | 27 | short revents; // events returned |
michael@0 | 28 | }; |
michael@0 | 29 | */ |
michael@0 | 30 | |
michael@0 | 31 | var pollfd = new ctypes.StructType("pollfd", |
michael@0 | 32 | [ {'fd': ctypes.int}, |
michael@0 | 33 | {'events': ctypes.short}, |
michael@0 | 34 | {'revents': ctypes.short} |
michael@0 | 35 | ]); |
michael@0 | 36 | |
michael@0 | 37 | var WriteBuffer = ctypes.uint8_t.array(BufferSize); |
michael@0 | 38 | var ReadBuffer = ctypes.char.array(BufferSize); |
michael@0 | 39 | |
michael@0 | 40 | |
michael@0 | 41 | const POLLIN = 0x0001; |
michael@0 | 42 | const POLLOUT = 0x0004; |
michael@0 | 43 | |
michael@0 | 44 | const POLLERR = 0x0008; // some poll error occurred |
michael@0 | 45 | const POLLHUP = 0x0010; // file descriptor was "hung up" |
michael@0 | 46 | const POLLNVAL = 0x0020; // requested events "invalid" |
michael@0 | 47 | |
michael@0 | 48 | const WNOHANG = 0x01; |
michael@0 | 49 | |
michael@0 | 50 | const pid_t = ctypes.int32_t; |
michael@0 | 51 | |
michael@0 | 52 | const INDEFINITE = -1; |
michael@0 | 53 | const NOWAIT = 0; |
michael@0 | 54 | const WAITTIME = 200 // wait time for poll() in ms |
michael@0 | 55 | |
michael@0 | 56 | function initLibc(libName) { |
michael@0 | 57 | postMessage({msg: "debug", data: "initialising library with "+ libName}); |
michael@0 | 58 | |
michael@0 | 59 | libc = ctypes.open(libName); |
michael@0 | 60 | |
michael@0 | 61 | libcFunc.pollFds = pollfd.array(1); |
michael@0 | 62 | |
michael@0 | 63 | // int poll(struct pollfd fds[], nfds_t nfds, int timeout); |
michael@0 | 64 | libcFunc.poll = libc.declare("poll", |
michael@0 | 65 | ctypes.default_abi, |
michael@0 | 66 | ctypes.int, |
michael@0 | 67 | libcFunc.pollFds, |
michael@0 | 68 | ctypes.unsigned_int, |
michael@0 | 69 | ctypes.int); |
michael@0 | 70 | |
michael@0 | 71 | //ssize_t write(int fd, const void *buf, size_t count); |
michael@0 | 72 | // NOTE: buf is declared as array of unsigned int8 instead of char to avoid |
michael@0 | 73 | // implicit charset conversion |
michael@0 | 74 | libcFunc.write = libc.declare("write", |
michael@0 | 75 | ctypes.default_abi, |
michael@0 | 76 | ctypes.int, |
michael@0 | 77 | ctypes.int, |
michael@0 | 78 | WriteBuffer, |
michael@0 | 79 | ctypes.int); |
michael@0 | 80 | |
michael@0 | 81 | //int read(int fd, void *buf, size_t count); |
michael@0 | 82 | libcFunc.read = libc.declare("read", |
michael@0 | 83 | ctypes.default_abi, |
michael@0 | 84 | ctypes.int, |
michael@0 | 85 | ctypes.int, |
michael@0 | 86 | ReadBuffer, |
michael@0 | 87 | ctypes.int); |
michael@0 | 88 | |
michael@0 | 89 | //int pipe(int pipefd[2]); |
michael@0 | 90 | libcFunc.pipefd = ctypes.int.array(2); |
michael@0 | 91 | |
michael@0 | 92 | //int close(int fd); |
michael@0 | 93 | libcFunc.close = libc.declare("close", |
michael@0 | 94 | ctypes.default_abi, |
michael@0 | 95 | ctypes.int, |
michael@0 | 96 | ctypes.int); |
michael@0 | 97 | |
michael@0 | 98 | //pid_t waitpid(pid_t pid, int *status, int options); |
michael@0 | 99 | libcFunc.waitpid = libc.declare("waitpid", |
michael@0 | 100 | ctypes.default_abi, |
michael@0 | 101 | pid_t, |
michael@0 | 102 | pid_t, |
michael@0 | 103 | ctypes.int.ptr, |
michael@0 | 104 | ctypes.int); |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | function closePipe(pipe) { |
michael@0 | 108 | libcFunc.close(pipe); |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | function writePipe(pipe, data) { |
michael@0 | 112 | |
michael@0 | 113 | postMessage({msg: "debug", data: "trying to write to "+pipe}); |
michael@0 | 114 | |
michael@0 | 115 | let numChunks = Math.floor(data.length / BufferSize); |
michael@0 | 116 | let pData = new WriteBuffer(); |
michael@0 | 117 | |
michael@0 | 118 | for (var chunk = 0; chunk <= numChunks; chunk ++) { |
michael@0 | 119 | let numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize; |
michael@0 | 120 | for (var i=0; i < numBytes; i++) { |
michael@0 | 121 | pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256; |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | let bytesWritten = libcFunc.write(pipe, pData, numBytes); |
michael@0 | 125 | if (bytesWritten != numBytes) { |
michael@0 | 126 | closePipe(); |
michael@0 | 127 | libc.close(); |
michael@0 | 128 | postMessage({ msg: "error", data: "error: wrote "+bytesWritten+" instead of "+numBytes+" bytes"}); |
michael@0 | 129 | close(); |
michael@0 | 130 | } |
michael@0 | 131 | } |
michael@0 | 132 | postMessage({msg: "info", data: "wrote "+data.length+" bytes of data"}); |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | |
michael@0 | 136 | function readString(data, length, charset) { |
michael@0 | 137 | var string = '', bytes = []; |
michael@0 | 138 | for(var i = 0;i < length; i++) { |
michael@0 | 139 | if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data |
michael@0 | 140 | break; |
michael@0 | 141 | |
michael@0 | 142 | bytes.push(data[i]); |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | return bytes; |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | function readPipe(pipe, charset, pid) { |
michael@0 | 149 | var p = new libcFunc.pollFds; |
michael@0 | 150 | p[0].fd = pipe; |
michael@0 | 151 | p[0].events = POLLIN | POLLERR | POLLHUP; |
michael@0 | 152 | p[0].revents = 0; |
michael@0 | 153 | var pollTimeout = WAITTIME; |
michael@0 | 154 | var exitCode = -1; |
michael@0 | 155 | var readCount = 0; |
michael@0 | 156 | var result, status = ctypes.int(); |
michael@0 | 157 | result = 0; |
michael@0 | 158 | |
michael@0 | 159 | |
michael@0 | 160 | const i=0; |
michael@0 | 161 | while (true) { |
michael@0 | 162 | if (result == 0) { |
michael@0 | 163 | result = libcFunc.waitpid(pid, status.address(), WNOHANG); |
michael@0 | 164 | if (result > 0) { |
michael@0 | 165 | pollTimeout = NOWAIT; |
michael@0 | 166 | exitCode = parseInt(status.value); |
michael@0 | 167 | postMessage({msg: "debug", data: "waitpid signaled subprocess stop, exitcode="+status.value }); |
michael@0 | 168 | } |
michael@0 | 169 | } |
michael@0 | 170 | var r = libcFunc.poll(p, 1, pollTimeout); |
michael@0 | 171 | if (r > 0) { |
michael@0 | 172 | if (p[i].revents & POLLIN) { |
michael@0 | 173 | postMessage({msg: "debug", data: "reading next chunk"}); |
michael@0 | 174 | readCount = readPolledFd(p[i].fd, charset); |
michael@0 | 175 | if (readCount == 0) break; |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | if (p[i].revents & POLLHUP) { |
michael@0 | 179 | postMessage({msg: "debug", data: "poll returned HUP"}); |
michael@0 | 180 | break; |
michael@0 | 181 | } |
michael@0 | 182 | else if (p[i].revents & POLLERR) { |
michael@0 | 183 | postMessage({msg: "error", data: "poll returned error"}); |
michael@0 | 184 | break; |
michael@0 | 185 | } |
michael@0 | 186 | else if (p[i].revents != POLLIN) { |
michael@0 | 187 | postMessage({msg: "error", data: "poll returned "+p[i]}); |
michael@0 | 188 | break; |
michael@0 | 189 | } |
michael@0 | 190 | } |
michael@0 | 191 | else |
michael@0 | 192 | if (pollTimeout == 0 || r < 0) break; |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | // continue reading until the buffer is empty |
michael@0 | 196 | while (readCount > 0) { |
michael@0 | 197 | readCount = readPolledFd(pipe, charset); |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | libcFunc.close(pipe); |
michael@0 | 201 | postMessage({msg: "done", data: exitCode }); |
michael@0 | 202 | libc.close(); |
michael@0 | 203 | close(); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | function readPolledFd(pipe, charset) { |
michael@0 | 207 | var line = new ReadBuffer(); |
michael@0 | 208 | var r = libcFunc.read(pipe, line, BufferSize); |
michael@0 | 209 | |
michael@0 | 210 | if (r > 0) { |
michael@0 | 211 | var c = readString(line, r, charset); |
michael@0 | 212 | postMessage({msg: "data", data: c, count: c.length}); |
michael@0 | 213 | } |
michael@0 | 214 | return r; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | onmessage = function (event) { |
michael@0 | 218 | switch (event.data.msg) { |
michael@0 | 219 | case "init": |
michael@0 | 220 | initLibc(event.data.libc); |
michael@0 | 221 | break; |
michael@0 | 222 | case "read": |
michael@0 | 223 | initLibc(event.data.libc); |
michael@0 | 224 | readPipe(event.data.pipe, event.data.charset, event.data.pid); |
michael@0 | 225 | break; |
michael@0 | 226 | case "write": |
michael@0 | 227 | // data contents: |
michael@0 | 228 | // msg: 'write' |
michael@0 | 229 | // data: the data (string) to write |
michael@0 | 230 | // pipe: ptr to pipe |
michael@0 | 231 | writePipe(event.data.pipe, event.data.data); |
michael@0 | 232 | postMessage({msg: "info", data: "WriteOK"}); |
michael@0 | 233 | break; |
michael@0 | 234 | case "close": |
michael@0 | 235 | postMessage({msg: "debug", data: "closing stdin\n"}); |
michael@0 | 236 | |
michael@0 | 237 | closePipe(event.data.pipe); |
michael@0 | 238 | postMessage({msg: "info", data: "ClosedOK"}); |
michael@0 | 239 | break; |
michael@0 | 240 | case "stop": |
michael@0 | 241 | libc.close(); // do not use libc after this point |
michael@0 | 242 | close(); |
michael@0 | 243 | break; |
michael@0 | 244 | default: |
michael@0 | 245 | throw("error: Unknown command"+event.data.msg+"\n"); |
michael@0 | 246 | } |
michael@0 | 247 | return; |
michael@0 | 248 | }; |