]>
Commit | Line | Data |
---|---|---|
93a37866 A |
1 | /* |
2 | * Copyright (C) 2012 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 | #ifndef JSStackInlines_h | |
27 | #define JSStackInlines_h | |
28 | ||
29 | #include "CallFrame.h" | |
30 | #include "CodeBlock.h" | |
31 | #include "JSStack.h" | |
32 | ||
33 | namespace JSC { | |
34 | ||
35 | inline Register* JSStack::getTopOfFrame(CallFrame* frame) | |
36 | { | |
37 | if (UNLIKELY(!frame)) | |
38 | return begin(); | |
39 | return frame->frameExtent(); | |
40 | } | |
41 | ||
42 | inline Register* JSStack::getTopOfStack() | |
43 | { | |
44 | return getTopOfFrame(m_topCallFrame); | |
45 | } | |
46 | ||
47 | inline Register* JSStack::getStartOfFrame(CallFrame* frame) | |
48 | { | |
49 | CallFrame* callerFrame = frame->callerFrameNoFlags(); | |
50 | return getTopOfFrame(callerFrame); | |
51 | } | |
52 | ||
53 | inline CallFrame* JSStack::pushFrame(CallFrame* callerFrame, | |
54 | class CodeBlock* codeBlock, JSScope* scope, int argsCount, JSObject* callee) | |
55 | { | |
56 | ASSERT(!!scope); | |
57 | Register* oldEnd = getTopOfStack(); | |
58 | ||
59 | // Ensure that we have enough space for the parameters: | |
60 | size_t paddedArgsCount = argsCount; | |
61 | if (codeBlock) { | |
62 | size_t numParameters = codeBlock->numParameters(); | |
63 | if (paddedArgsCount < numParameters) | |
64 | paddedArgsCount = numParameters; | |
65 | } | |
66 | ||
67 | Register* newCallFrameSlot = oldEnd + paddedArgsCount + JSStack::CallFrameHeaderSize; | |
68 | #if ENABLE(DEBUG_JSSTACK) | |
69 | newCallFrameSlot += JSStack::FenceSize; | |
70 | #endif | |
71 | Register* newEnd = newCallFrameSlot; | |
72 | if (!!codeBlock) | |
73 | newEnd += codeBlock->m_numCalleeRegisters; | |
74 | ||
75 | // Ensure that we have the needed stack capacity to push the new frame: | |
76 | if (!grow(newEnd)) | |
77 | return 0; | |
78 | ||
79 | // Compute the address of the new frame for this invocation: | |
80 | CallFrame* newCallFrame = CallFrame::create(newCallFrameSlot); | |
81 | ASSERT(!!newCallFrame); | |
82 | ||
83 | // The caller frame should always be the real previous frame on the stack, | |
84 | // and not a potential GlobalExec that was passed in. Point callerFrame to | |
85 | // the top frame on the stack. | |
86 | callerFrame = m_topCallFrame; | |
87 | ||
88 | // Initialize the frame header: | |
89 | newCallFrame->init(codeBlock, 0, scope, | |
90 | callerFrame->addHostCallFrameFlag(), argsCount, callee); | |
91 | ||
92 | ASSERT(!!newCallFrame->scope()); | |
93 | ||
94 | // Pad additional args if needed: | |
95 | // Note: we need to subtract 1 from argsCount and paddedArgsCount to | |
96 | // exclude the this pointer. | |
97 | for (size_t i = argsCount-1; i < paddedArgsCount-1; ++i) | |
98 | newCallFrame->setArgument(i, jsUndefined()); | |
99 | ||
100 | installFence(newCallFrame, __FUNCTION__, __LINE__); | |
101 | validateFence(newCallFrame, __FUNCTION__, __LINE__); | |
102 | installTrapsAfterFrame(newCallFrame); | |
103 | ||
104 | // Push the new frame: | |
105 | m_topCallFrame = newCallFrame; | |
106 | ||
107 | return newCallFrame; | |
108 | } | |
109 | ||
110 | inline void JSStack::popFrame(CallFrame* frame) | |
111 | { | |
112 | validateFence(frame, __FUNCTION__, __LINE__); | |
113 | CallFrame* callerFrame = frame->callerFrameNoFlags(); | |
114 | ||
115 | // Pop to the caller: | |
116 | m_topCallFrame = callerFrame; | |
117 | ||
118 | // If we are popping the very first frame from the stack i.e. no more | |
119 | // frames before this, then we can now safely shrink the stack. In | |
120 | // this case, we're shrinking all the way to the beginning since there | |
121 | // are no more frames on the stack. | |
122 | if (!callerFrame) | |
123 | shrink(begin()); | |
124 | ||
125 | installTrapsAfterFrame(callerFrame); | |
126 | } | |
127 | ||
128 | ||
129 | #if ENABLE(DEBUG_JSSTACK) | |
130 | inline JSValue JSStack::generateFenceValue(size_t argIndex) | |
131 | { | |
132 | unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf); | |
133 | JSValue fenceValue = JSValue(fenceBits); | |
134 | return fenceValue; | |
135 | } | |
136 | ||
137 | // The JSStack fences mechanism works as follows: | |
138 | // 1. A fence is a number (JSStack::FenceSize) of JSValues that are initialized | |
139 | // with values generated by JSStack::generateFenceValue(). | |
140 | // 2. When pushFrame() is called, the fence is installed after the max extent | |
141 | // of the previous topCallFrame and the last arg of the new frame: | |
142 | // | |
143 | // | ... | | |
144 | // |--------------------------------------| | |
145 | // | Frame Header of previous frame | | |
146 | // |--------------------------------------| | |
147 | // topCallFrame --> | | | |
148 | // | Locals of previous frame | | |
149 | // |--------------------------------------| | |
150 | // | *** the Fence *** | | |
151 | // |--------------------------------------| | |
152 | // | Args of new frame | | |
153 | // |--------------------------------------| | |
154 | // | Frame Header of new frame | | |
155 | // |--------------------------------------| | |
156 | // frame --> | Locals of new frame | | |
157 | // | | | |
158 | // | |
159 | // 3. In popFrame() and elsewhere, we can call JSStack::validateFence() to | |
160 | // assert that the fence contains the values we expect. | |
161 | ||
162 | inline void JSStack::installFence(CallFrame* frame, const char *function, int lineNo) | |
163 | { | |
164 | UNUSED_PARAM(function); | |
165 | UNUSED_PARAM(lineNo); | |
166 | Register* startOfFrame = getStartOfFrame(frame); | |
167 | ||
168 | // The last argIndex is at: | |
169 | size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; | |
170 | size_t startIndex = maxIndex - FenceSize; | |
171 | for (size_t i = startIndex; i < maxIndex; ++i) { | |
172 | JSValue fenceValue = generateFenceValue(i); | |
173 | frame->setArgument(i, fenceValue); | |
174 | } | |
175 | } | |
176 | ||
177 | inline void JSStack::validateFence(CallFrame* frame, const char *function, int lineNo) | |
178 | { | |
179 | UNUSED_PARAM(function); | |
180 | UNUSED_PARAM(lineNo); | |
181 | ASSERT(!!frame->scope()); | |
182 | Register* startOfFrame = getStartOfFrame(frame); | |
183 | size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; | |
184 | size_t startIndex = maxIndex - FenceSize; | |
185 | for (size_t i = startIndex; i < maxIndex; ++i) { | |
186 | JSValue fenceValue = generateFenceValue(i); | |
187 | JSValue actualValue = frame->getArgumentUnsafe(i); | |
188 | ASSERT(fenceValue == actualValue); | |
189 | } | |
190 | } | |
191 | ||
192 | // When debugging the JSStack, we install bad values after the extent of the | |
193 | // topCallFrame at the end of pushFrame() and popFrame(). The intention is | |
194 | // to trigger crashes in the event that memory in this supposedly unused | |
195 | // region is read and consumed without proper initialization. After the trap | |
196 | // words are installed, the stack looks like this: | |
197 | // | |
198 | // | ... | | |
199 | // |-----------------------------| | |
200 | // | Frame Header of frame | | |
201 | // |-----------------------------| | |
202 | // topCallFrame --> | | | |
203 | // | Locals of frame | | |
204 | // |-----------------------------| | |
205 | // | *** Trap words *** | | |
206 | // |-----------------------------| | |
207 | // | Unused space ... | | |
208 | // | ... | | |
209 | ||
210 | inline void JSStack::installTrapsAfterFrame(CallFrame* frame) | |
211 | { | |
212 | Register* topOfFrame = getTopOfFrame(frame); | |
213 | const int sizeOfTrap = 64; | |
214 | int32_t* startOfTrap = reinterpret_cast<int32_t*>(topOfFrame); | |
215 | int32_t* endOfTrap = startOfTrap + sizeOfTrap; | |
216 | int32_t* endOfCommitedMemory = reinterpret_cast<int32_t*>(m_commitEnd); | |
217 | ||
218 | // Make sure we're not exceeding the amount of available memory to write to: | |
219 | if (endOfTrap > endOfCommitedMemory) | |
220 | endOfTrap = endOfCommitedMemory; | |
221 | ||
222 | // Lay the traps: | |
223 | int32_t* p = startOfTrap; | |
224 | while (p < endOfTrap) | |
225 | *p++ = 0xabadcafe; // A bad word to trigger a crash if deref'ed. | |
226 | } | |
227 | #endif // ENABLE(DEBUG_JSSTACK) | |
228 | ||
229 | } // namespace JSC | |
230 | ||
231 | #endif // JSStackInlines_h |