1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "CallFrame.h"
29#include "JSCJSValue.h"
30#include <wtf/FastMalloc.h>
31#include <wtf/Noncopyable.h>
32#include <wtf/PrintStream.h>
33#include <wtf/StdLibExtras.h>
34#include <wtf/Vector.h>
35
36namespace JSC {
37
38class CodeBlock;
39class ExecState;
40class JSArray;
41class JSObject;
42class JSScope;
43class LLIntOffsetsExtractor;
44class SlotVisitor;
45class VM;
46
47typedef ExecState CallFrame;
48
49// ShadowChicken is a log that can be used to produce a shadow stack of CHICKEN-style stack frames.
50// This enables the debugger to almost always see the tail-deleted stack frames, so long as we have
51// memory inside ShadowChicken to remember them.
52//
53// The ShadowChicken log comprises packets that have one of two shapes:
54//
55// Prologue Packet, which has:
56// - Callee object.
57// - Frame pointer.
58// - Caller frame pointer.
59//
60// Tail Call Packet, which has just:
61// - Frame pointer.
62//
63// Prologue Packets are placed into the log in any JS function's prologue. Tail Call Packets are
64// placed into the log just before making a proper tail call. We never log returns, since that would
65// require a lot of infrastructure (unwinding, multiple ways of returning, etc). We don't need to
66// see the returns because the prologue packets have a frame pointer. The tail call packets tell us
67// when there was a tail call, and record the FP *before* the tail call.
68//
69// At any time it is possible to construct a shadow stack from the log and the actual machine stack.
70
71class ShadowChicken {
72 WTF_MAKE_NONCOPYABLE(ShadowChicken);
73 WTF_MAKE_FAST_ALLOCATED;
74public:
75 struct Packet {
76 Packet()
77 {
78 }
79
80 static const constexpr unsigned unlikelyValue = 0x7a11;
81
82 static const constexpr intptr_t tailMarkerValue = static_cast<intptr_t>(unlikelyValue);
83 static JSObject* tailMarker()
84 {
85 return bitwise_cast<JSObject*>(tailMarkerValue);
86 }
87
88 static JSObject* throwMarker()
89 {
90 return bitwise_cast<JSObject*>(static_cast<intptr_t>(unlikelyValue + 1));
91 }
92
93 static Packet prologue(JSObject* callee, CallFrame* frame, CallFrame* callerFrame, JSScope* scope)
94 {
95 Packet result;
96 result.callee = callee;
97 result.frame = frame;
98 result.callerFrame = callerFrame;
99 result.scope = scope;
100 return result;
101 }
102
103 static Packet tail(CallFrame* frame, JSValue thisValue, JSScope* scope, CodeBlock* codeBlock, CallSiteIndex callSiteIndex)
104 {
105 Packet result;
106 result.callee = tailMarker();
107 result.frame = frame;
108 result.thisValue = thisValue;
109 result.scope = scope;
110 result.codeBlock = codeBlock;
111 result.callSiteIndex = callSiteIndex;
112 return result;
113 }
114
115 static Packet throwPacket()
116 {
117 Packet result;
118 result.callee = throwMarker();
119 return result;
120 }
121
122 explicit operator bool() const { return !!callee; }
123
124 bool isPrologue() const { return *this && callee != tailMarker() && callee != throwMarker(); }
125 bool isTail() const { return *this && callee == tailMarker(); }
126 bool isThrow() const { return *this && callee == throwMarker(); }
127
128 void dump(PrintStream&) const;
129
130 // Only tail packets have a valid thisValue, CodeBlock*, and CallSiteIndex. We grab 'this' and CodeBlock* from non tail-deleted frames from the machine frame.
131 JSValue thisValue { JSValue() };
132 JSObject* callee { nullptr };
133 CallFrame* frame { nullptr };
134 CallFrame* callerFrame { nullptr };
135 JSScope* scope { nullptr };
136 CodeBlock* codeBlock { nullptr };
137 CallSiteIndex callSiteIndex;
138 };
139
140 struct Frame {
141 Frame()
142 {
143 }
144
145 Frame(JSObject* callee, CallFrame* frame, bool isTailDeleted, JSValue thisValue = JSValue(), JSScope* scope = nullptr, CodeBlock* codeBlock = nullptr, CallSiteIndex callSiteIndex = CallSiteIndex())
146 : callee(callee)
147 , frame(frame)
148 , thisValue(thisValue)
149 , scope(scope)
150 , codeBlock(codeBlock)
151 , callSiteIndex(callSiteIndex)
152 , isTailDeleted(isTailDeleted)
153 {
154 }
155
156 bool operator==(const Frame& other) const
157 {
158 return callee == other.callee
159 && frame == other.frame
160 && thisValue == other.thisValue
161 && scope == other.scope
162 && codeBlock == other.codeBlock
163 && callSiteIndex.bits() == other.callSiteIndex.bits()
164 && isTailDeleted == other.isTailDeleted;
165 }
166
167 bool operator!=(const Frame& other) const
168 {
169 return !(*this == other);
170 }
171
172 void dump(PrintStream&) const;
173
174 // FIXME: This should be able to hold the moral equivalent of StackVisitor::Frame, so that
175 // we can support inlining.
176 // https://bugs.webkit.org/show_bug.cgi?id=155686
177 JSObject* callee { nullptr };
178 CallFrame* frame { nullptr };
179 JSValue thisValue { JSValue() };
180 JSScope* scope { nullptr };
181 CodeBlock* codeBlock { nullptr };
182 CallSiteIndex callSiteIndex;
183 bool isTailDeleted { false };
184 };
185
186 ShadowChicken();
187 ~ShadowChicken();
188
189 void log(VM& vm, ExecState* exec, const Packet&);
190
191 void update(VM&, ExecState*);
192
193 // Expects this signature: (const Frame& frame) -> bool. Return true to keep iterating. Return false to stop iterating.
194 // Note that this only works right with inlining disabled, but that's OK since for now we
195 // disable inlining when the inspector is attached. It would be easy to make this work with
196 // inlining, and would mostly require that we can request that StackVisitor doesn't skip tail
197 // frames.
198 template<typename Functor>
199 void iterate(VM&, ExecState*, const Functor&);
200
201 void visitChildren(SlotVisitor&);
202 void reset();
203
204 // JIT support.
205 Packet* log() const { return m_log; }
206 unsigned logSize() const { return m_logSize; }
207 Packet** addressOfLogCursor() { return &m_logCursor; }
208 Packet* logEnd() { return m_logEnd; }
209
210 void dump(PrintStream&) const;
211
212 JS_EXPORT_PRIVATE JSArray* functionsOnStack(ExecState*);
213
214private:
215 friend class LLIntOffsetsExtractor;
216
217 Packet* m_log { nullptr };
218 unsigned m_logSize { 0 };
219 Packet* m_logCursor { nullptr };
220 Packet* m_logEnd { nullptr };
221
222 Vector<Frame> m_stack;
223};
224
225} // namespace JSC
226
227