Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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 "vm/SharedArrayObject.h"
9 #include "jsprf.h"
10 #include "jsobjinlines.h"
12 #ifdef XP_WIN
13 # include "jswin.h"
14 #else
15 # include <sys/mman.h>
16 #endif
18 #ifdef MOZ_VALGRIND
19 # include <valgrind/memcheck.h>
20 #endif
22 #include "mozilla/Atomics.h"
23 #include "jit/AsmJS.h"
25 using namespace js;
27 using mozilla::IsNaN;
28 using mozilla::PodCopy;
30 /*
31 * SharedArrayRawBuffer
32 */
34 static inline void *
35 MapMemory(size_t length, bool commit)
36 {
37 #ifdef XP_WIN
38 int prot = (commit ? MEM_COMMIT : MEM_RESERVE);
39 int flags = (commit ? PAGE_READWRITE : PAGE_NOACCESS);
40 return VirtualAlloc(nullptr, length, prot, flags);
41 #else
42 int prot = (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE);
43 void *p = mmap(nullptr, length, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
44 if (p == MAP_FAILED)
45 return nullptr;
46 return p;
47 #endif
48 }
50 static inline void
51 UnmapMemory(void *addr, size_t len)
52 {
53 #ifdef XP_WIN
54 VirtualFree(addr, 0, MEM_RELEASE);
55 #else
56 munmap(addr, len);
57 #endif
58 }
60 static inline bool
61 MarkValidRegion(void *addr, size_t len)
62 {
63 #ifdef XP_WIN
64 if (!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE))
65 return false;
66 return true;
67 #else
68 if (mprotect(addr, len, PROT_READ | PROT_WRITE))
69 return false;
70 return true;
71 #endif
72 }
74 SharedArrayRawBuffer *
75 SharedArrayRawBuffer::New(uint32_t length)
76 {
77 // Enforced by SharedArrayBufferObject constructor.
78 JS_ASSERT(IsValidAsmJSHeapLength(length));
80 #ifdef JS_CPU_X64
81 // Get the entire reserved region (with all pages inaccessible)
82 void *p = MapMemory(AsmJSMappedSize, false);
83 if (!p)
84 return nullptr;
86 size_t validLength = AsmJSPageSize + length;
87 if (!MarkValidRegion(p, validLength)) {
88 UnmapMemory(p, AsmJSMappedSize);
89 return nullptr;
90 }
91 # if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
92 // Tell Valgrind/Memcheck to not report accesses in the inaccessible region.
93 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)p + validLength,
94 AsmJSMappedSize-validLength);
95 # endif
96 #else
97 uint32_t allocSize = length + AsmJSPageSize;
98 if (allocSize <= length)
99 return nullptr;
101 void *p = MapMemory(allocSize, true);
102 if (!p)
103 return nullptr;
104 #endif
105 uint8_t *buffer = reinterpret_cast<uint8_t*>(p) + AsmJSPageSize;
106 uint8_t *base = buffer - sizeof(SharedArrayRawBuffer);
107 return new (base) SharedArrayRawBuffer(buffer, length);
108 }
110 void
111 SharedArrayRawBuffer::addReference()
112 {
113 JS_ASSERT(this->refcount > 0);
114 ++this->refcount; // Atomic.
115 }
117 void
118 SharedArrayRawBuffer::dropReference()
119 {
120 // Drop the reference to the buffer.
121 uint32_t refcount = --this->refcount; // Atomic.
123 // If this was the final reference, release the buffer.
124 if (refcount == 0) {
125 uint8_t *p = this->dataPointer() - AsmJSPageSize;
126 JS_ASSERT(uintptr_t(p) % AsmJSPageSize == 0);
127 #ifdef JS_CPU_X64
128 UnmapMemory(p, AsmJSMappedSize);
129 # if defined(MOZ_VALGRIND) \
130 && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
131 // Tell Valgrind/Memcheck to recommence reporting accesses in the
132 // previously-inaccessible region.
133 if (AsmJSMappedSize > 0) {
134 VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(p, AsmJSMappedSize);
135 }
136 # endif
137 #else
138 UnmapMemory(p, this->length + AsmJSPageSize);
139 #endif
140 }
141 }
143 /*
144 * SharedArrayBufferObject
145 */
146 bool
147 js::IsSharedArrayBuffer(HandleValue v)
148 {
149 return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
150 }
152 MOZ_ALWAYS_INLINE bool
153 SharedArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
154 {
155 JS_ASSERT(IsSharedArrayBuffer(args.thisv()));
156 args.rval().setInt32(args.thisv().toObject().as<SharedArrayBufferObject>().byteLength());
157 return true;
158 }
160 bool
161 SharedArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
162 {
163 CallArgs args = CallArgsFromVp(argc, vp);
164 return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
165 }
167 bool
168 SharedArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
169 {
170 int32_t length = 0;
171 CallArgs args = CallArgsFromVp(argc, vp);
172 if (args.length() > 0 && !ToInt32(cx, args[0], &length))
173 return false;
175 if (length < 0) {
176 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
177 return false;
178 }
180 JSObject *bufobj = New(cx, uint32_t(length));
181 if (!bufobj)
182 return false;
183 args.rval().setObject(*bufobj);
184 return true;
185 }
187 JSObject *
188 SharedArrayBufferObject::New(JSContext *cx, uint32_t length)
189 {
190 if (!IsValidAsmJSHeapLength(length)) {
191 ScopedJSFreePtr<char> msg(
192 JS_smprintf("SharedArrayBuffer byteLength 0x%x is not a valid length. The next valid "
193 "length is 0x%x", length, RoundUpToNextValidAsmJSHeapLength(length)));
194 JS_ReportError(cx, msg.get());
195 return nullptr;
196 }
198 SharedArrayRawBuffer *buffer = SharedArrayRawBuffer::New(length);
199 if (!buffer)
200 return nullptr;
202 return New(cx, buffer);
203 }
205 JSObject *
206 SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
207 {
208 Rooted<SharedArrayBufferObject*> obj(cx, NewBuiltinClassInstance<SharedArrayBufferObject>(cx));
209 if (!obj)
210 return nullptr;
212 JS_ASSERT(obj->getClass() == &class_);
214 obj->initialize(buffer->byteLength(), nullptr, DoesntOwnData);
216 obj->acceptRawBuffer(buffer);
217 obj->setIsSharedArrayBuffer();
219 return obj;
220 }
222 void
223 SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer *buffer)
224 {
225 setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, PrivateValue(buffer));
226 }
228 void
229 SharedArrayBufferObject::dropRawBuffer()
230 {
231 setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, UndefinedValue());
232 }
234 SharedArrayRawBuffer *
235 SharedArrayBufferObject::rawBufferObject() const
236 {
237 // RAWBUF_SLOT must be populated via acceptRawBuffer(),
238 // and the raw buffer must not have been dropped.
239 Value v = getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
240 return (SharedArrayRawBuffer *)v.toPrivate();
241 }
243 uint8_t *
244 SharedArrayBufferObject::dataPointer() const
245 {
246 return rawBufferObject()->dataPointer();
247 }
249 uint32_t
250 SharedArrayBufferObject::byteLength() const
251 {
252 return rawBufferObject()->byteLength();
253 }
255 void
256 SharedArrayBufferObject::Finalize(FreeOp *fop, JSObject *obj)
257 {
258 SharedArrayBufferObject &buf = obj->as<SharedArrayBufferObject>();
260 // Detect the case of failure during SharedArrayBufferObject creation,
261 // which causes a SharedArrayRawBuffer to never be attached.
262 Value v = buf.getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
263 if (!v.isUndefined()) {
264 buf.rawBufferObject()->dropReference();
265 buf.dropRawBuffer();
266 }
267 }
269 /*
270 * SharedArrayBufferObject
271 */
273 const Class SharedArrayBufferObject::protoClass = {
274 "SharedArrayBufferPrototype",
275 JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
276 JS_PropertyStub, /* addProperty */
277 JS_DeletePropertyStub, /* delProperty */
278 JS_PropertyStub, /* getProperty */
279 JS_StrictPropertyStub, /* setProperty */
280 JS_EnumerateStub,
281 JS_ResolveStub,
282 JS_ConvertStub
283 };
285 const Class SharedArrayBufferObject::class_ = {
286 "SharedArrayBuffer",
287 JSCLASS_IMPLEMENTS_BARRIERS |
288 JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
289 JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
290 JS_PropertyStub, /* addProperty */
291 JS_DeletePropertyStub, /* delProperty */
292 JS_PropertyStub, /* getProperty */
293 JS_StrictPropertyStub, /* setProperty */
294 JS_EnumerateStub,
295 JS_ResolveStub,
296 JS_ConvertStub,
297 SharedArrayBufferObject::Finalize,
298 nullptr, /* call */
299 nullptr, /* hasInstance */
300 nullptr, /* construct */
301 ArrayBufferObject::obj_trace,
302 JS_NULL_CLASS_SPEC,
303 JS_NULL_CLASS_EXT
304 };
306 JSObject *
307 js_InitSharedArrayBufferClass(JSContext *cx, HandleObject obj)
308 {
309 JS_ASSERT(obj->isNative());
310 Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
311 RootedObject proto(cx, global->createBlankPrototype(cx, &SharedArrayBufferObject::protoClass));
312 if (!proto)
313 return nullptr;
315 RootedFunction ctor(cx, global->createConstructor(cx, SharedArrayBufferObject::class_constructor,
316 cx->names().SharedArrayBuffer, 1));
317 if (!ctor)
318 return nullptr;
320 if (!LinkConstructorAndPrototype(cx, ctor, proto))
321 return nullptr;
323 RootedId byteLengthId(cx, NameToId(cx->names().byteLength));
324 unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
325 JSObject *getter = NewFunction(cx, NullPtr(), SharedArrayBufferObject::byteLengthGetter, 0,
326 JSFunction::NATIVE_FUN, global, NullPtr());
327 if (!getter)
328 return nullptr;
330 RootedValue value(cx, UndefinedValue());
331 if (!DefineNativeProperty(cx, proto, byteLengthId, value,
332 JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs))
333 {
334 return nullptr;
335 }
337 if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
338 return nullptr;
339 return proto;
340 }