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