js/src/vm/SharedArrayObject.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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.

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

mercurial