v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
signature-hashing.h
Go to the documentation of this file.
1// Copyright 2024 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_WASM_SIGNATURE_HASHING_H_
6#define V8_WASM_SIGNATURE_HASHING_H_
7
8#if !V8_ENABLE_WEBASSEMBLY
9#error This header should only be included if WebAssembly is enabled.
10#endif // !V8_ENABLE_WEBASSEMBLY
11
15#include "src/wasm/value-type.h"
17
18namespace v8::internal::wasm {
19
21 return type.machine_representation();
22}
23
25 return type.representation();
26}
27
28// This shared helper ensures that {GetWasmCallDescriptor} and
29// {SignatureHasher::Hash} remain in sync.
30// The {SigType} type must match the {Signature} class, i.e. must support:
31// size_t parameter_count();
32// size_t return_count();
33// T GetParam(size_t index); for T in {ValueType, MachineType}
34// T GetReturn(size_t index); for T in {ValueType, MachineType}
35// The {ResultCollector} type must match the {LocationSignature::Builder}
36// class, i.e. must support:
37// void AddParamAt(size_t index, LinkageLocation location);
38// void AddReturnAt(size_t index, LinkageLocation location);
39// {extra_callable_param} configures adding the implicit "callable" parameter
40// that import call wrappers have, hard-coded to use the kJSFunctionRegister.
41template <class ResultCollector, class SigType>
42void IterateSignatureImpl(const SigType* sig, bool extra_callable_param,
43 ResultCollector& locations,
44 int* untagged_parameter_slots,
45 int* total_parameter_slots,
46 int* untagged_return_slots, int* total_return_slots) {
47 constexpr int kParamsSlotOffset = 0;
49 kParamsSlotOffset);
50 // The instance object.
51 locations.AddParamAt(0, params.Next(MachineRepresentation::kTaggedPointer));
52 const size_t param_offset = 1; // Actual params start here.
53
54 // Parameters are separated into two groups (first all untagged, then all
55 // tagged parameters). This allows for easy iteration of tagged parameters
56 // during frame iteration. It also allows for easy signature verification
57 // based on counts.
58 const size_t parameter_count = sig->parameter_count();
59 bool has_tagged_param = false;
60 for (size_t i = 0; i < parameter_count; i++) {
62 // Skip tagged parameters (e.g. any-ref).
63 if (IsAnyTagged(param)) {
64 has_tagged_param = true;
65 continue;
66 }
67 locations.AddParamAt(i + param_offset, params.Next(param));
68 }
69 params.EndSlotArea(); // End the untagged area. Tagged slots come after.
70 *untagged_parameter_slots = params.NumStackSlots();
71 if (has_tagged_param) {
72 for (size_t i = 0; i < parameter_count; i++) {
74 if (!IsAnyTagged(param)) continue; // Skip untagged parameters.
75 locations.AddParamAt(i + param_offset, params.Next(param));
76 }
77 }
78 // Import call wrappers have an additional (implicit) parameter, the callable.
79 // For consistency with JS, we use the JSFunction register.
80 if (extra_callable_param) {
81 locations.AddParamAt(
82 parameter_count + param_offset,
85 }
86 int params_stack_height = AddArgumentPaddingSlots(params.NumStackSlots());
87 *total_parameter_slots = params_stack_height;
88
89 // Add return location(s).
90 // For efficient signature verification, order results by taggedness, such
91 // that all untagged results appear first in registers and on the stack,
92 // followed by tagged results. That way, we can simply check the size of
93 // each section, rather than needing a bit map.
95 params_stack_height);
96
97 const size_t return_count = sig->return_count();
98 bool has_tagged_result = false;
99 for (size_t i = 0; i < return_count; i++) {
101 if (IsAnyTagged(ret)) {
102 has_tagged_result = true;
103 continue;
104 }
105 locations.AddReturnAt(i, rets.Next(ret));
106 }
107 rets.EndSlotArea(); // End the untagged area.
108 *untagged_return_slots = rets.NumStackSlots();
109 if (has_tagged_result) {
110 for (size_t i = 0; i < return_count; i++) {
112 if (!IsAnyTagged(ret)) continue;
113 locations.AddReturnAt(i, rets.Next(ret));
114 }
115 }
116 *total_return_slots = rets.NumStackSlots();
117}
118
119#if V8_ENABLE_SANDBOX
120
121// Computes a "signature hash" for sandbox hardening: two functions should have
122// the same "signature hash" iff mixing them up (due to in-sandbox corruption)
123// cannot possibly lead to a sandbox escape. That means in particular that we
124// must ensure the following properties:
125// - there must be no tagged/untagged mixups among parameters passed in GP
126// registers.
127// - there must be no tagged/untagged mixups among parameters passed on the
128// stack.
129// - there must be no mismatch in the sizes of the stack regions used for
130// passing parameters.
131// - these same properties must hold for return values.
132// To achieve this, we simulate the linkage locations that
133// {GetWasmCallDescriptor} would assign, and collect the counts of
134// tagged/untagged parameters in registers and on the stack, respectively.
135class SignatureHasher {
136 public:
137 template <typename SigType>
138 static uint64_t Hash(const SigType* sig) {
139 SignatureHasher hasher;
140 int total_param_stack_slots;
141 int total_return_stack_slots;
143 sig, false /* no extra callable parameter */, hasher,
144 &hasher.params_.untagged_on_stack_, &total_param_stack_slots,
145 &hasher.rets_.untagged_on_stack_, &total_return_stack_slots);
146
147 hasher.params_.tagged_on_stack_ =
148 total_param_stack_slots - hasher.params_.untagged_on_stack_;
149 hasher.rets_.tagged_on_stack_ =
150 total_return_stack_slots - hasher.rets_.untagged_on_stack_;
151
152 return hasher.GetHash();
153 }
154
155 void AddParamAt(size_t index, LinkageLocation location) {
156 if (index == 0) return; // Skip the instance object.
157 CountIfRegister(location, params_);
158 }
159 void AddReturnAt(size_t index, LinkageLocation location) {
160 CountIfRegister(location, rets_);
161 }
162
163 private:
164 static constexpr int kUntaggedInRegBits = 3;
165 static constexpr int kTaggedInRegBits = 3;
166 static constexpr int kUntaggedOnStackBits = 11;
167 static constexpr int kTaggedOnStackBits = 10;
168
169 using UntaggedInReg =
170 base::BitField<uint32_t, 0, kUntaggedInRegBits, uint64_t>;
171 using TaggedInReg = UntaggedInReg::Next<uint32_t, kTaggedInRegBits>;
172 using UntaggedOnStack = TaggedInReg::Next<uint32_t, kUntaggedOnStackBits>;
173 using TaggedOnStack = UntaggedOnStack::Next<uint32_t, kTaggedOnStackBits>;
174
175 static constexpr int kTotalWidth = TaggedOnStack::kLastUsedBit + 1;
176 // Make sure we can return the full result (params + results) in a uint64_t.
177 static_assert(kTotalWidth * 2 <= 64);
178 // Also, we use ~uint64_t{0} as an invalid signature marker, so make sure that
179 // this can't be a valid signature.
180 static_assert(kTotalWidth * 2 < 64);
181
182 // Make sure we chose the bit fields large enough.
183 static_assert(arraysize(wasm::kGpParamRegisters) <=
184 UntaggedInReg::kNumValues);
185 static_assert(arraysize(wasm::kGpParamRegisters) <= TaggedInReg::kNumValues);
186 static_assert(arraysize(wasm::kGpReturnRegisters) <=
187 UntaggedInReg::kNumValues);
188 static_assert(arraysize(wasm::kGpReturnRegisters) <= TaggedInReg::kNumValues);
189 static constexpr int kMaxValueSizeInPointers =
191 static_assert(wasm::kV8MaxWasmFunctionParams * kMaxValueSizeInPointers <=
192 UntaggedOnStack::kNumValues);
193 static_assert(wasm::kV8MaxWasmFunctionParams <= TaggedOnStack::kNumValues);
194 static_assert(wasm::kV8MaxWasmFunctionReturns * kMaxValueSizeInPointers <=
195 UntaggedOnStack::kNumValues);
196 static_assert(wasm::kV8MaxWasmFunctionReturns <= TaggedOnStack::kNumValues);
197
198 struct Counts {
199 int tagged_in_reg_{0};
200 int untagged_in_reg_{0};
201 int tagged_on_stack_{0};
202 int untagged_on_stack_{0};
203
204 uint64_t GetHash() const {
205 return UntaggedInReg::encode(untagged_in_reg_) |
206 TaggedInReg::encode(tagged_in_reg_) |
207 UntaggedOnStack::encode(untagged_on_stack_) |
208 TaggedOnStack::encode(tagged_on_stack_);
209 }
210 };
211
212 uint64_t GetHash() const {
213 return (rets_.GetHash() << kTotalWidth) | params_.GetHash();
214 }
215
216 void CountIfRegister(LinkageLocation loc, Counts& counts) {
217 if (!loc.IsRegister()) {
218 DCHECK(loc.IsCallerFrameSlot());
219 return;
220 }
221 MachineType type = loc.GetType();
222 if (type.IsTagged()) {
223 counts.tagged_in_reg_++;
224 } else if (IsIntegral(type.representation())) {
225 counts.untagged_in_reg_++;
226 } else {
227 DCHECK(IsFloatingPoint(type.representation()));
228 // No need to count FP registers.
229 }
230 }
231
232 Counts params_{};
233 Counts rets_{};
234};
235
236#else // V8_ENABLE_SANDBOX
237
239 public:
240 template <typename SigType>
241 static uint64_t Hash(const SigType* sig) {
242 return 0;
243 }
244};
245
246#endif // V8_ENABLE_SANDBOX
247
248} // namespace v8::internal::wasm
249
250#endif // V8_WASM_SIGNATURE_HASHING_H_
int16_t parameter_count
Definition builtins.cc:67
static LinkageLocation ForRegister(int32_t reg, MachineType type=MachineType::None())
static constexpr MachineType TaggedPointer()
constexpr int8_t code() const
LinkageLocation Next(MachineRepresentation rep)
static uint64_t Hash(const SigType *sig)
constexpr DoubleRegister kFpReturnRegisters[]
constexpr Register kGpParamRegisters[]
constexpr DoubleRegister kFpParamRegisters[]
constexpr Register kGpReturnRegisters[]
constexpr size_t kV8MaxWasmFunctionReturns
Definition wasm-limits.h:54
void IterateSignatureImpl(const SigType *sig, bool extra_callable_param, ResultCollector &locations, int *untagged_parameter_slots, int *total_parameter_slots, int *untagged_return_slots, int *total_return_slots)
MachineRepresentation GetMachineRepresentation(ValueTypeBase type)
constexpr int kMaxValueTypeSize
Definition value-type.h:49
constexpr size_t kV8MaxWasmFunctionParams
Definition wasm-limits.h:53
constexpr int AddArgumentPaddingSlots(int argument_count)
Definition register.h:14
kWasmInternalFunctionIndirectPointerTag kProtectedInstanceDataOffset sig
constexpr bool IsAnyTagged(MachineRepresentation rep)
constexpr int kSystemPointerSize
Definition globals.h:410
constexpr bool IsFloatingPoint(MachineRepresentation rep)
constexpr bool IsIntegral(MachineRepresentation rep)
constexpr Register kJSFunctionRegister
#define DCHECK(condition)
Definition logging.h:482
#define arraysize(array)
Definition macros.h:67