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.
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 | } |