Line data Source code
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/. */
6 :
7 : #include "vm/SavedStacks.h"
8 :
9 : #include "mozilla/ArrayUtils.h"
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/DebugOnly.h"
12 : #include "mozilla/Move.h"
13 :
14 : #include <algorithm>
15 : #include <math.h>
16 :
17 : #include "jsapi.h"
18 : #include "jscompartment.h"
19 : #include "jsfriendapi.h"
20 : #include "jshashutil.h"
21 : #include "jsmath.h"
22 : #include "jsnum.h"
23 : #include "jsscript.h"
24 :
25 : #include "gc/Marking.h"
26 : #include "gc/Policy.h"
27 : #include "gc/Rooting.h"
28 : #include "js/CharacterEncoding.h"
29 : #include "js/Vector.h"
30 : #include "vm/Debugger.h"
31 : #include "vm/GeckoProfiler.h"
32 : #include "vm/SavedFrame.h"
33 : #include "vm/StringBuffer.h"
34 : #include "vm/Time.h"
35 : #include "vm/WrapperObject.h"
36 :
37 : #include "jscntxtinlines.h"
38 :
39 : #include "vm/NativeObject-inl.h"
40 : #include "vm/Stack-inl.h"
41 :
42 : using mozilla::AddToHash;
43 : using mozilla::DebugOnly;
44 : using mozilla::HashString;
45 : using mozilla::Maybe;
46 : using mozilla::Move;
47 : using mozilla::Nothing;
48 : using mozilla::Some;
49 :
50 : namespace js {
51 :
52 : /**
53 : * Maximum number of saved frames returned for an async stack.
54 : */
55 : const uint32_t ASYNC_STACK_MAX_FRAME_COUNT = 60;
56 :
57 : /* static */ Maybe<LiveSavedFrameCache::FramePtr>
58 13980 : LiveSavedFrameCache::getFramePtr(FrameIter& iter)
59 : {
60 13980 : if (iter.hasUsableAbstractFramePtr())
61 13140 : return Some(FramePtr(iter.abstractFramePtr()));
62 :
63 840 : if (iter.isPhysicalIonFrame())
64 840 : return Some(FramePtr(iter.physicalIonFrame()));
65 :
66 0 : return Nothing();
67 : }
68 :
69 : void
70 106 : LiveSavedFrameCache::trace(JSTracer* trc)
71 : {
72 106 : if (!initialized())
73 74 : return;
74 :
75 243 : for (auto* entry = frames->begin(); entry < frames->end(); entry++) {
76 211 : TraceEdge(trc,
77 : &entry->savedFrame,
78 211 : "LiveSavedFrameCache::frames SavedFrame");
79 : }
80 : }
81 :
82 : bool
83 2641 : LiveSavedFrameCache::insert(JSContext* cx, FramePtr& framePtr, jsbytecode* pc,
84 : HandleSavedFrame savedFrame)
85 : {
86 2641 : MOZ_ASSERT(initialized());
87 :
88 2641 : if (!frames->emplaceBack(framePtr, pc, savedFrame)) {
89 0 : ReportOutOfMemory(cx);
90 0 : return false;
91 : }
92 :
93 : // Safe to dereference the cache key because the stack frames are still
94 : // live. After this point, they should never be dereferenced again.
95 2641 : if (framePtr.is<AbstractFramePtr>())
96 2641 : framePtr.as<AbstractFramePtr>().setHasCachedSavedFrame();
97 : else
98 0 : framePtr.as<jit::CommonFrameLayout*>()->setHasCachedSavedFrame();
99 :
100 2641 : return true;
101 : }
102 :
103 : void
104 1517 : LiveSavedFrameCache::find(JSContext* cx, FrameIter& frameIter, MutableHandleSavedFrame frame) const
105 : {
106 1517 : MOZ_ASSERT(initialized());
107 1517 : MOZ_ASSERT(!frameIter.done());
108 1517 : MOZ_ASSERT(frameIter.hasCachedSavedFrame());
109 :
110 1861 : Maybe<FramePtr> maybeFramePtr = getFramePtr(frameIter);
111 1517 : MOZ_ASSERT(maybeFramePtr.isSome());
112 :
113 1861 : FramePtr framePtr(*maybeFramePtr);
114 1517 : jsbytecode* pc = frameIter.pc();
115 1517 : size_t numberStillValid = 0;
116 :
117 1517 : frame.set(nullptr);
118 2813 : for (auto* p = frames->begin(); p < frames->end(); p++) {
119 1640 : numberStillValid++;
120 1640 : if (framePtr == p->framePtr && pc == p->pc) {
121 344 : frame.set(p->savedFrame);
122 344 : break;
123 : }
124 : }
125 :
126 1517 : if (!frame) {
127 1173 : frames->clear();
128 1173 : return;
129 : }
130 :
131 344 : MOZ_ASSERT(0 < numberStillValid && numberStillValid <= frames->length());
132 :
133 344 : if (frame->compartment() != cx->compartment()) {
134 105 : frame.set(nullptr);
135 105 : numberStillValid--;
136 : }
137 :
138 : // Everything after the cached SavedFrame are stale younger frames we have
139 : // since popped.
140 344 : frames->shrinkBy(frames->length() - numberStillValid);
141 : }
142 :
143 17457 : struct SavedFrame::Lookup {
144 12463 : Lookup(JSAtom* source, uint32_t line, uint32_t column,
145 : JSAtom* functionDisplayName, JSAtom* asyncCause, SavedFrame* parent,
146 : JSPrincipals* principals,
147 : const Maybe<LiveSavedFrameCache::FramePtr>& framePtr = Nothing(),
148 : jsbytecode* pc = nullptr, Activation* activation = nullptr)
149 12463 : : source(source),
150 : line(line),
151 : column(column),
152 : functionDisplayName(functionDisplayName),
153 : asyncCause(asyncCause),
154 : parent(parent),
155 : principals(principals),
156 : framePtr(framePtr),
157 : pc(pc),
158 12463 : activation(activation)
159 : {
160 12463 : MOZ_ASSERT(source);
161 12463 : MOZ_ASSERT_IF(framePtr.isSome(), activation);
162 12463 : MOZ_ASSERT_IF(framePtr.isSome() && !activation->isWasm(), pc);
163 :
164 : #ifdef JS_MORE_DETERMINISTIC
165 : column = 0;
166 : #endif
167 12463 : }
168 :
169 2200 : explicit Lookup(SavedFrame& savedFrame)
170 2200 : : source(savedFrame.getSource()),
171 2200 : line(savedFrame.getLine()),
172 2200 : column(savedFrame.getColumn()),
173 2200 : functionDisplayName(savedFrame.getFunctionDisplayName()),
174 2200 : asyncCause(savedFrame.getAsyncCause()),
175 2200 : parent(savedFrame.getParent()),
176 2200 : principals(savedFrame.getPrincipals()),
177 2200 : framePtr(Nothing()),
178 : pc(nullptr),
179 17600 : activation(nullptr)
180 : {
181 2200 : MOZ_ASSERT(source);
182 2200 : }
183 :
184 : JSAtom* source;
185 : uint32_t line;
186 : uint32_t column;
187 : JSAtom* functionDisplayName;
188 : JSAtom* asyncCause;
189 : SavedFrame* parent;
190 : JSPrincipals* principals;
191 :
192 : // These are used only by the LiveSavedFrameCache and not used for identity or
193 : // hashing.
194 : Maybe<LiveSavedFrameCache::FramePtr> framePtr;
195 : jsbytecode* pc;
196 : Activation* activation;
197 :
198 0 : void trace(JSTracer* trc) {
199 0 : TraceManuallyBarrieredEdge(trc, &source, "SavedFrame::Lookup::source");
200 0 : if (functionDisplayName) {
201 0 : TraceManuallyBarrieredEdge(trc, &functionDisplayName,
202 0 : "SavedFrame::Lookup::functionDisplayName");
203 : }
204 0 : if (asyncCause)
205 0 : TraceManuallyBarrieredEdge(trc, &asyncCause, "SavedFrame::Lookup::asyncCause");
206 0 : if (parent)
207 0 : TraceManuallyBarrieredEdge(trc, &parent, "SavedFrame::Lookup::parent");
208 0 : }
209 : };
210 :
211 3277 : class MOZ_STACK_CLASS SavedFrame::AutoLookupVector : public JS::CustomAutoRooter {
212 : public:
213 3277 : explicit AutoLookupVector(JSContext* cx)
214 3277 : : JS::CustomAutoRooter(cx),
215 3277 : lookups(cx)
216 3277 : { }
217 :
218 : typedef Vector<Lookup, ASYNC_STACK_MAX_FRAME_COUNT> LookupVector;
219 18256 : inline LookupVector* operator->() { return &lookups; }
220 14053 : inline HandleLookup operator[](size_t i) { return HandleLookup(lookups[i]); }
221 :
222 : private:
223 : LookupVector lookups;
224 :
225 0 : virtual void trace(JSTracer* trc) {
226 0 : for (size_t i = 0; i < lookups.length(); i++)
227 0 : lookups[i].trace(trc);
228 0 : }
229 : };
230 :
231 : /* static */ bool
232 0 : SavedFrame::HashPolicy::hasHash(const Lookup& l)
233 : {
234 0 : return SavedFramePtrHasher::hasHash(l.parent);
235 : }
236 :
237 : /* static */ bool
238 14053 : SavedFrame::HashPolicy::ensureHash(const Lookup& l)
239 : {
240 14053 : return SavedFramePtrHasher::ensureHash(l.parent);
241 : }
242 :
243 : /* static */ HashNumber
244 16069 : SavedFrame::HashPolicy::hash(const Lookup& lookup)
245 : {
246 32138 : JS::AutoCheckCannotGC nogc;
247 : // Assume that we can take line mod 2^32 without losing anything of
248 : // interest. If that assumption changes, we'll just need to start with 0
249 : // and add another overload of AddToHash with more arguments.
250 32138 : return AddToHash(lookup.line,
251 16069 : lookup.column,
252 16069 : lookup.source,
253 16069 : lookup.functionDisplayName,
254 16069 : lookup.asyncCause,
255 : SavedFramePtrHasher::hash(lookup.parent),
256 32138 : JSPrincipalsPtrHasher::hash(lookup.principals));
257 : }
258 :
259 : /* static */ bool
260 12037 : SavedFrame::HashPolicy::match(SavedFrame* existing, const Lookup& lookup)
261 : {
262 12037 : MOZ_ASSERT(existing);
263 :
264 12037 : if (existing->getLine() != lookup.line)
265 0 : return false;
266 :
267 12037 : if (existing->getColumn() != lookup.column)
268 0 : return false;
269 :
270 12037 : if (existing->getParent() != lookup.parent)
271 0 : return false;
272 :
273 12037 : if (existing->getPrincipals() != lookup.principals)
274 0 : return false;
275 :
276 12037 : JSAtom* source = existing->getSource();
277 12037 : if (source != lookup.source)
278 0 : return false;
279 :
280 12037 : JSAtom* functionDisplayName = existing->getFunctionDisplayName();
281 12037 : if (functionDisplayName != lookup.functionDisplayName)
282 0 : return false;
283 :
284 12037 : JSAtom* asyncCause = existing->getAsyncCause();
285 12037 : if (asyncCause != lookup.asyncCause)
286 0 : return false;
287 :
288 12037 : return true;
289 : }
290 :
291 : /* static */ void
292 0 : SavedFrame::HashPolicy::rekey(Key& key, const Key& newKey)
293 : {
294 0 : key = newKey;
295 0 : }
296 :
297 : /* static */ bool
298 62 : SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto)
299 : {
300 : // The only object with the SavedFrame::class_ that doesn't have a source
301 : // should be the prototype.
302 62 : proto->as<NativeObject>().setReservedSlot(SavedFrame::JSSLOT_SOURCE, NullValue());
303 :
304 62 : return FreezeObject(cx, proto);
305 : }
306 :
307 : static const ClassOps SavedFrameClassOps = {
308 : nullptr, // addProperty
309 : nullptr, // delProperty
310 : nullptr, // getProperty
311 : nullptr, // setProperty
312 : nullptr, // enumerate
313 : nullptr, // newEnumerate
314 : nullptr, // resolve
315 : nullptr, // mayResolve
316 : SavedFrame::finalize, // finalize
317 : nullptr, // call
318 : nullptr, // hasInstance
319 : nullptr, // construct
320 : nullptr, // trace
321 : };
322 :
323 : const ClassSpec SavedFrame::classSpec_ = {
324 : GenericCreateConstructor<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
325 : GenericCreatePrototype,
326 : SavedFrame::staticFunctions,
327 : nullptr,
328 : SavedFrame::protoFunctions,
329 : SavedFrame::protoAccessors,
330 : SavedFrame::finishSavedFrameInit,
331 : ClassSpec::DontDefineConstructor
332 : };
333 :
334 : /* static */ const Class SavedFrame::class_ = {
335 : "SavedFrame",
336 : JSCLASS_HAS_PRIVATE |
337 : JSCLASS_HAS_RESERVED_SLOTS(SavedFrame::JSSLOT_COUNT) |
338 : JSCLASS_HAS_CACHED_PROTO(JSProto_SavedFrame) |
339 : JSCLASS_IS_ANONYMOUS |
340 : JSCLASS_FOREGROUND_FINALIZE,
341 : &SavedFrameClassOps,
342 : &SavedFrame::classSpec_
343 : };
344 :
345 : /* static */ const JSFunctionSpec
346 : SavedFrame::staticFunctions[] = {
347 : JS_FS_END
348 : };
349 :
350 : /* static */ const JSFunctionSpec
351 : SavedFrame::protoFunctions[] = {
352 : JS_FN("constructor", SavedFrame::construct, 0, 0),
353 : JS_FN("toString", SavedFrame::toStringMethod, 0, 0),
354 : JS_FS_END
355 : };
356 :
357 : /* static */ const JSPropertySpec
358 : SavedFrame::protoAccessors[] = {
359 : JS_PSG("source", SavedFrame::sourceProperty, 0),
360 : JS_PSG("line", SavedFrame::lineProperty, 0),
361 : JS_PSG("column", SavedFrame::columnProperty, 0),
362 : JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
363 : JS_PSG("asyncCause", SavedFrame::asyncCauseProperty, 0),
364 : JS_PSG("asyncParent", SavedFrame::asyncParentProperty, 0),
365 : JS_PSG("parent", SavedFrame::parentProperty, 0),
366 : JS_PS_END
367 : };
368 :
369 : /* static */ void
370 0 : SavedFrame::finalize(FreeOp* fop, JSObject* obj)
371 : {
372 0 : MOZ_ASSERT(fop->onActiveCooperatingThread());
373 0 : JSPrincipals* p = obj->as<SavedFrame>().getPrincipals();
374 0 : if (p) {
375 0 : JSRuntime* rt = obj->runtimeFromActiveCooperatingThread();
376 0 : JS_DropPrincipals(rt->activeContextFromOwnThread(), p);
377 : }
378 0 : }
379 :
380 : JSAtom*
381 14290 : SavedFrame::getSource()
382 : {
383 14290 : const Value& v = getReservedSlot(JSSLOT_SOURCE);
384 14290 : JSString* s = v.toString();
385 14290 : return &s->asAtom();
386 : }
387 :
388 : uint32_t
389 14254 : SavedFrame::getLine()
390 : {
391 14254 : const Value& v = getReservedSlot(JSSLOT_LINE);
392 14254 : return v.toPrivateUint32();
393 : }
394 :
395 : uint32_t
396 14238 : SavedFrame::getColumn()
397 : {
398 14238 : const Value& v = getReservedSlot(JSSLOT_COLUMN);
399 14238 : return v.toPrivateUint32();
400 : }
401 :
402 : JSAtom*
403 14238 : SavedFrame::getFunctionDisplayName()
404 : {
405 14238 : const Value& v = getReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME);
406 14238 : if (v.isNull())
407 334 : return nullptr;
408 13904 : JSString* s = v.toString();
409 13904 : return &s->asAtom();
410 : }
411 :
412 : JSAtom*
413 14238 : SavedFrame::getAsyncCause()
414 : {
415 14238 : const Value& v = getReservedSlot(JSSLOT_ASYNCCAUSE);
416 14238 : if (v.isNull())
417 13574 : return nullptr;
418 664 : JSString* s = v.toString();
419 664 : return &s->asAtom();
420 : }
421 :
422 : SavedFrame*
423 16437 : SavedFrame::getParent() const
424 : {
425 16437 : const Value& v = getReservedSlot(JSSLOT_PARENT);
426 16437 : return v.isObject() ? &v.toObject().as<SavedFrame>() : nullptr;
427 : }
428 :
429 : JSPrincipals*
430 14274 : SavedFrame::getPrincipals()
431 : {
432 14274 : const Value& v = getReservedSlot(JSSLOT_PRINCIPALS);
433 14274 : if (v.isUndefined())
434 0 : return nullptr;
435 14274 : return static_cast<JSPrincipals*>(v.toPrivate());
436 : }
437 :
438 : void
439 2016 : SavedFrame::initSource(JSAtom* source)
440 : {
441 2016 : MOZ_ASSERT(source);
442 2016 : initReservedSlot(JSSLOT_SOURCE, StringValue(source));
443 2016 : }
444 :
445 : void
446 2016 : SavedFrame::initLine(uint32_t line)
447 : {
448 2016 : initReservedSlot(JSSLOT_LINE, PrivateUint32Value(line));
449 2016 : }
450 :
451 : void
452 2016 : SavedFrame::initColumn(uint32_t column)
453 : {
454 : #ifdef JS_MORE_DETERMINISTIC
455 : column = 0;
456 : #endif
457 2016 : initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column));
458 2016 : }
459 :
460 : void
461 2016 : SavedFrame::initPrincipals(JSPrincipals* principals)
462 : {
463 2016 : if (principals)
464 2016 : JS_HoldPrincipals(principals);
465 2016 : initPrincipalsAlreadyHeld(principals);
466 2016 : }
467 :
468 : void
469 2016 : SavedFrame::initPrincipalsAlreadyHeld(JSPrincipals* principals)
470 : {
471 2016 : MOZ_ASSERT_IF(principals, principals->refcount > 0);
472 2016 : initReservedSlot(JSSLOT_PRINCIPALS, PrivateValue(principals));
473 2016 : }
474 :
475 : void
476 2016 : SavedFrame::initFunctionDisplayName(JSAtom* maybeName)
477 : {
478 2016 : initReservedSlot(JSSLOT_FUNCTIONDISPLAYNAME, maybeName ? StringValue(maybeName) : NullValue());
479 2016 : }
480 :
481 : void
482 2016 : SavedFrame::initAsyncCause(JSAtom* maybeCause)
483 : {
484 2016 : initReservedSlot(JSSLOT_ASYNCCAUSE, maybeCause ? StringValue(maybeCause) : NullValue());
485 2016 : }
486 :
487 : void
488 2016 : SavedFrame::initParent(SavedFrame* maybeParent)
489 : {
490 2016 : initReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(maybeParent));
491 2016 : }
492 :
493 : void
494 2016 : SavedFrame::initFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup)
495 : {
496 : // Make sure any atoms used in the lookup are marked in the current zone.
497 : // Normally we would try to keep these mark bits up to date around the
498 : // points where the context moves between compartments, but Lookups live on
499 : // the stack (where the atoms are kept alive regardless) and this is a
500 : // more convenient pinchpoint.
501 2016 : if (lookup->source)
502 2016 : cx->markAtom(lookup->source);
503 2016 : if (lookup->functionDisplayName)
504 1942 : cx->markAtom(lookup->functionDisplayName);
505 2016 : if (lookup->asyncCause)
506 172 : cx->markAtom(lookup->asyncCause);
507 :
508 2016 : initSource(lookup->source);
509 2016 : initLine(lookup->line);
510 2016 : initColumn(lookup->column);
511 2016 : initFunctionDisplayName(lookup->functionDisplayName);
512 2016 : initAsyncCause(lookup->asyncCause);
513 2016 : initParent(lookup->parent);
514 2016 : initPrincipals(lookup->principals);
515 2016 : }
516 :
517 : /* static */ SavedFrame*
518 2016 : SavedFrame::create(JSContext* cx)
519 : {
520 4032 : RootedGlobalObject global(cx, cx->global());
521 2016 : assertSameCompartment(cx, global);
522 :
523 : // Ensure that we don't try to capture the stack again in the
524 : // `SavedStacksMetadataBuilder` for this new SavedFrame object, and
525 : // accidentally cause O(n^2) behavior.
526 4032 : SavedStacks::AutoReentrancyGuard guard(cx->compartment()->savedStacks());
527 :
528 4032 : RootedNativeObject proto(cx, GlobalObject::getOrCreateSavedFramePrototype(cx, global));
529 2016 : if (!proto)
530 0 : return nullptr;
531 2016 : assertSameCompartment(cx, proto);
532 :
533 4032 : RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto,
534 4032 : TenuredObject));
535 2016 : if (!frameObj)
536 0 : return nullptr;
537 :
538 2016 : return &frameObj->as<SavedFrame>();
539 : }
540 :
541 : bool
542 36 : SavedFrame::isSelfHosted(JSContext* cx)
543 : {
544 36 : JSAtom* source = getSource();
545 36 : return source == cx->names().selfHosted;
546 : }
547 :
548 : /* static */ bool
549 0 : SavedFrame::construct(JSContext* cx, unsigned argc, Value* vp)
550 : {
551 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
552 0 : "SavedFrame");
553 0 : return false;
554 : }
555 :
556 : static bool
557 37 : SavedFrameSubsumedByCaller(JSContext* cx, HandleSavedFrame frame)
558 : {
559 37 : auto subsumes = cx->runtime()->securityCallbacks->subsumes;
560 37 : if (!subsumes)
561 0 : return true;
562 :
563 37 : auto currentCompartmentPrincipals = cx->compartment()->principals();
564 37 : MOZ_ASSERT(!ReconstructedSavedFramePrincipals::is(currentCompartmentPrincipals));
565 :
566 37 : auto framePrincipals = frame->getPrincipals();
567 :
568 : // Handle SavedFrames that have been reconstructed from stacks in a heap
569 : // snapshot.
570 37 : if (framePrincipals == &ReconstructedSavedFramePrincipals::IsSystem)
571 0 : return cx->runningWithTrustedPrincipals();
572 37 : if (framePrincipals == &ReconstructedSavedFramePrincipals::IsNotSystem)
573 0 : return true;
574 :
575 37 : return subsumes(currentCompartmentPrincipals, framePrincipals);
576 : }
577 :
578 : // Return the first SavedFrame in the chain that starts with |frame| whose
579 : // principals are subsumed by |principals|, according to |subsumes|. If there is
580 : // no such frame, return nullptr. |skippedAsync| is set to true if any of the
581 : // skipped frames had the |asyncCause| property set, otherwise it is explicitly
582 : // set to false.
583 : static SavedFrame*
584 37 : GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, JS::SavedFrameSelfHosted selfHosted,
585 : bool& skippedAsync)
586 : {
587 37 : skippedAsync = false;
588 :
589 74 : RootedSavedFrame rootedFrame(cx, frame);
590 37 : while (rootedFrame) {
591 110 : if ((selfHosted == JS::SavedFrameSelfHosted::Include ||
592 111 : !rootedFrame->isSelfHosted(cx)) &&
593 148 : SavedFrameSubsumedByCaller(cx, rootedFrame))
594 : {
595 37 : return rootedFrame;
596 : }
597 :
598 0 : if (rootedFrame->getAsyncCause())
599 0 : skippedAsync = true;
600 :
601 0 : rootedFrame = rootedFrame->getParent();
602 : }
603 :
604 0 : return nullptr;
605 : }
606 :
607 : JS_FRIEND_API(JSObject*)
608 0 : GetFirstSubsumedSavedFrame(JSContext* cx, HandleObject savedFrame,
609 : JS::SavedFrameSelfHosted selfHosted)
610 : {
611 0 : if (!savedFrame)
612 0 : return nullptr;
613 : bool skippedAsync;
614 0 : RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
615 0 : return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
616 : }
617 :
618 : static MOZ_MUST_USE bool
619 0 : SavedFrame_checkThis(JSContext* cx, CallArgs& args, const char* fnName,
620 : MutableHandleObject frame)
621 : {
622 0 : const Value& thisValue = args.thisv();
623 :
624 0 : if (!thisValue.isObject()) {
625 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
626 0 : InformalValueTypeName(thisValue));
627 0 : return false;
628 : }
629 :
630 0 : JSObject* thisObject = CheckedUnwrap(&thisValue.toObject());
631 0 : if (!thisObject || !thisObject->is<SavedFrame>()) {
632 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
633 0 : SavedFrame::class_.name, fnName,
634 0 : thisObject ? thisObject->getClass()->name : "object");
635 0 : return false;
636 : }
637 :
638 : // Check for SavedFrame.prototype, which has the same class as SavedFrame
639 : // instances, however doesn't actually represent a captured stack frame. It
640 : // is the only object that is<SavedFrame>() but doesn't have a source.
641 0 : if (!SavedFrame::isSavedFrameAndNotProto(*thisObject)) {
642 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
643 0 : SavedFrame::class_.name, fnName, "prototype object");
644 0 : return false;
645 : }
646 :
647 : // Now set "frame" to the actual object we were invoked in (which may be a
648 : // wrapper), not the unwrapped version. Consumers will need to know what
649 : // that original object was, and will do principal checks as needed.
650 0 : frame.set(&thisValue.toObject());
651 0 : return true;
652 : }
653 :
654 : // Get the SavedFrame * from the current this value and handle any errors that
655 : // might occur therein.
656 : //
657 : // These parameters must already exist when calling this macro:
658 : // - JSContext* cx
659 : // - unsigned argc
660 : // - Value* vp
661 : // - const char* fnName
662 : // These parameters will be defined after calling this macro:
663 : // - CallArgs args
664 : // - Rooted<SavedFrame*> frame (will be non-null)
665 : #define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame) \
666 : CallArgs args = CallArgsFromVp(argc, vp); \
667 : RootedObject frame(cx); \
668 : if (!SavedFrame_checkThis(cx, args, fnName, &frame)) \
669 : return false;
670 :
671 : } /* namespace js */
672 :
673 : namespace JS {
674 :
675 : namespace {
676 :
677 : // It's possible that our caller is privileged (and hence would see the entire
678 : // stack) but we're working with an SavedFrame object that was captured in
679 : // unprivileged code. If so, drop privileges down to its level. The idea is
680 : // that this way devtools code that's asking an exception object for a stack to
681 : // display will end up with the stack the web developer would see via doing
682 : // .stack in a web page, with Firefox implementation details excluded.
683 : //
684 : // We want callers to pass us the object they were actually passed, not an
685 : // unwrapped form of it. That way Xray access to SavedFrame objects should not
686 : // be affected by AutoMaybeEnterFrameCompartment and the only things that will
687 : // be affected will be cases in which privileged code works with some C++ object
688 : // that then pokes at an unprivileged StackFrame it has on hand.
689 37 : class MOZ_STACK_CLASS AutoMaybeEnterFrameCompartment
690 : {
691 : public:
692 37 : AutoMaybeEnterFrameCompartment(JSContext* cx,
693 : HandleObject obj
694 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
695 37 : {
696 37 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
697 :
698 37 : MOZ_RELEASE_ASSERT(cx->compartment());
699 37 : if (obj)
700 37 : MOZ_RELEASE_ASSERT(obj->compartment());
701 :
702 : // Note that obj might be null here, since we're doing this before
703 : // UnwrapSavedFrame.
704 37 : if (obj && cx->compartment() != obj->compartment())
705 : {
706 0 : JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
707 0 : if (subsumes && subsumes(cx->compartment()->principals(),
708 0 : obj->compartment()->principals()))
709 : {
710 0 : ac_.emplace(cx, obj);
711 : }
712 : }
713 37 : }
714 :
715 : private:
716 : Maybe<JSAutoCompartment> ac_;
717 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
718 : };
719 :
720 : } // namespace
721 :
722 : static inline js::SavedFrame*
723 37 : UnwrapSavedFrame(JSContext* cx, HandleObject obj, SavedFrameSelfHosted selfHosted,
724 : bool& skippedAsync)
725 : {
726 37 : if (!obj)
727 0 : return nullptr;
728 :
729 74 : RootedObject savedFrameObj(cx, CheckedUnwrap(obj));
730 37 : if (!savedFrameObj)
731 0 : return nullptr;
732 :
733 37 : MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
734 74 : js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
735 37 : return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
736 : }
737 :
738 : JS_PUBLIC_API(SavedFrameResult)
739 17 : GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
740 : SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
741 : {
742 17 : js::AssertHeapIsIdle();
743 34 : CHECK_REQUEST(cx);
744 17 : MOZ_RELEASE_ASSERT(cx->compartment());
745 :
746 : {
747 34 : AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
748 : bool skippedAsync;
749 34 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
750 17 : if (!frame) {
751 0 : sourcep.set(cx->runtime()->emptyString);
752 0 : return SavedFrameResult::AccessDenied;
753 : }
754 17 : sourcep.set(frame->getSource());
755 : }
756 17 : if (sourcep->isAtom())
757 17 : cx->markAtom(&sourcep->asAtom());
758 17 : return SavedFrameResult::Ok;
759 : }
760 :
761 : JS_PUBLIC_API(SavedFrameResult)
762 17 : GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
763 : SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
764 : {
765 17 : js::AssertHeapIsIdle();
766 34 : CHECK_REQUEST(cx);
767 17 : MOZ_RELEASE_ASSERT(cx->compartment());
768 17 : MOZ_ASSERT(linep);
769 :
770 34 : AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
771 : bool skippedAsync;
772 34 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
773 17 : if (!frame) {
774 0 : *linep = 0;
775 0 : return SavedFrameResult::AccessDenied;
776 : }
777 17 : *linep = frame->getLine();
778 17 : return SavedFrameResult::Ok;
779 : }
780 :
781 : JS_PUBLIC_API(SavedFrameResult)
782 1 : GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
783 : SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
784 : {
785 1 : js::AssertHeapIsIdle();
786 2 : CHECK_REQUEST(cx);
787 1 : MOZ_RELEASE_ASSERT(cx->compartment());
788 1 : MOZ_ASSERT(columnp);
789 :
790 2 : AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
791 : bool skippedAsync;
792 2 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
793 1 : if (!frame) {
794 0 : *columnp = 0;
795 0 : return SavedFrameResult::AccessDenied;
796 : }
797 1 : *columnp = frame->getColumn();
798 1 : return SavedFrameResult::Ok;
799 : }
800 :
801 : JS_PUBLIC_API(SavedFrameResult)
802 1 : GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
803 : SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
804 : {
805 1 : js::AssertHeapIsIdle();
806 2 : CHECK_REQUEST(cx);
807 1 : MOZ_RELEASE_ASSERT(cx->compartment());
808 :
809 : {
810 2 : AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
811 : bool skippedAsync;
812 2 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
813 1 : if (!frame) {
814 0 : namep.set(nullptr);
815 0 : return SavedFrameResult::AccessDenied;
816 : }
817 1 : namep.set(frame->getFunctionDisplayName());
818 : }
819 1 : if (namep && namep->isAtom())
820 1 : cx->markAtom(&namep->asAtom());
821 1 : return SavedFrameResult::Ok;
822 : }
823 :
824 : JS_PUBLIC_API(SavedFrameResult)
825 1 : GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
826 : SavedFrameSelfHosted unused_ /* = SavedFrameSelfHosted::Include */)
827 : {
828 1 : js::AssertHeapIsIdle();
829 2 : CHECK_REQUEST(cx);
830 1 : MOZ_RELEASE_ASSERT(cx->compartment());
831 :
832 : {
833 2 : AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
834 : bool skippedAsync;
835 : // This function is always called with self-hosted frames excluded by
836 : // GetValueIfNotCached in dom/bindings/Exceptions.cpp. However, we want
837 : // to include them because our Promise implementation causes us to have
838 : // the async cause on a self-hosted frame. So we just ignore the
839 : // parameter and always include self-hosted frames.
840 2 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, SavedFrameSelfHosted::Include,
841 2 : skippedAsync));
842 1 : if (!frame) {
843 0 : asyncCausep.set(nullptr);
844 0 : return SavedFrameResult::AccessDenied;
845 : }
846 1 : asyncCausep.set(frame->getAsyncCause());
847 1 : if (!asyncCausep && skippedAsync)
848 0 : asyncCausep.set(cx->names().Async);
849 : }
850 1 : if (asyncCausep && asyncCausep->isAtom())
851 0 : cx->markAtom(&asyncCausep->asAtom());
852 1 : return SavedFrameResult::Ok;
853 : }
854 :
855 : JS_PUBLIC_API(SavedFrameResult)
856 0 : GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
857 : SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
858 : {
859 0 : js::AssertHeapIsIdle();
860 0 : CHECK_REQUEST(cx);
861 0 : MOZ_RELEASE_ASSERT(cx->compartment());
862 :
863 0 : AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
864 : bool skippedAsync;
865 0 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
866 0 : if (!frame) {
867 0 : asyncParentp.set(nullptr);
868 0 : return SavedFrameResult::AccessDenied;
869 : }
870 0 : js::RootedSavedFrame parent(cx, frame->getParent());
871 :
872 : // The current value of |skippedAsync| is not interesting, because we are
873 : // interested in whether we would cross any async parents to get from here
874 : // to the first subsumed parent frame instead.
875 0 : js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
876 0 : skippedAsync));
877 :
878 : // Even if |parent| is not subsumed, we still want to return a pointer to it
879 : // rather than |subsumedParent| so it can pick up any |asyncCause| from the
880 : // inaccessible part of the chain.
881 0 : if (subsumedParent && (subsumedParent->getAsyncCause() || skippedAsync))
882 0 : asyncParentp.set(parent);
883 : else
884 0 : asyncParentp.set(nullptr);
885 0 : return SavedFrameResult::Ok;
886 : }
887 :
888 : JS_PUBLIC_API(SavedFrameResult)
889 0 : GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
890 : SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
891 : {
892 0 : js::AssertHeapIsIdle();
893 0 : CHECK_REQUEST(cx);
894 0 : MOZ_RELEASE_ASSERT(cx->compartment());
895 :
896 0 : AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
897 : bool skippedAsync;
898 0 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
899 0 : if (!frame) {
900 0 : parentp.set(nullptr);
901 0 : return SavedFrameResult::AccessDenied;
902 : }
903 0 : js::RootedSavedFrame parent(cx, frame->getParent());
904 :
905 : // The current value of |skippedAsync| is not interesting, because we are
906 : // interested in whether we would cross any async parents to get from here
907 : // to the first subsumed parent frame instead.
908 0 : js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
909 0 : skippedAsync));
910 :
911 : // Even if |parent| is not subsumed, we still want to return a pointer to it
912 : // rather than |subsumedParent| so it can pick up any |asyncCause| from the
913 : // inaccessible part of the chain.
914 0 : if (subsumedParent && !(subsumedParent->getAsyncCause() || skippedAsync))
915 0 : parentp.set(parent);
916 : else
917 0 : parentp.set(nullptr);
918 0 : return SavedFrameResult::Ok;
919 : }
920 :
921 : static bool
922 0 : FormatSpiderMonkeyStackFrame(JSContext* cx, js::StringBuffer& sb,
923 : js::HandleSavedFrame frame, size_t indent,
924 : bool skippedAsync)
925 : {
926 0 : RootedString asyncCause(cx, frame->getAsyncCause());
927 0 : if (!asyncCause && skippedAsync)
928 0 : asyncCause.set(cx->names().Async);
929 :
930 0 : js::RootedAtom name(cx, frame->getFunctionDisplayName());
931 0 : return (!indent || sb.appendN(' ', indent))
932 0 : && (!asyncCause || (sb.append(asyncCause) && sb.append('*')))
933 0 : && (!name || sb.append(name))
934 0 : && sb.append('@')
935 0 : && sb.append(frame->getSource())
936 0 : && sb.append(':')
937 0 : && NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
938 0 : && sb.append(':')
939 0 : && NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
940 0 : && sb.append('\n');
941 : }
942 :
943 : static bool
944 0 : FormatV8StackFrame(JSContext* cx, js::StringBuffer& sb,
945 : js::HandleSavedFrame frame, size_t indent, bool lastFrame)
946 : {
947 0 : js::RootedAtom name(cx, frame->getFunctionDisplayName());
948 0 : return sb.appendN(' ', indent + 4)
949 0 : && sb.append('a')
950 0 : && sb.append('t')
951 0 : && sb.append(' ')
952 0 : && (!name || (sb.append(name) &&
953 0 : sb.append(' ') &&
954 0 : sb.append('(')))
955 0 : && sb.append(frame->getSource())
956 0 : && sb.append(':')
957 0 : && NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
958 0 : && sb.append(':')
959 0 : && NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
960 0 : && (!name || sb.append(')'))
961 0 : && (lastFrame || sb.append('\n'));
962 : }
963 :
964 : JS_PUBLIC_API(bool)
965 0 : BuildStackString(JSContext* cx, HandleObject stack, MutableHandleString stringp,
966 : size_t indent, js::StackFormat format)
967 : {
968 0 : js::AssertHeapIsIdle();
969 0 : CHECK_REQUEST(cx);
970 0 : MOZ_RELEASE_ASSERT(cx->compartment());
971 :
972 0 : js::StringBuffer sb(cx);
973 :
974 0 : if (format == js::StackFormat::Default)
975 0 : format = cx->runtime()->stackFormat();
976 0 : MOZ_ASSERT(format != js::StackFormat::Default);
977 :
978 : // Enter a new block to constrain the scope of possibly entering the stack's
979 : // compartment. This ensures that when we finish the StringBuffer, we are
980 : // back in the cx's original compartment, and fulfill our contract with
981 : // callers to place the output string in the cx's current compartment.
982 : {
983 0 : AutoMaybeEnterFrameCompartment ac(cx, stack);
984 : bool skippedAsync;
985 0 : js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, SavedFrameSelfHosted::Exclude,
986 0 : skippedAsync));
987 0 : if (!frame) {
988 0 : stringp.set(cx->runtime()->emptyString);
989 0 : return true;
990 : }
991 :
992 0 : js::RootedSavedFrame parent(cx);
993 0 : do {
994 0 : MOZ_ASSERT(SavedFrameSubsumedByCaller(cx, frame));
995 0 : MOZ_ASSERT(!frame->isSelfHosted(cx));
996 :
997 0 : parent = frame->getParent();
998 : bool skippedNextAsync;
999 0 : js::RootedSavedFrame nextFrame(cx, js::GetFirstSubsumedFrame(cx, parent,
1000 0 : SavedFrameSelfHosted::Exclude, skippedNextAsync));
1001 :
1002 0 : switch (format) {
1003 : case js::StackFormat::SpiderMonkey:
1004 0 : if (!FormatSpiderMonkeyStackFrame(cx, sb, frame, indent, skippedAsync))
1005 0 : return false;
1006 0 : break;
1007 : case js::StackFormat::V8:
1008 0 : if (!FormatV8StackFrame(cx, sb, frame, indent, !nextFrame))
1009 0 : return false;
1010 0 : break;
1011 : case js::StackFormat::Default:
1012 0 : MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected value");
1013 : break;
1014 : }
1015 :
1016 0 : frame = nextFrame;
1017 0 : skippedAsync = skippedNextAsync;
1018 0 : } while (frame);
1019 : }
1020 :
1021 0 : JSString* str = sb.finishString();
1022 0 : if (!str)
1023 0 : return false;
1024 0 : assertSameCompartment(cx, str);
1025 0 : stringp.set(str);
1026 0 : return true;
1027 : }
1028 :
1029 : JS_PUBLIC_API(bool)
1030 0 : IsSavedFrame(JSObject* obj)
1031 : {
1032 0 : if (!obj)
1033 0 : return false;
1034 :
1035 0 : JSObject* unwrapped = js::CheckedUnwrap(obj);
1036 0 : if (!unwrapped)
1037 0 : return false;
1038 :
1039 0 : return js::SavedFrame::isSavedFrameAndNotProto(*unwrapped);
1040 : }
1041 :
1042 : } /* namespace JS */
1043 :
1044 : namespace js {
1045 :
1046 : /* static */ bool
1047 0 : SavedFrame::sourceProperty(JSContext* cx, unsigned argc, Value* vp)
1048 : {
1049 0 : THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
1050 0 : RootedString source(cx);
1051 0 : if (JS::GetSavedFrameSource(cx, frame, &source) == JS::SavedFrameResult::Ok) {
1052 0 : if (!cx->compartment()->wrap(cx, &source))
1053 0 : return false;
1054 0 : args.rval().setString(source);
1055 : } else {
1056 0 : args.rval().setNull();
1057 : }
1058 0 : return true;
1059 : }
1060 :
1061 : /* static */ bool
1062 0 : SavedFrame::lineProperty(JSContext* cx, unsigned argc, Value* vp)
1063 : {
1064 0 : THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
1065 : uint32_t line;
1066 0 : if (JS::GetSavedFrameLine(cx, frame, &line) == JS::SavedFrameResult::Ok)
1067 0 : args.rval().setNumber(line);
1068 : else
1069 0 : args.rval().setNull();
1070 0 : return true;
1071 : }
1072 :
1073 : /* static */ bool
1074 0 : SavedFrame::columnProperty(JSContext* cx, unsigned argc, Value* vp)
1075 : {
1076 0 : THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
1077 : uint32_t column;
1078 0 : if (JS::GetSavedFrameColumn(cx, frame, &column) == JS::SavedFrameResult::Ok)
1079 0 : args.rval().setNumber(column);
1080 : else
1081 0 : args.rval().setNull();
1082 0 : return true;
1083 : }
1084 :
1085 : /* static */ bool
1086 0 : SavedFrame::functionDisplayNameProperty(JSContext* cx, unsigned argc, Value* vp)
1087 : {
1088 0 : THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
1089 0 : RootedString name(cx);
1090 0 : JS::SavedFrameResult result = JS::GetSavedFrameFunctionDisplayName(cx, frame, &name);
1091 0 : if (result == JS::SavedFrameResult::Ok && name) {
1092 0 : if (!cx->compartment()->wrap(cx, &name))
1093 0 : return false;
1094 0 : args.rval().setString(name);
1095 : } else {
1096 0 : args.rval().setNull();
1097 : }
1098 0 : return true;
1099 : }
1100 :
1101 : /* static */ bool
1102 0 : SavedFrame::asyncCauseProperty(JSContext* cx, unsigned argc, Value* vp)
1103 : {
1104 0 : THIS_SAVEDFRAME(cx, argc, vp, "(get asyncCause)", args, frame);
1105 0 : RootedString asyncCause(cx);
1106 0 : JS::SavedFrameResult result = JS::GetSavedFrameAsyncCause(cx, frame, &asyncCause);
1107 0 : if (result == JS::SavedFrameResult::Ok && asyncCause) {
1108 0 : if (!cx->compartment()->wrap(cx, &asyncCause))
1109 0 : return false;
1110 0 : args.rval().setString(asyncCause);
1111 : } else {
1112 0 : args.rval().setNull();
1113 : }
1114 0 : return true;
1115 : }
1116 :
1117 : /* static */ bool
1118 0 : SavedFrame::asyncParentProperty(JSContext* cx, unsigned argc, Value* vp)
1119 : {
1120 0 : THIS_SAVEDFRAME(cx, argc, vp, "(get asyncParent)", args, frame);
1121 0 : RootedObject asyncParent(cx);
1122 0 : (void) JS::GetSavedFrameAsyncParent(cx, frame, &asyncParent);
1123 0 : if (!cx->compartment()->wrap(cx, &asyncParent))
1124 0 : return false;
1125 0 : args.rval().setObjectOrNull(asyncParent);
1126 0 : return true;
1127 : }
1128 :
1129 : /* static */ bool
1130 0 : SavedFrame::parentProperty(JSContext* cx, unsigned argc, Value* vp)
1131 : {
1132 0 : THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
1133 0 : RootedObject parent(cx);
1134 0 : (void) JS::GetSavedFrameParent(cx, frame, &parent);
1135 0 : if (!cx->compartment()->wrap(cx, &parent))
1136 0 : return false;
1137 0 : args.rval().setObjectOrNull(parent);
1138 0 : return true;
1139 : }
1140 :
1141 : /* static */ bool
1142 0 : SavedFrame::toStringMethod(JSContext* cx, unsigned argc, Value* vp)
1143 : {
1144 0 : THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
1145 0 : RootedString string(cx);
1146 0 : if (!JS::BuildStackString(cx, frame, &string))
1147 0 : return false;
1148 0 : args.rval().setString(string);
1149 0 : return true;
1150 : }
1151 :
1152 : bool
1153 315 : SavedStacks::init()
1154 : {
1155 630 : return frames.init() &&
1156 630 : pcLocationMap.init();
1157 : }
1158 :
1159 : bool
1160 2961 : SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
1161 : JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
1162 : {
1163 2961 : MOZ_ASSERT(initialized());
1164 2961 : MOZ_RELEASE_ASSERT(cx->compartment());
1165 2961 : assertSameCompartment(cx, this);
1166 :
1167 11844 : if (creatingSavedFrame ||
1168 8883 : cx->isExceptionPending() ||
1169 23688 : !cx->global() ||
1170 8883 : !cx->global()->isStandardClassResolved(JSProto_Object))
1171 : {
1172 0 : frame.set(nullptr);
1173 0 : return true;
1174 : }
1175 :
1176 5922 : AutoGeckoProfilerEntry psuedoFrame(cx->runtime(), "js::SavedStacks::saveCurrentStack");
1177 5922 : FrameIter iter(cx);
1178 2961 : return insertFrames(cx, iter, frame, mozilla::Move(capture));
1179 : }
1180 :
1181 : bool
1182 0 : SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
1183 : MutableHandleSavedFrame adoptedStack, uint32_t maxFrameCount)
1184 : {
1185 0 : MOZ_ASSERT(initialized());
1186 0 : MOZ_RELEASE_ASSERT(cx->compartment());
1187 0 : assertSameCompartment(cx, this);
1188 :
1189 0 : RootedObject asyncStackObj(cx, CheckedUnwrap(asyncStack));
1190 0 : MOZ_RELEASE_ASSERT(asyncStackObj);
1191 0 : MOZ_RELEASE_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*asyncStackObj));
1192 0 : RootedSavedFrame frame(cx, &asyncStackObj->as<js::SavedFrame>());
1193 :
1194 0 : return adoptAsyncStack(cx, frame, asyncCause, adoptedStack, maxFrameCount);
1195 : }
1196 :
1197 : void
1198 0 : SavedStacks::sweep()
1199 : {
1200 0 : frames.sweep();
1201 0 : pcLocationMap.sweep();
1202 0 : }
1203 :
1204 : void
1205 26 : SavedStacks::trace(JSTracer* trc)
1206 : {
1207 26 : pcLocationMap.trace(trc);
1208 26 : }
1209 :
1210 : uint32_t
1211 0 : SavedStacks::count()
1212 : {
1213 0 : MOZ_ASSERT(initialized());
1214 0 : return frames.count();
1215 : }
1216 :
1217 : void
1218 15 : SavedStacks::clear()
1219 : {
1220 15 : frames.clear();
1221 15 : }
1222 :
1223 : size_t
1224 0 : SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
1225 : {
1226 0 : return frames.sizeOfExcludingThis(mallocSizeOf) +
1227 0 : pcLocationMap.sizeOfExcludingThis(mallocSizeOf);
1228 : }
1229 :
1230 : // Given that we have captured a stqck frame with the given principals and
1231 : // source, return true if the requested `StackCapture` has been satisfied and
1232 : // stack walking can halt. Return false otherwise (and stack walking and frame
1233 : // capturing should continue).
1234 : static inline bool
1235 12463 : captureIsSatisfied(JSContext* cx, JSPrincipals* principals, const JSAtom* source,
1236 : JS::StackCapture& capture)
1237 : {
1238 : class Matcher
1239 : {
1240 : JSContext* cx_;
1241 : JSPrincipals* framePrincipals_;
1242 : const JSAtom* frameSource_;
1243 :
1244 : public:
1245 12463 : Matcher(JSContext* cx, JSPrincipals* principals, const JSAtom* source)
1246 12463 : : cx_(cx)
1247 : , framePrincipals_(principals)
1248 12463 : , frameSource_(source)
1249 12463 : { }
1250 :
1251 0 : bool match(JS::FirstSubsumedFrame& target) {
1252 0 : auto subsumes = cx_->runtime()->securityCallbacks->subsumes;
1253 0 : return (!subsumes || subsumes(target.principals, framePrincipals_)) &&
1254 0 : (!target.ignoreSelfHosted || frameSource_ != cx_->names().selfHosted);
1255 : }
1256 :
1257 9822 : bool match(JS::MaxFrames& target) {
1258 9822 : return target.maxFrames == 1;
1259 : }
1260 :
1261 2641 : bool match(JS::AllFrames&) {
1262 2641 : return false;
1263 : }
1264 : };
1265 :
1266 12463 : Matcher m(cx, principals, source);
1267 12463 : return capture.match(m);
1268 : }
1269 :
1270 : bool
1271 2961 : SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFrame frame,
1272 : JS::StackCapture&& capture)
1273 : {
1274 : // In order to lookup a cached SavedFrame object, we need to have its parent
1275 : // SavedFrame, which means we need to walk the stack from oldest frame to
1276 : // youngest. However, FrameIter walks the stack from youngest frame to
1277 : // oldest. The solution is to append stack frames to a vector as we walk the
1278 : // stack with FrameIter, and then do a second pass through that vector in
1279 : // reverse order after the traversal has completed and get or create the
1280 : // SavedFrame objects at that time.
1281 : //
1282 : // To avoid making many copies of FrameIter (whose copy constructor is
1283 : // relatively slow), we use a vector of `SavedFrame::Lookup` objects, which
1284 : // only contain the FrameIter data we need. The `SavedFrame::Lookup`
1285 : // objects are partially initialized with everything except their parent
1286 : // pointers on the first pass, and then we fill in the parent pointers as we
1287 : // return in the second pass.
1288 :
1289 2961 : Activation* asyncActivation = nullptr;
1290 5922 : RootedSavedFrame asyncStack(cx, nullptr);
1291 5922 : RootedString asyncCause(cx, nullptr);
1292 2961 : bool parentIsInCache = false;
1293 5922 : RootedSavedFrame cachedFrame(cx, nullptr);
1294 :
1295 : // Accumulate the vector of Lookup objects in |stackChain|.
1296 5922 : SavedFrame::AutoLookupVector stackChain(cx);
1297 27409 : while (!iter.done()) {
1298 12521 : Activation& activation = *iter.activation();
1299 :
1300 12521 : if (asyncActivation && asyncActivation != &activation) {
1301 : // We found an async stack in the previous activation, and we
1302 : // walked past the oldest frame of that activation, we're done.
1303 : // However, we only want to use the async parent if it was
1304 : // explicitly requested; if we got here otherwise, we have
1305 : // a direct parent, which we prefer.
1306 59 : if (asyncActivation->asyncCallIsExplicit())
1307 355 : break;
1308 1 : asyncActivation = nullptr;
1309 : }
1310 :
1311 12463 : if (!asyncActivation) {
1312 12356 : asyncStack = activation.asyncStack();
1313 12356 : if (asyncStack) {
1314 : // While walking from the youngest to the oldest frame, we found
1315 : // an activation that has an async stack set. We will use the
1316 : // youngest frame of the async stack as the parent of the oldest
1317 : // frame of this activation. We still need to iterate over other
1318 : // frames in this activation before reaching the oldest frame.
1319 634 : AutoCompartmentUnchecked ac(cx, iter.compartment());
1320 317 : const char* cause = activation.asyncCause();
1321 317 : UTF8Chars utf8Chars(cause, strlen(cause));
1322 317 : size_t twoByteCharsLen = 0;
1323 634 : char16_t* twoByteChars = UTF8CharsToNewTwoByteCharsZ(cx, utf8Chars,
1324 634 : &twoByteCharsLen).get();
1325 317 : if (!twoByteChars)
1326 0 : return false;
1327 :
1328 : // We expect that there will be a relatively small set of
1329 : // asyncCause reasons ("setTimeout", "promise", etc.), so we
1330 : // atomize the cause here in hopes of being able to benefit
1331 : // from reuse.
1332 317 : asyncCause = JS_AtomizeUCStringN(cx, twoByteChars, twoByteCharsLen);
1333 317 : js_free(twoByteChars);
1334 317 : if (!asyncCause)
1335 0 : return false;
1336 317 : asyncActivation = &activation;
1337 : }
1338 : }
1339 :
1340 24687 : Rooted<LocationValue> location(cx);
1341 : {
1342 24926 : AutoCompartmentUnchecked ac(cx, iter.compartment());
1343 12463 : if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location))
1344 0 : return false;
1345 : }
1346 :
1347 : // The bit set means that the next older parent (frame, pc) pair *must*
1348 : // be in the cache.
1349 12463 : if (capture.is<JS::AllFrames>())
1350 2641 : parentIsInCache = iter.hasCachedSavedFrame();
1351 :
1352 12463 : auto principals = iter.compartment()->principals();
1353 12463 : auto displayAtom = (iter.isWasm() || iter.isFunctionFrame()) ? iter.functionDisplayAtom() : nullptr;
1354 49852 : if (!stackChain->emplaceBack(location.source(),
1355 24926 : location.line(),
1356 24926 : location.column(),
1357 : displayAtom,
1358 : nullptr,
1359 : nullptr,
1360 : principals,
1361 24926 : LiveSavedFrameCache::getFramePtr(iter),
1362 24926 : iter.pc(),
1363 24926 : &activation))
1364 : {
1365 0 : ReportOutOfMemory(cx);
1366 0 : return false;
1367 : }
1368 :
1369 12463 : if (captureIsSatisfied(cx, principals, location.source(), capture)) {
1370 : // The frame we just saved was the last one we were asked to save.
1371 : // If we had an async stack, ensure we don't use any of its frames.
1372 0 : asyncStack.set(nullptr);
1373 0 : break;
1374 : }
1375 :
1376 12463 : ++iter;
1377 :
1378 14226 : if (parentIsInCache &&
1379 13980 : !iter.done() &&
1380 1517 : iter.hasCachedSavedFrame())
1381 : {
1382 1517 : auto* cache = activation.getLiveSavedFrameCache(cx);
1383 1517 : if (!cache)
1384 0 : return false;
1385 1517 : cache->find(cx, iter, &cachedFrame);
1386 1517 : if (cachedFrame)
1387 239 : break;
1388 : }
1389 :
1390 12224 : if (capture.is<JS::MaxFrames>())
1391 9822 : capture.as<JS::MaxFrames>().maxFrames--;
1392 : }
1393 :
1394 : // Limit the depth of the async stack, if any, and ensure that the
1395 : // SavedFrame instances we use are stored in the same compartment as the
1396 : // rest of the synchronous stack chain.
1397 5922 : RootedSavedFrame parentFrame(cx, cachedFrame);
1398 2961 : if (asyncStack && !capture.is<JS::FirstSubsumedFrame>()) {
1399 316 : uint32_t maxAsyncFrames = capture.is<JS::MaxFrames>()
1400 316 : ? capture.as<JS::MaxFrames>().maxFrames
1401 316 : : ASYNC_STACK_MAX_FRAME_COUNT;
1402 316 : if (!adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, maxAsyncFrames))
1403 0 : return false;
1404 : }
1405 :
1406 : // Iterate through |stackChain| in reverse order and get or create the
1407 : // actual SavedFrame instances.
1408 15424 : for (size_t i = stackChain->length(); i != 0; i--) {
1409 12463 : SavedFrame::HandleLookup lookup = stackChain[i-1];
1410 12463 : lookup->parent = parentFrame;
1411 12463 : parentFrame.set(getOrCreateSavedFrame(cx, lookup));
1412 12463 : if (!parentFrame)
1413 0 : return false;
1414 :
1415 12463 : if (capture.is<JS::AllFrames>() && lookup->framePtr && parentFrame != cachedFrame) {
1416 2641 : auto* cache = lookup->activation->getLiveSavedFrameCache(cx);
1417 2641 : if (!cache || !cache->insert(cx, *lookup->framePtr, lookup->pc, parentFrame))
1418 0 : return false;
1419 : }
1420 : }
1421 :
1422 2961 : frame.set(parentFrame);
1423 2961 : return true;
1424 : }
1425 :
1426 : bool
1427 316 : SavedStacks::adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
1428 : HandleString asyncCause,
1429 : MutableHandleSavedFrame adoptedStack,
1430 : uint32_t maxFrameCount)
1431 : {
1432 632 : RootedAtom asyncCauseAtom(cx, AtomizeString(cx, asyncCause));
1433 316 : if (!asyncCauseAtom)
1434 0 : return false;
1435 :
1436 : // If maxFrameCount is zero, the caller asked for an unlimited number of
1437 : // stack frames, but async stacks are not limited by the available stack
1438 : // memory, so we need to set an arbitrary limit when collecting them. We
1439 : // still don't enforce an upper limit if the caller requested more frames.
1440 316 : uint32_t maxFrames = maxFrameCount > 0 ? maxFrameCount : ASYNC_STACK_MAX_FRAME_COUNT;
1441 :
1442 : // Accumulate the vector of Lookup objects in |stackChain|.
1443 632 : SavedFrame::AutoLookupVector stackChain(cx);
1444 316 : SavedFrame* currentSavedFrame = asyncStack;
1445 316 : SavedFrame* firstSavedFrameParent = nullptr;
1446 2516 : for (uint32_t i = 0; i < maxFrames && currentSavedFrame; i++) {
1447 2200 : if (!stackChain->emplaceBack(*currentSavedFrame)) {
1448 0 : ReportOutOfMemory(cx);
1449 0 : return false;
1450 : }
1451 :
1452 2200 : currentSavedFrame = currentSavedFrame->getParent();
1453 :
1454 : // Attach the asyncCause to the youngest frame.
1455 2200 : if (i == 0) {
1456 316 : stackChain->back().asyncCause = asyncCauseAtom;
1457 316 : firstSavedFrameParent = currentSavedFrame;
1458 : }
1459 : }
1460 :
1461 : // This is the 1-based index of the oldest frame we care about.
1462 316 : size_t oldestFramePosition = stackChain->length();
1463 632 : RootedSavedFrame parentFrame(cx, nullptr);
1464 :
1465 632 : if (currentSavedFrame == nullptr &&
1466 316 : asyncStack->compartment() == cx->compartment()) {
1467 : // If we consumed the full async stack, and the stack is in the same
1468 : // compartment as the one requested, we don't need to rebuild the full
1469 : // chain again using the lookup objects, we can just reference the
1470 : // existing chain and change the asyncCause on the younger frame.
1471 139 : oldestFramePosition = 1;
1472 139 : parentFrame = firstSavedFrameParent;
1473 177 : } else if (maxFrameCount == 0 &&
1474 : oldestFramePosition == ASYNC_STACK_MAX_FRAME_COUNT) {
1475 : // If we captured the maximum number of frames and the caller requested
1476 : // no specific limit, we only return half of them. This means that for
1477 : // the next iterations, it's likely we can use the optimization above.
1478 0 : oldestFramePosition = ASYNC_STACK_MAX_FRAME_COUNT / 2;
1479 : }
1480 :
1481 : // Iterate through |stackChain| in reverse order and get or create the
1482 : // actual SavedFrame instances.
1483 1906 : for (size_t i = oldestFramePosition; i != 0; i--) {
1484 1590 : SavedFrame::HandleLookup lookup = stackChain[i-1];
1485 1590 : lookup->parent = parentFrame;
1486 1590 : parentFrame.set(getOrCreateSavedFrame(cx, lookup));
1487 1590 : if (!parentFrame)
1488 0 : return false;
1489 : }
1490 :
1491 316 : adoptedStack.set(parentFrame);
1492 316 : return true;
1493 : }
1494 :
1495 : SavedFrame*
1496 14053 : SavedStacks::getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup lookup)
1497 : {
1498 14053 : const SavedFrame::Lookup& lookupInstance = lookup.get();
1499 14053 : DependentAddPtr<SavedFrame::Set> p(cx, frames, lookupInstance);
1500 14053 : if (p) {
1501 12037 : MOZ_ASSERT(*p);
1502 12037 : return *p;
1503 : }
1504 :
1505 4032 : RootedSavedFrame frame(cx, createFrameFromLookup(cx, lookup));
1506 2016 : if (!frame)
1507 0 : return nullptr;
1508 :
1509 2016 : if (!p.add(cx, frames, lookupInstance, frame))
1510 0 : return nullptr;
1511 :
1512 2016 : return frame;
1513 : }
1514 :
1515 : SavedFrame*
1516 2016 : SavedStacks::createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup)
1517 : {
1518 4032 : RootedSavedFrame frame(cx, SavedFrame::create(cx));
1519 2016 : if (!frame)
1520 0 : return nullptr;
1521 2016 : frame->initFromLookup(cx, lookup);
1522 :
1523 2016 : if (!FreezeObject(cx, frame))
1524 0 : return nullptr;
1525 :
1526 2016 : return frame;
1527 : }
1528 :
1529 : bool
1530 12463 : SavedStacks::getLocation(JSContext* cx, const FrameIter& iter,
1531 : MutableHandle<LocationValue> locationp)
1532 : {
1533 : // We should only ever be caching location values for scripts in this
1534 : // compartment. Otherwise, we would get dead cross-compartment scripts in
1535 : // the cache because our compartment's sweep method isn't called when their
1536 : // compartment gets collected.
1537 12463 : assertSameCompartment(cx, this, iter.compartment());
1538 :
1539 : // When we have a |JSScript| for this frame, use a potentially memoized
1540 : // location from our PCLocationMap and copy it into |locationp|. When we do
1541 : // not have a |JSScript| for this frame (wasm frames), we take a slow path
1542 : // that doesn't employ memoization, and update |locationp|'s slots directly.
1543 :
1544 12463 : if (!iter.hasScript()) {
1545 0 : if (const char16_t* displayURL = iter.displayURL()) {
1546 0 : locationp.setSource(AtomizeChars(cx, displayURL, js_strlen(displayURL)));
1547 : } else {
1548 0 : const char* filename = iter.filename() ? iter.filename() : "";
1549 0 : locationp.setSource(Atomize(cx, filename, strlen(filename)));
1550 : }
1551 0 : if (!locationp.source())
1552 0 : return false;
1553 :
1554 0 : uint32_t column = 0;
1555 0 : locationp.setLine(iter.computeLine(&column));
1556 : // XXX: Make the column 1-based as in other browsers, instead of 0-based
1557 : // which is how SpiderMonkey stores it internally. This will be
1558 : // unnecessary once bug 1144340 is fixed.
1559 0 : locationp.setColumn(column + 1);
1560 0 : return true;
1561 : }
1562 :
1563 24926 : RootedScript script(cx, iter.script());
1564 12463 : jsbytecode* pc = iter.pc();
1565 :
1566 24926 : PCKey key(script, pc);
1567 12463 : PCLocationMap::AddPtr p = pcLocationMap.lookupForAdd(key);
1568 :
1569 12463 : if (!p) {
1570 1444 : RootedAtom source(cx);
1571 722 : if (const char16_t* displayURL = iter.displayURL()) {
1572 0 : source = AtomizeChars(cx, displayURL, js_strlen(displayURL));
1573 : } else {
1574 722 : const char* filename = script->filename() ? script->filename() : "";
1575 722 : source = Atomize(cx, filename, strlen(filename));
1576 : }
1577 722 : if (!source)
1578 0 : return false;
1579 :
1580 : uint32_t column;
1581 722 : uint32_t line = PCToLineNumber(script, pc, &column);
1582 :
1583 : // Make the column 1-based. See comment above.
1584 1444 : LocationValue value(source, line, column + 1);
1585 722 : if (!pcLocationMap.add(p, key, value)) {
1586 0 : ReportOutOfMemory(cx);
1587 0 : return false;
1588 : }
1589 : }
1590 :
1591 12463 : locationp.set(p->value());
1592 12463 : return true;
1593 : }
1594 :
1595 : void
1596 0 : SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
1597 : {
1598 0 : GlobalObject* global = compartment->maybeGlobal();
1599 0 : if (!global)
1600 0 : return;
1601 :
1602 0 : GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
1603 0 : if (!dbgs || dbgs->empty())
1604 0 : return;
1605 :
1606 0 : mozilla::DebugOnly<ReadBarriered<Debugger*>*> begin = dbgs->begin();
1607 0 : mozilla::DebugOnly<bool> foundAnyDebuggers = false;
1608 :
1609 0 : double probability = 0;
1610 0 : for (auto dbgp = dbgs->begin(); dbgp < dbgs->end(); dbgp++) {
1611 : // The set of debuggers had better not change while we're iterating,
1612 : // such that the vector gets reallocated.
1613 0 : MOZ_ASSERT(dbgs->begin() == begin);
1614 :
1615 0 : if ((*dbgp)->trackingAllocationSites && (*dbgp)->enabled) {
1616 0 : foundAnyDebuggers = true;
1617 0 : probability = std::max((*dbgp)->allocationSamplingProbability,
1618 0 : probability);
1619 : }
1620 : }
1621 0 : MOZ_ASSERT(foundAnyDebuggers);
1622 :
1623 0 : if (!bernoulliSeeded) {
1624 0 : mozilla::Array<uint64_t, 2> seed;
1625 0 : GenerateXorShift128PlusSeed(seed);
1626 0 : bernoulli.setRandomState(seed[0], seed[1]);
1627 0 : bernoulliSeeded = true;
1628 : }
1629 :
1630 0 : bernoulli.setProbability(probability);
1631 : }
1632 :
1633 : JSObject*
1634 0 : SavedStacks::MetadataBuilder::build(JSContext* cx, HandleObject target,
1635 : AutoEnterOOMUnsafeRegion& oomUnsafe) const
1636 : {
1637 0 : RootedObject obj(cx, target);
1638 :
1639 0 : SavedStacks& stacks = cx->compartment()->savedStacks();
1640 0 : if (!stacks.bernoulli.trial())
1641 0 : return nullptr;
1642 :
1643 0 : RootedSavedFrame frame(cx);
1644 0 : if (!stacks.saveCurrentStack(cx, &frame))
1645 0 : oomUnsafe.crash("SavedStacksMetadataBuilder");
1646 :
1647 0 : if (!Debugger::onLogAllocationSite(cx, obj, frame, mozilla::TimeStamp::Now()))
1648 0 : oomUnsafe.crash("SavedStacksMetadataBuilder");
1649 :
1650 0 : MOZ_ASSERT_IF(frame, !frame->is<WrapperObject>());
1651 0 : return frame;
1652 : }
1653 :
1654 3 : const SavedStacks::MetadataBuilder SavedStacks::metadataBuilder;
1655 :
1656 : #ifdef JS_CRASH_DIAGNOSTICS
1657 : void
1658 15424 : CompartmentChecker::check(SavedStacks* stacks)
1659 : {
1660 15424 : if (&compartment->savedStacks() != stacks) {
1661 : printf("*** Compartment SavedStacks mismatch: %p vs. %p\n",
1662 0 : (void*) &compartment->savedStacks(), stacks);
1663 0 : MOZ_CRASH();
1664 : }
1665 15424 : }
1666 : #endif /* JS_CRASH_DIAGNOSTICS */
1667 :
1668 3 : /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsSystem;
1669 3 : /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsNotSystem;
1670 :
1671 : UTF8CharsZ
1672 0 : BuildUTF8StackString(JSContext* cx, HandleObject stack)
1673 : {
1674 0 : RootedString stackStr(cx);
1675 0 : if (!JS::BuildStackString(cx, stack, &stackStr))
1676 0 : return UTF8CharsZ();
1677 :
1678 0 : char* chars = JS_EncodeStringToUTF8(cx, stackStr);
1679 0 : return UTF8CharsZ(chars, strlen(chars));
1680 : }
1681 :
1682 : } /* namespace js */
1683 :
1684 : namespace JS {
1685 : namespace ubi {
1686 :
1687 : bool
1688 0 : ConcreteStackFrame<SavedFrame>::isSystem() const
1689 : {
1690 0 : auto trustedPrincipals = get().runtimeFromAnyThread()->trustedPrincipals();
1691 0 : return get().getPrincipals() == trustedPrincipals ||
1692 0 : get().getPrincipals() == &js::ReconstructedSavedFramePrincipals::IsSystem;
1693 : }
1694 :
1695 : bool
1696 0 : ConcreteStackFrame<SavedFrame>::constructSavedFrameStack(JSContext* cx,
1697 : MutableHandleObject outSavedFrameStack)
1698 : const
1699 : {
1700 0 : outSavedFrameStack.set(&get());
1701 0 : if (!cx->compartment()->wrap(cx, outSavedFrameStack)) {
1702 0 : outSavedFrameStack.set(nullptr);
1703 0 : return false;
1704 : }
1705 0 : return true;
1706 : }
1707 :
1708 : // A `mozilla::Variant` matcher that converts the inner value of a
1709 : // `JS::ubi::AtomOrTwoByteChars` string to a `JSAtom*`.
1710 : struct MOZ_STACK_CLASS AtomizingMatcher
1711 : {
1712 : JSContext* cx;
1713 : size_t length;
1714 :
1715 0 : explicit AtomizingMatcher(JSContext* cx, size_t length)
1716 0 : : cx(cx)
1717 0 : , length(length)
1718 0 : { }
1719 :
1720 0 : JSAtom* match(JSAtom* atom) {
1721 0 : MOZ_ASSERT(atom);
1722 0 : return atom;
1723 : }
1724 :
1725 0 : JSAtom* match(const char16_t* chars) {
1726 0 : MOZ_ASSERT(chars);
1727 0 : return AtomizeChars(cx, chars, length);
1728 : }
1729 : };
1730 :
1731 : JS_PUBLIC_API(bool)
1732 0 : ConstructSavedFrameStackSlow(JSContext* cx, JS::ubi::StackFrame& frame,
1733 : MutableHandleObject outSavedFrameStack)
1734 : {
1735 0 : SavedFrame::AutoLookupVector stackChain(cx);
1736 0 : Rooted<JS::ubi::StackFrame> ubiFrame(cx, frame);
1737 :
1738 0 : while (ubiFrame.get()) {
1739 : // Convert the source and functionDisplayName strings to atoms.
1740 :
1741 0 : js::RootedAtom source(cx);
1742 0 : AtomizingMatcher atomizer(cx, ubiFrame.get().sourceLength());
1743 0 : source = ubiFrame.get().source().match(atomizer);
1744 0 : if (!source)
1745 0 : return false;
1746 :
1747 0 : js::RootedAtom functionDisplayName(cx);
1748 0 : auto nameLength = ubiFrame.get().functionDisplayNameLength();
1749 0 : if (nameLength > 0) {
1750 0 : AtomizingMatcher atomizer(cx, nameLength);
1751 0 : functionDisplayName = ubiFrame.get().functionDisplayName().match(atomizer);
1752 0 : if (!functionDisplayName)
1753 0 : return false;
1754 : }
1755 :
1756 0 : auto principals = js::ReconstructedSavedFramePrincipals::getSingleton(ubiFrame.get());
1757 :
1758 0 : if (!stackChain->emplaceBack(source, ubiFrame.get().line(), ubiFrame.get().column(),
1759 : functionDisplayName, /* asyncCause */ nullptr,
1760 : /* parent */ nullptr, principals))
1761 : {
1762 0 : ReportOutOfMemory(cx);
1763 0 : return false;
1764 : }
1765 :
1766 0 : ubiFrame = ubiFrame.get().parent();
1767 : }
1768 :
1769 0 : js::RootedSavedFrame parentFrame(cx);
1770 0 : for (size_t i = stackChain->length(); i != 0; i--) {
1771 0 : SavedFrame::HandleLookup lookup = stackChain[i-1];
1772 0 : lookup->parent = parentFrame;
1773 0 : parentFrame = cx->compartment()->savedStacks().getOrCreateSavedFrame(cx, lookup);
1774 0 : if (!parentFrame)
1775 0 : return false;
1776 : }
1777 :
1778 0 : outSavedFrameStack.set(parentFrame);
1779 0 : return true;
1780 : }
1781 :
1782 :
1783 : } // namespace ubi
1784 : } // namespace JS
|