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 CallFrame;
39class CodeBlock;
40class JSArray;
41class JSObject;
42class JSScope;
43class LLIntOffsetsExtractor;
44class SlotVisitor;
45class VM;
46
47// ShadowChicken is a log that can be used to produce a shadow stack of CHICKEN-style stack frames.
48// This enables the debugger to almost always see the tail-deleted stack frames, so long as we have
49// memory inside ShadowChicken to remember them.
50//
51// The ShadowChicken log comprises packets that have one of two shapes:
52//
53// Prologue Packet, which has:
54// - Callee object.
55// - Frame pointer.
56// - Caller frame pointer.
57//
58// Tail Call Packet, which has just:
59// - Frame pointer.
60//
61// Prologue Packets are placed into the log in any JS function's prologue. Tail Call Packets are
62// placed into the log just before making a proper tail call. We never log returns, since that would
63// require a lot of infrastructure (unwinding, multiple ways of returning, etc). We don't need to
64// see the returns because the prologue packets have a frame pointer. The tail call packets tell us
65// when there was a tail call, and record the FP *before* the tail call.
66//
67// At any time it is possible to construct a shadow stack from the log and the actual machine stack.
68
69class ShadowChicken {
70 WTF_MAKE_NONCOPYABLE(ShadowChicken);
71 WTF_MAKE_FAST_ALLOCATED;
72public:
73 struct Packet {
74 Packet()
75 {
76 }
77
78 static constexpr unsigned unlikelyValue = 0x7a11;
79
80 static constexpr intptr_t tailMarkerValue = static_cast<intptr_t>(unlikelyValue);
81 static JSObject* tailMarker()
82 {
83 return bitwise_cast<JSObject*>(tailMarkerValue);
84 }
85
86 static JSObject* throwMarker()
87 {
88 return bitwise_cast<JSObject*>(static_cast<intptr_t>(unlikelyValue + 1));
89 }
90
91 static Packet prologue(JSObject* callee, CallFrame* frame, CallFrame* callerFrame, JSScope* scope)
92 {
93 Packet result;
94 result.callee = callee;
95 result.frame = frame;
96 result.callerFrame = callerFrame;
97 result.scope = scope;
98 return result;
99 }
100
101 static Packet tail(CallFrame* frame, JSValue thisValue, JSScope* scope, CodeBlock* codeBlock, CallSiteIndex callSiteIndex)
102 {
103 Packet result;
104 result.callee = tailMarker();
105 result.frame = frame;
106 result.thisValue = thisValue;
107 result.scope = scope;
108 result.codeBlock = codeBlock;
109 result.callSiteIndex = callSiteIndex;
110 return result;
111 }
112
113 static Packet throwPacket()
114 {
115 Packet result;
116 result.callee = throwMarker();
117 return result;
118 }
119
120 explicit operator bool() const { return !!callee; }
121
122 bool isPrologue() const { return *this && callee != tailMarker() && callee != throwMarker(); }
123 bool isTail() const { return *this && callee == tailMarker(); }
124 bool isThrow() const { return *this && callee == throwMarker(); }
125
126 void dump(PrintStream&) const;
127
128 // Only tail packets have a valid thisValue, CodeBlock*, and CallSiteIndex. We grab 'this' and CodeBlock* from non tail-deleted frames from the machine frame.
129 JSValue thisValue { JSValue() };
130 JSObject* callee { nullptr };
131 CallFrame* frame { nullptr };
132 CallFrame* callerFrame { nullptr };
133 JSScope* scope { nullptr };
134 CodeBlock* codeBlock { nullptr };
135 CallSiteIndex callSiteIndex;
136 };
137
138 struct Frame {
139 Frame()
140 {
141 }
142
143 Frame(JSObject* callee, CallFrame* frame, bool isTailDeleted, JSValue thisValue = JSValue(), JSScope* scope = nullptr, CodeBlock* codeBlock = nullptr, CallSiteIndex callSiteIndex = CallSiteIndex())
144 : callee(callee)
145 , frame(frame)
146 , thisValue(thisValue)
147 , scope(scope)
148 , codeBlock(codeBlock)
149 , callSiteIndex(callSiteIndex)
150 , isTailDeleted(isTailDeleted)
151 {
152 }
153
154 bool operator==(const Frame& other) const
155 {
156 return callee == other.callee
157 && frame == other.frame
158 && thisValue == other.thisValue
159 && scope == other.scope
160 && codeBlock == other.codeBlock
161 && callSiteIndex.bits() == other.callSiteIndex.bits()
162 && isTailDeleted == other.isTailDeleted;
163 }
164
165 bool operator!=(const Frame& other) const
166 {
167 return !(*this == other);
168 }
169
170 void dump(PrintStream&) const;
171
172 // FIXME: This should be able to hold the moral equivalent of StackVisitor::Frame, so that
173 // we can support inlining.
174 // https://bugs.webkit.org/show_bug.cgi?id=155686
175 JSObject* callee { nullptr };
176 CallFrame* frame { nullptr };
177 JSValue thisValue { JSValue() };
178 JSScope* scope { nullptr };
179 CodeBlock* codeBlock { nullptr };
180 CallSiteIndex callSiteIndex;
181 bool isTailDeleted { false };
182 };
183
184 ShadowChicken();
185 ~ShadowChicken();
186
187 void log(VM& vm, CallFrame*, const Packet&);
188
189 void update(VM&, CallFrame*);
190
191 // Expects this signature: (const Frame& frame) -> bool. Return true to keep iterating. Return false to stop iterating.
192 // Note that this only works right with inlining disabled, but that's OK since for now we
193 // disable inlining when the inspector is attached. It would be easy to make this work with
194 // inlining, and would mostly require that we can request that StackVisitor doesn't skip tail
195 // frames.
196 template<typename Functor>
197 void iterate(VM&, CallFrame*, const Functor&);
198
199 void visitChildren(SlotVisitor&);
200 void reset();
201
202 // JIT support.
203 Packet* log() const { return m_log; }
204 unsigned logSize() const { return m_logSize; }
205 Packet** addressOfLogCursor() { return &m_logCursor; }
206 Packet* logEnd() { return m_logEnd; }
207
208 void dump(PrintStream&) const;
209
210 JS_EXPORT_PRIVATE JSArray* functionsOnStack(JSGlobalObject*, CallFrame*);
211
212private:
213 friend class LLIntOffsetsExtractor;
214
215 Packet* m_log { nullptr };
216 unsigned m_logSize { 0 };
217 Packet* m_logCursor { nullptr };
218 Packet* m_logEnd { nullptr };
219
220 Vector<Frame> m_stack;
221};
222
223} // namespace JSC
224
225