WebAssembly: poison JS object's secrets
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / JSToWasm.cpp
1 /*
2  * Copyright (C) 2016-2018 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 #include "config.h"
27 #include "JSToWasm.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "CCallHelpers.h"
32 #include "JSWebAssemblyInstance.h"
33 #include "WasmCallingConvention.h"
34
35 namespace JSC { namespace Wasm {
36
37 std::unique_ptr<InternalFunction> createJSToWasmWrapper(CompilationContext& compilationContext, const Signature& signature, Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls, const ModuleInformation& info, MemoryMode mode, unsigned functionIndex)
38 {
39     CCallHelpers& jit = *compilationContext.embedderEntrypointJIT;
40
41     auto result = std::make_unique<InternalFunction>();
42     jit.emitFunctionPrologue();
43
44     // FIXME Stop using 0 as codeBlocks. https://bugs.webkit.org/show_bug.cgi?id=165321
45     jit.store64(CCallHelpers::TrustedImm64(0), CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast<int>(sizeof(Register))));
46     MacroAssembler::DataLabelPtr calleeMoveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), GPRInfo::nonPreservedNonReturnGPR);
47     jit.storePtr(GPRInfo::nonPreservedNonReturnGPR, CCallHelpers::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register))));
48     CodeLocationDataLabelPtr* linkedCalleeMove = &result->calleeMoveLocation;
49     jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
50         *linkedCalleeMove = linkBuffer.locationOf(calleeMoveLocation);
51     });
52
53     const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get();
54     RegisterSet toSave = pinnedRegs.toSave(mode);
55
56 #if !ASSERT_DISABLED
57     unsigned toSaveSize = toSave.numberOfSetGPRs();
58     // They should all be callee saves.
59     toSave.filter(RegisterSet::calleeSaveRegisters());
60     ASSERT(toSave.numberOfSetGPRs() == toSaveSize);
61 #endif
62
63     RegisterAtOffsetList registersToSpill(toSave, RegisterAtOffsetList::OffsetBaseType::FramePointerBased);
64     result->entrypoint.calleeSaveRegisters = registersToSpill;
65
66     unsigned totalFrameSize = registersToSpill.size() * sizeof(void*);
67     totalFrameSize += WasmCallingConvention::headerSizeInBytes();
68     totalFrameSize -= sizeof(CallerFrameAndPC);
69     unsigned numGPRs = 0;
70     unsigned numFPRs = 0;
71     for (unsigned i = 0; i < signature.argumentCount(); i++) {
72         switch (signature.argument(i)) {
73         case Wasm::I64:
74         case Wasm::I32:
75             if (numGPRs >= wasmCallingConvention().m_gprArgs.size())
76                 totalFrameSize += sizeof(void*);
77             ++numGPRs;
78             break;
79         case Wasm::F32:
80         case Wasm::F64:
81             if (numFPRs >= wasmCallingConvention().m_fprArgs.size())
82                 totalFrameSize += sizeof(void*);
83             ++numFPRs;
84             break;
85         default:
86             RELEASE_ASSERT_NOT_REACHED();
87         }
88     }
89
90     totalFrameSize = WTF::roundUpToMultipleOf(stackAlignmentBytes(), totalFrameSize);
91     jit.subPtr(MacroAssembler::TrustedImm32(totalFrameSize), MacroAssembler::stackPointerRegister);
92
93     // We save all these registers regardless of having a memory or not.
94     // The reason is that we use one of these as a scratch. That said,
95     // almost all real wasm programs use memory, so it's not really
96     // worth optimizing for the case that they don't.
97     for (const RegisterAtOffset& regAtOffset : registersToSpill) {
98         GPRReg reg = regAtOffset.reg().gpr();
99         ptrdiff_t offset = regAtOffset.offset();
100         jit.storePtr(reg, CCallHelpers::Address(GPRInfo::callFrameRegister, offset));
101     }
102
103     GPRReg wasmContextInstanceGPR = pinnedRegs.wasmContextInstancePointer;
104
105     {
106         CCallHelpers::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC)));
107         numGPRs = 0;
108         numFPRs = 0;
109         // We're going to set the pinned registers after this. So
110         // we can use this as a scratch for now since we saved it above.
111         GPRReg scratchReg = pinnedRegs.baseMemoryPointer;
112
113         ptrdiff_t jsOffset = CallFrameSlot::thisArgument * sizeof(EncodedJSValue);
114
115         // vmEntryToWasm passes the JSWebAssemblyInstance corresponding to Wasm::Context*'s
116         // instance as the first JS argument when we're not using fast TLS to hold the
117         // Wasm::Context*'s instance.
118         if (!Context::useFastTLS()) {
119             jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmContextInstanceGPR);
120             jit.loadPtr(CCallHelpers::Address(wasmContextInstanceGPR, JSWebAssemblyInstance::offsetOfPoisonedInstance()), wasmContextInstanceGPR);
121             jit.move(CCallHelpers::TrustedImm64(makeConstExprPoison(JSWebAssemblyInstancePoison)), scratchReg);
122             jit.xor64(scratchReg, wasmContextInstanceGPR);
123             jsOffset += sizeof(EncodedJSValue);
124         }
125
126         ptrdiff_t wasmOffset = CallFrame::headerSizeInRegisters * sizeof(void*);
127         for (unsigned i = 0; i < signature.argumentCount(); i++) {
128             switch (signature.argument(i)) {
129             case Wasm::I32:
130             case Wasm::I64:
131                 if (numGPRs >= wasmCallingConvention().m_gprArgs.size()) {
132                     if (signature.argument(i) == Wasm::I32) {
133                         jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
134                         jit.store32(scratchReg, calleeFrame.withOffset(wasmOffset));
135                     } else {
136                         jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
137                         jit.store64(scratchReg, calleeFrame.withOffset(wasmOffset));
138                     }
139                     wasmOffset += sizeof(void*);
140                 } else {
141                     if (signature.argument(i) == Wasm::I32)
142                         jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_gprArgs[numGPRs].gpr());
143                     else
144                         jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_gprArgs[numGPRs].gpr());
145                 }
146                 ++numGPRs;
147                 break;
148             case Wasm::F32:
149             case Wasm::F64:
150                 if (numFPRs >= wasmCallingConvention().m_fprArgs.size()) {
151                     if (signature.argument(i) == Wasm::F32) {
152                         jit.load32(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
153                         jit.store32(scratchReg, calleeFrame.withOffset(wasmOffset));
154                     } else {
155                         jit.load64(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), scratchReg);
156                         jit.store64(scratchReg, calleeFrame.withOffset(wasmOffset));
157                     }
158                     wasmOffset += sizeof(void*);
159                 } else {
160                     if (signature.argument(i) == Wasm::F32)
161                         jit.loadFloat(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_fprArgs[numFPRs].fpr());
162                     else
163                         jit.loadDouble(CCallHelpers::Address(GPRInfo::callFrameRegister, jsOffset), wasmCallingConvention().m_fprArgs[numFPRs].fpr());
164                 }
165                 ++numFPRs;
166                 break;
167             default:
168                 RELEASE_ASSERT_NOT_REACHED();
169             }
170
171             jsOffset += sizeof(EncodedJSValue);
172         }
173     }
174
175     if (!!info.memory) {
176         GPRReg baseMemory = pinnedRegs.baseMemoryPointer;
177
178         if (!Context::useFastTLS())
179             jit.loadPtr(CCallHelpers::Address(wasmContextInstanceGPR, Instance::offsetOfMemory()), baseMemory);
180         else {
181             jit.loadWasmContextInstance(baseMemory);
182             jit.loadPtr(CCallHelpers::Address(baseMemory, Instance::offsetOfMemory()), baseMemory);
183         }
184
185         if (mode != MemoryMode::Signaling) {
186             jit.loadPtr(CCallHelpers::Address(baseMemory, Wasm::Memory::offsetOfIndexingMask()), pinnedRegs.indexingMask);
187             const auto& sizeRegs = pinnedRegs.sizeRegisters;
188             ASSERT(sizeRegs.size() >= 1);
189             ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0.
190             jit.loadPtr(CCallHelpers::Address(baseMemory, Wasm::Memory::offsetOfSize()), sizeRegs[0].sizeRegister);
191             for (unsigned i = 1; i < sizeRegs.size(); ++i)
192                 jit.add64(CCallHelpers::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister);
193         }
194
195         jit.loadPtr(CCallHelpers::Address(baseMemory, Wasm::Memory::offsetOfMemory()), baseMemory);
196     }
197
198     CCallHelpers::Call call = jit.threadSafePatchableNearCall();
199     unsigned functionIndexSpace = functionIndex + info.importFunctionCount();
200     ASSERT(functionIndexSpace < info.functionIndexSpaceSize());
201     jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndexSpace] (LinkBuffer& linkBuffer) {
202         unlinkedWasmToWasmCalls->append({ linkBuffer.locationOfNearCall(call), functionIndexSpace });
203     });
204
205
206     for (const RegisterAtOffset& regAtOffset : registersToSpill) {
207         GPRReg reg = regAtOffset.reg().gpr();
208         ASSERT(reg != GPRInfo::returnValueGPR);
209         ptrdiff_t offset = regAtOffset.offset();
210         jit.loadPtr(CCallHelpers::Address(GPRInfo::callFrameRegister, offset), reg);
211     }
212
213     switch (signature.returnType()) {
214     case Wasm::F32:
215         jit.moveFloatTo32(FPRInfo::returnValueFPR, GPRInfo::returnValueGPR);
216         break;
217     case Wasm::F64:
218         jit.moveDoubleTo64(FPRInfo::returnValueFPR, GPRInfo::returnValueGPR);
219         break;
220     default:
221         break;
222     }
223
224     jit.emitFunctionEpilogue();
225     jit.ret();
226
227     return result;
228 }
229
230 } } // namespace JSC::Wasm
231
232 #endif // ENABLE(WEBASSEMBLY)