deps: V8: backport 0a8b1cdcc8b2

Original commit message:

    implement rapidhash secret generation

    Bug: 409717082
    Change-Id: I471f33d66de32002f744aeba534c1d34f71e27d2
    Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/6733490
    Reviewed-by: Leszek Swirski <leszeks@chromium.org>
    Commit-Queue: snek <snek@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#101499}

Refs: https://github.com/v8/v8/commit/0a8b1cdcc8b243c62cf045fa8beb50600e11758a
Co-authored-by: Joyee Cheung <joyeec9h3@gmail.com>
Backport-PR-URL: https://github.com/nodejs-private/node-private/pull/833
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
PR-URL: https://github.com/nodejs-private/node-private/pull/809
CVE-ID: CVE-2026-21717
This commit is contained in:
snekandJoyee Cheung authored and Antoine du Hamel committed 2026-03-22 17:04:56 +01:00
1 parent 06fc3436f6
commit 076acd052d
36 files changed
+692 -78

No files matched your search

+1 -1
View File
@@ -38,7 +38,7 @@
# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.35',
'v8_embedder_string': '-node.36',
##### V8 defaults for Node.js #####
+1
View File
@@ -80,6 +80,7 @@
!/third_party/inspector_protocol
!/third_party/jsoncpp
/third_party/jsoncpp/source
!/third_party/rapidhash-v8
!/third_party/re2
/third_party/re2/src
!/third_party/test262-harness
+3
View File
@@ -1849,6 +1849,7 @@ filegroup(
"src/numbers/conversions.cc",
"src/numbers/conversions.h",
"src/numbers/conversions-inl.h",
"src/numbers/hash-seed.h",
"src/numbers/hash-seed-inl.h",
"src/numbers/integer-literal.h",
"src/numbers/integer-literal-inl.h",
@@ -2444,6 +2445,8 @@ filegroup(
"src/zone/zone-segment.h",
"src/zone/zone-type-traits.h",
"src/zone/zone-utils.h",
"third_party/rapidhash-v8/rapidhash.h",
"third_party/rapidhash-v8/secret.h",
":cppgc_base_files",
":generated_bytecode_builtins_list",
":v8_bigint",
+11 -1
View File
@@ -428,6 +428,9 @@ declare_args() {
# Whether custom embedder snapshots may extend (= allocate new objects in)
# ReadOnlySpace.
v8_enable_extensible_ro_snapshot = true
# Use a hard-coded secret value when hashing.
v8_use_default_hasher_secret = true
}
# Derived defaults.
@@ -863,6 +866,7 @@ config("external_startup_data") {
# Make sure the |v8_generate_features_json| action is also updated when adding
# or removing defines below.
external_v8_defines = [
"V8_USE_DEFAULT_HASHER_SECRET=${v8_use_default_hasher_secret}",
"V8_ENABLE_CHECKS",
"V8_COMPRESS_POINTERS",
"V8_COMPRESS_POINTERS_IN_SHARED_CAGE",
@@ -880,7 +884,9 @@ external_v8_defines = [
"V8_MINORMS_STRING_SHORTCUTTING",
]
enabled_external_v8_defines = []
enabled_external_v8_defines = [
"V8_USE_DEFAULT_HASHER_SECRET=${v8_use_default_hasher_secret}",
]
if (v8_enable_v8_checks) {
enabled_external_v8_defines += [ "V8_ENABLE_CHECKS" ]
@@ -2768,6 +2774,7 @@ generated_file("v8_generate_features_json") {
v8_random_seed = v8_random_seed
v8_use_perfetto = v8_use_perfetto
v8_use_siphash = v8_use_siphash
v8_use_default_hasher_secret = v8_use_default_hasher_secret
}
}
@@ -3763,6 +3770,7 @@ v8_header_set("v8_internal_headers") {
"src/numbers/conversions-inl.h",
"src/numbers/conversions.h",
"src/numbers/hash-seed-inl.h",
"src/numbers/hash-seed.h",
"src/numbers/math-random.h",
"src/objects/all-objects-inl.h",
"src/objects/allocation-site-inl.h",
@@ -4116,6 +4124,8 @@ v8_header_set("v8_internal_headers") {
"src/temporal/temporal-parser.h",
"src/third_party/siphash/halfsiphash.h",
"src/third_party/utf8-decoder/utf8-decoder.h",
"third_party/rapidhash-v8/rapidhash.h",
"third_party/rapidhash-v8/secret.h",
"src/torque/runtime-macro-shims.h",
"src/tracing/trace-event.h",
"src/tracing/traced-value.h",
+9
View File
@@ -123,4 +123,13 @@ specific_include_rules = {
"snapshot\.cc": [
"+src/heap/read-only-promotion.h",
],
"string-hasher-inl\.h": [
"+third_party/rapidhash-v8/rapidhash.h",
],
"heap\.cc": [
"+third_party/rapidhash-v8/secret.h",
],
"hash-seed-inl\.h": [
"+third_party/rapidhash-v8/secret.h",
],
}
+2 -1
View File
@@ -291,7 +291,8 @@ std::forward_list<const AstRawString*> AstConsString::ToRawStrings() const {
return result;
}
AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed)
AstStringConstants::AstStringConstants(Isolate* isolate,
const HashSeed hash_seed)
: zone_(isolate->allocator(), ZONE_NAME),
string_table_(),
hash_seed_(hash_seed) {
+7 -6
View File
@@ -35,6 +35,7 @@
#include "src/common/globals.h"
#include "src/handles/handles.h"
#include "src/numbers/conversions.h"
#include "src/numbers/hash-seed.h"
#include "src/objects/name.h"
// Ast(Raw|Cons)String and AstValueFactory are for storing strings and
@@ -290,7 +291,7 @@ using AstRawStringMap =
class AstStringConstants final {
public:
AstStringConstants(Isolate* isolate, uint64_t hash_seed);
AstStringConstants(Isolate* isolate, const HashSeed hash_seed);
AstStringConstants(const AstStringConstants&) = delete;
AstStringConstants& operator=(const AstStringConstants&) = delete;
@@ -299,13 +300,13 @@ class AstStringConstants final {
AST_STRING_CONSTANTS(F)
#undef F
uint64_t hash_seed() const { return hash_seed_; }
const HashSeed hash_seed() const { return hash_seed_; }
const AstRawStringMap* string_table() const { return &string_table_; }
private:
Zone zone_;
AstRawStringMap string_table_;
uint64_t hash_seed_;
const HashSeed hash_seed_;
#define F(name, str) AstRawString* name##_string_;
AST_STRING_CONSTANTS(F)
@@ -315,12 +316,12 @@ class AstStringConstants final {
class AstValueFactory {
public:
AstValueFactory(Zone* zone, const AstStringConstants* string_constants,
uint64_t hash_seed)
const HashSeed hash_seed)
: AstValueFactory(zone, zone, string_constants, hash_seed) {}
AstValueFactory(Zone* ast_raw_string_zone, Zone* single_parse_zone,
const AstStringConstants* string_constants,
uint64_t hash_seed)
const HashSeed hash_seed)
: string_table_(string_constants->string_table()),
strings_(nullptr),
strings_end_(&strings_),
@@ -418,7 +419,7 @@ class AstValueFactory {
Zone* ast_raw_string_zone_;
Zone* single_parse_zone_;
uint64_t hash_seed_;
const HashSeed hash_seed_;
};
extern template EXPORT_TEMPLATE_DECLARE(
+2 -1
View File
@@ -1176,7 +1176,8 @@ FUNCTION_REFERENCE(jsreceiver_create_identity_hash,
static uint32_t ComputeSeededIntegerHash(Isolate* isolate, int32_t key) {
DisallowGarbageCollection no_gc;
return ComputeSeededHash(static_cast<uint32_t>(key), HashSeed(isolate));
return ComputeSeededHash(static_cast<uint32_t>(key),
HashSeed(isolate).seed());
}
FUNCTION_REFERENCE(compute_integer_hash, ComputeSeededIntegerHash)
+2 -1
View File
@@ -910,7 +910,8 @@ uint32_t WasmScript::GetFunctionHash(int function_index) {
wire_bytes.GetFunctionBytes(&func);
// TODO(herhut): Maybe also take module, name and signature into account.
return i::StringHasher::HashSequentialString(function_bytes.begin(),
function_bytes.length(), 0);
function_bytes.length(),
internal::HashSeed::Default());
}
int WasmScript::CodeOffset() const {
+12
View File
@@ -119,6 +119,7 @@
#include "src/tracing/trace-event.h"
#include "src/utils/utils-inl.h"
#include "src/utils/utils.h"
#include "third_party/rapidhash-v8/secret.h"
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
#include "src/heap/conservative-stack-visitor.h"
@@ -5825,9 +5826,20 @@ void Heap::InitializeHashSeed() {
} else {
new_hash_seed = static_cast<uint64_t>(v8_flags.hash_seed);
}
Tagged<ByteArray> hash_seed = ReadOnlyRoots(this).hash_seed();
MemCopy(hash_seed->begin(), reinterpret_cast<uint8_t*>(&new_hash_seed),
kInt64Size);
#if V8_USE_DEFAULT_HASHER_SECRET
MemCopy(hash_seed->begin() + kInt64Size,
reinterpret_cast<const uint8_t*>(RAPIDHASH_DEFAULT_SECRET),
kInt64Size * 3);
#else
rapidhash_make_secret(new_hash_seed, reinterpret_cast<uint64_t*>(
hash_seed->begin() + kInt64Size));
#endif // V8_USE_DEFAULT_HASHER_SECRET
}
std::shared_ptr<v8::TaskRunner> Heap::GetForegroundTaskRunner() const {
+2 -1
View File
@@ -853,7 +853,8 @@ bool Heap::CreateImportantReadOnlyObjects() {
// Hash seed for strings
Factory* factory = isolate()->factory();
set_hash_seed(*factory->NewByteArray(kInt64Size, AllocationType::kReadOnly));
set_hash_seed(
*factory->NewByteArray(kInt64Size * 4, AllocationType::kReadOnly));
InitializeHashSeed();
// Important strings and symbols
-1
View File
@@ -314,7 +314,6 @@ bool JsonParseInternalizer::RecurseAndApply(Handle<JSReceiver> holder,
template <typename Char>
JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source)
: isolate_(isolate),
hash_seed_(HashSeed(isolate)),
object_constructor_(isolate_->object_function()),
original_source_(source) {
size_t start = 0;
-1
View File
@@ -385,7 +385,6 @@ class JsonParser final {
int position() const { return static_cast<int>(cursor_ - chars_); }
Isolate* isolate_;
const uint64_t hash_seed_;
JsonToken next_;
// Indicates whether the bytes underneath source_ can relocate during GC.
bool chars_may_relocate_;
+21 -32
View File
@@ -5,47 +5,36 @@
#ifndef V8_NUMBERS_HASH_SEED_INL_H_
#define V8_NUMBERS_HASH_SEED_INL_H_
#include <stdint.h>
// The #includes below currently lead to cyclic transitive includes, so
// HashSeed() ends up being required before it is defined, so we have to
// declare it here. This is a workaround; if we needed this permanently then
// we should put that line into a "hash-seed.h" header; but we won't need
// it for long.
// TODO(jkummerow): Get rid of this by breaking circular include dependencies.
namespace v8 {
namespace internal {
class Isolate;
class LocalIsolate;
class ReadOnlyRoots;
inline uint64_t HashSeed(Isolate* isolate);
inline uint64_t HashSeed(LocalIsolate* isolate);
inline uint64_t HashSeed(ReadOnlyRoots roots);
} // namespace internal
} // namespace v8
// See comment above for why this isn't at the top of the file.
#include "src/numbers/hash-seed.h"
#include "src/objects/fixed-array-inl.h"
#include "src/roots/roots-inl.h"
#include "third_party/rapidhash-v8/secret.h"
namespace v8 {
namespace internal {
inline uint64_t HashSeed(Isolate* isolate) {
return HashSeed(ReadOnlyRoots(isolate));
inline HashSeed::HashSeed(Isolate* isolate)
: HashSeed(ReadOnlyRoots(isolate)) {}
inline HashSeed::HashSeed(LocalIsolate* isolate)
: HashSeed(ReadOnlyRoots(isolate)) {}
inline HashSeed::HashSeed(ReadOnlyRoots roots) {
// roots.hash_seed is not aligned
MemCopy(&seed_, roots.hash_seed()->begin(), sizeof(seed_));
MemCopy(secret_, roots.hash_seed()->begin() + sizeof(seed_), sizeof(secret_));
}
inline uint64_t HashSeed(LocalIsolate* isolate) {
return HashSeed(ReadOnlyRoots(isolate));
}
inline HashSeed::HashSeed(uint64_t seed, const uint64_t secret[3])
: seed_(seed),
secret_{
secret[0],
secret[1],
secret[2],
} {}
inline uint64_t HashSeed(ReadOnlyRoots roots) {
uint64_t seed;
MemCopy(&seed, roots.hash_seed()->begin(), sizeof(seed));
return seed;
inline HashSeed HashSeed::Default() {
return HashSeed(0, RAPIDHASH_DEFAULT_SECRET);
}
} // namespace internal
+42
View File
@@ -0,0 +1,42 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_NUMBERS_HASH_SEED_H_
#define V8_NUMBERS_HASH_SEED_H_
#include <stdint.h>
namespace v8 {
namespace internal {
class Isolate;
class LocalIsolate;
class ReadOnlyRoots;
class HashSeed {
public:
inline explicit HashSeed(Isolate* isolate);
inline explicit HashSeed(LocalIsolate* isolate);
inline explicit HashSeed(ReadOnlyRoots roots);
inline explicit HashSeed(uint64_t seed, const uint64_t secret[3]);
static inline HashSeed Default();
uint64_t seed() const { return seed_; }
const uint64_t* secret() const { return secret_; }
constexpr bool operator==(const HashSeed& b) const {
return seed_ == b.seed_ && secret_[0] == b.secret_[0] &&
secret_[1] == b.secret_[1] && secret_[2] == b.secret_[2];
}
private:
uint64_t seed_;
uint64_t secret_[3];
};
} // namespace internal
} // namespace v8
#endif // V8_NUMBERS_HASH_SEED_H_
+2 -2
View File
@@ -320,14 +320,14 @@ bool NumberDictionaryBaseShape::IsMatch(uint32_t key, Tagged<Object> other) {
}
uint32_t NumberDictionaryBaseShape::Hash(ReadOnlyRoots roots, uint32_t key) {
return ComputeSeededHash(key, HashSeed(roots));
return ComputeSeededHash(key, HashSeed(roots).seed());
}
uint32_t NumberDictionaryBaseShape::HashForObject(ReadOnlyRoots roots,
Tagged<Object> other) {
DCHECK(IsNumber(other));
return ComputeSeededHash(static_cast<uint32_t>(Object::Number(other)),
HashSeed(roots));
HashSeed(roots).seed());
}
template <AllocationType allocation>
+5 -4
View File
@@ -399,7 +399,7 @@ Char FlatStringReader::Get(int index) const {
template <typename Char>
class SequentialStringKey final : public StringTableKey {
public:
SequentialStringKey(base::Vector<const Char> chars, uint64_t seed,
SequentialStringKey(base::Vector<const Char> chars, const HashSeed seed,
bool convert = false)
: SequentialStringKey(StringHasher::HashSequentialString<Char>(
chars.begin(), chars.length(), seed),
@@ -798,13 +798,14 @@ String::FlatContent::~FlatContent() {
#ifdef ENABLE_SLOW_DCHECKS
uint32_t String::FlatContent::ComputeChecksum() const {
constexpr uint64_t hashseed = 1;
uint32_t hash;
if (state_ == ONE_BYTE) {
hash = StringHasher::HashSequentialString(onebyte_start, length_, hashseed);
hash = StringHasher::HashSequentialString(onebyte_start, length_,
HashSeed::Default());
} else {
DCHECK_EQ(TWO_BYTE, state_);
hash = StringHasher::HashSequentialString(twobyte_start, length_, hashseed);
hash = StringHasher::HashSequentialString(twobyte_start, length_,
HashSeed::Default());
}
DCHECK_NE(kChecksumVerificationDisabled, hash);
return hash;
+3 -2
View File
@@ -567,7 +567,7 @@ Address StringTable::Data::TryStringToIndexOrLookupExisting(
return internalized.ptr();
}
uint64_t seed = HashSeed(isolate);
const HashSeed seed = HashSeed(isolate);
CharBuffer<Char> buffer;
const Char* chars;
@@ -588,7 +588,8 @@ Address StringTable::Data::TryStringToIndexOrLookupExisting(
}
// TODO(verwaest): Internalize to one-byte when possible.
SequentialStringKey<Char> key(raw_hash_field,
base::Vector<const Char>(chars, length), seed);
base::Vector<const Char>(chars, length),
seed.seed());
// String could be an array index.
if (Name::ContainsCachedArrayIndex(raw_hash_field)) {
+2 -2
View File
@@ -1660,7 +1660,7 @@ namespace {
template <typename Char>
uint32_t HashString(Tagged<String> string, size_t start, int length,
uint64_t seed,
const HashSeed seed,
const SharedStringAccessGuardIfNeeded& access_guard) {
DisallowGarbageCollection no_gc;
@@ -1703,7 +1703,7 @@ uint32_t String::ComputeAndSetRawHash(
DCHECK_IMPLIES(!v8_flags.shared_string_table, !HasHashCode());
// Store the hash code in the object.
uint64_t seed = HashSeed(EarlyGetReadOnlyRoots());
const HashSeed seed = HashSeed(EarlyGetReadOnlyRoots());
size_t start = 0;
Tagged<String> string = this;
StringShape shape(string);
+4 -3
View File
@@ -13,6 +13,7 @@
#include "src/base/logging.h"
#include "src/common/globals.h"
#include "src/handles/handles.h"
#include "src/numbers/hash-seed.h"
#include "src/objects/function-kind.h"
#include "src/objects/function-syntax-kind.h"
#include "src/objects/script.h"
@@ -202,7 +203,7 @@ class V8_EXPORT_PRIVATE ReusableUnoptimizedCompileState {
AstValueFactory* ast_value_factory() const {
return ast_value_factory_.get();
}
uint64_t hash_seed() const { return hash_seed_; }
HashSeed hash_seed() const { return hash_seed_; }
AccountingAllocator* allocator() const { return allocator_; }
const AstStringConstants* ast_string_constants() const {
return ast_string_constants_;
@@ -212,7 +213,7 @@ class V8_EXPORT_PRIVATE ReusableUnoptimizedCompileState {
LazyCompileDispatcher* dispatcher() const { return dispatcher_; }
private:
uint64_t hash_seed_;
const HashSeed hash_seed_;
AccountingAllocator* allocator_;
V8FileLogger* v8_file_logger_;
LazyCompileDispatcher* dispatcher_;
@@ -247,7 +248,7 @@ class V8_EXPORT_PRIVATE ParseInfo {
const UnoptimizedCompileFlags& flags() const { return flags_; }
// Getters for reusable state.
uint64_t hash_seed() const { return reusable_state_->hash_seed(); }
HashSeed hash_seed() const { return reusable_state_->hash_seed(); }
AccountingAllocator* allocator() const {
return reusable_state_->allocator();
}
+1 -2
View File
@@ -55,8 +55,7 @@ Isolate* HeapEntry::isolate() const { return snapshot_->profiler()->isolate(); }
uint32_t HeapSnapshotJSONSerializer::StringHash(const void* string) {
const char* s = reinterpret_cast<const char*>(string);
int len = static_cast<int>(strlen(s));
return StringHasher::HashSequentialString(s, len,
v8::internal::kZeroHashSeed);
return StringHasher::HashSequentialString(s, len, HashSeed::Default());
}
int HeapSnapshotJSONSerializer::to_node_index(const HeapEntry* e) {
+1 -1
View File
@@ -138,7 +138,7 @@ namespace {
inline uint32_t ComputeStringHash(const char* str, int len) {
uint32_t raw_hash_field = base::bits::RotateLeft32(
StringHasher::HashSequentialString(str, len, kZeroHashSeed), 2);
StringHasher::HashSequentialString(str, len, HashSeed::Default()), 2);
return Name::HashBits::decode(raw_hash_field);
}
+2 -2
View File
@@ -65,8 +65,8 @@ struct IntrinsicFunctionIdentifier {
}
uint32_t Hash() {
return StringHasher::HashSequentialString<uint8_t>(
data_, length_, v8::internal::kZeroHashSeed);
return StringHasher::HashSequentialString<uint8_t>(data_, length_,
HashSeed::Default());
}
const unsigned char* data_;
+3 -3
View File
@@ -48,7 +48,7 @@ uint32_t StringHasher::GetTrivialHash(int length) {
template <typename char_t>
uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length,
uint64_t seed) {
const HashSeed seed) {
static_assert(std::is_integral<char_t>::value);
static_assert(sizeof(char_t) <= 2);
using uchar = typename std::make_unsigned<char_t>::type;
@@ -80,7 +80,7 @@ uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length,
// Perform a regular hash computation, and additionally check
// if there are non-digit characters.
String::HashFieldType type = String::HashFieldType::kIntegerIndex;
uint32_t running_hash = static_cast<uint32_t>(seed);
uint32_t running_hash = static_cast<uint32_t>(seed.seed());
uint64_t index_big = 0;
const uchar* end = &chars[length];
while (chars != end) {
@@ -112,7 +112,7 @@ uint32_t StringHasher::HashSequentialString(const char_t* chars_raw, int length,
}
// Non-index hash.
uint32_t running_hash = static_cast<uint32_t>(seed);
uint32_t running_hash = static_cast<uint32_t>(seed.seed());
const uchar* end = &chars[length];
while (chars != end) {
running_hash = AddCharacterCore(running_hash, *chars++);
+4 -3
View File
@@ -6,6 +6,7 @@
#define V8_STRINGS_STRING_HASHER_H_
#include "src/common/globals.h"
#include "src/numbers/hash-seed.h"
namespace v8 {
@@ -23,7 +24,7 @@ class V8_EXPORT_PRIVATE StringHasher final {
StringHasher() = delete;
template <typename char_t>
static inline uint32_t HashSequentialString(const char_t* chars, int length,
uint64_t seed);
const HashSeed seed);
// Calculated hash value for a string consisting of 1 to
// String::kMaxArrayIndexSize digits with no leading zeros (except "0").
@@ -44,10 +45,10 @@ class V8_EXPORT_PRIVATE StringHasher final {
// Useful for std containers that require something ()'able.
struct SeededStringHasher {
explicit SeededStringHasher(uint64_t hashseed) : hashseed_(hashseed) {}
explicit SeededStringHasher(const HashSeed hashseed) : hashseed_(hashseed) {}
inline std::size_t operator()(const char* name) const;
uint64_t hashseed_;
const HashSeed hashseed_;
};
// Useful for std containers that require something ()'able.
-2
View File
@@ -251,8 +251,6 @@ inline T RoundingAverageUnsigned(T a, T b) {
// ----------------------------------------------------------------------------
// Hash function.
static const uint64_t kZeroHashSeed = 0;
// Thomas Wang, Integer Hash Functions.
// http://www.concentric.net/~Ttwang/tech/inthash.htm`
inline uint32_t ComputeUnseededHash(uint32_t key) {
+1 -1
View File
@@ -762,7 +762,7 @@ int JumpTableOffset(const WasmModule* module, int func_index) {
size_t GetWireBytesHash(base::Vector<const uint8_t> wire_bytes) {
return StringHasher::HashSequentialString(
reinterpret_cast<const char*>(wire_bytes.begin()), wire_bytes.length(),
kZeroHashSeed);
HashSeed::Default());
}
int NumFeedbackSlots(const WasmModule* module, int func_index) {
+1 -1
View File
@@ -6644,7 +6644,7 @@ UNINITIALIZED_TEST(ReinitializeStringHashSeed) {
{
v8::Isolate::Scope isolate_scope(isolate);
CHECK_EQ(static_cast<uint64_t>(1337 * i),
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
HashSeed(reinterpret_cast<i::Isolate*>(isolate)).seed());
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
+1 -1
View File
@@ -501,7 +501,7 @@ TEST(ComputeIntegerHash) {
Handle<Smi> key(Smi::FromInt(k), isolate);
Handle<Object> result = ft.Call(key).ToHandleChecked();
uint32_t hash = ComputeSeededHash(k, HashSeed(isolate));
uint32_t hash = ComputeSeededHash(k, HashSeed(isolate).seed());
Tagged<Smi> expected = Smi::FromInt(hash);
CHECK_EQ(expected, Smi::cast(*result));
}
+2 -2
View File
@@ -4878,7 +4878,7 @@ UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) {
v8::Isolate::Scope isolate_scope(isolate);
// Check that rehashing has been performed.
CHECK_EQ(static_cast<uint64_t>(1337),
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
HashSeed(reinterpret_cast<i::Isolate*>(isolate)).seed());
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
@@ -4947,7 +4947,7 @@ UNINITIALIZED_TEST(ReinitializeHashSeedRehashable) {
v8::Isolate::Scope isolate_scope(isolate);
// Check that rehashing has been performed.
CHECK_EQ(static_cast<uint64_t>(1337),
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
HashSeed(reinterpret_cast<i::Isolate*>(isolate)).seed());
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
CHECK(!context.IsEmpty());
+1 -1
View File
@@ -128,7 +128,7 @@ UNINITIALIZED_TEST(InPlaceInternalizableStringsAreShared) {
CHECK(!InAnySharedSpace(*young_two_byte_seq));
// Internalized strings are shared.
uint64_t seed = HashSeed(i_isolate1);
HashSeed seed = HashSeed(i_isolate1);
Handle<String> one_byte_intern = factory1->NewOneByteInternalizedString(
base::OneByteVector(raw_one_byte),
StringHasher::HashSequentialString<char>(raw_one_byte, 3, seed));
+34
View File
@@ -0,0 +1,34 @@
/*
* rapidhash - Very fast, high quality, platform independant hashing algorithm.
* Copyright (C) 2024 Nicolas De Carli
*
* Based on 'wyhash', by Wang Yi <godspeed_china@yeah.net>
*
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at:
* - rapidhash source repository: https://github.com/Nicoshev/rapidhash
*/
+2
View File
@@ -0,0 +1,2 @@
leszeks@chromium.org
mlippautz@chromium.org
+20
View File
@@ -0,0 +1,20 @@
Name: rapidhash
URL: https://github.com/Nicoshev/rapidhash/blob/master/rapidhash.h
Version: N/A
Date: 2024-07-08
Revision: 588978411df8683777429f729be5213eb1bfd5f3
License: BSD 2-clause
License File: LICENSE
Shipped: Yes
Security Critical: yes
Description:
A fast hash function.
This is a fork of upstream, with the parts that we don't need or want
removed. We do not intend to sync regularly with upstream git. This
particular version is copied over from Chromium.
Local Modifications:
- Copied over from Chromium's third_party/rapidhash with all its changes.
- Removed base/ includes and replaced with V8 versions.
+290
View File
@@ -0,0 +1,290 @@
/*
* rapidhash - Very fast, high quality, platform-independent hashing algorithm.
* Copyright (C) 2024 Nicolas De Carli
*
* Based on 'wyhash', by Wang Yi <godspeed_china@yeah.net>
*
* BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at:
* - rapidhash source repository: https://github.com/Nicoshev/rapidhash
*/
#ifndef _THIRD_PARTY_RAPIDHASH_H
#define _THIRD_PARTY_RAPIDHASH_H 1
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <tuple>
#include <utility>
#include "include/v8config.h"
#include "src/base/logging.h"
// Chromium has some local modifications to upstream rapidhash,
// mostly around the concept of HashReaders (including slightly
// more comments for ease of understanding). Generally, rapidhash
// hashes bytes without really caring what these bytes are,
// but often in Chromium, we want to hash _strings_, and strings
// can have multiple representations. In particular, the WTF
// StringImpl class (and by extension, String and AtomicString)
// can have three different states:
//
// 1. Latin1 (or ASCII) code points, in 8 bits per character (LChar).
// 2. The same code points, in 16 bits per character (UChar).
// 3. Strings actually including non-Latin1 code points, in 16 bits
// per character (UChar, UTF-16 encoding).
//
// The problem is that we'd like to hash case #1 and #2 to actually
// return the same hash. There are several ways we could deal with this:
//
// a) Just ignore the problem and hash everything as raw bytes;
// case #2 is fairly rare, and some algorithms (e.g. opportunistic
// deduplication) could live with some false negatives.
// b) Expand all strings to UTF-16 before hashing. This was the
// strategy for the old StringHasher (using SuperFastHash),
// as it naturally worked in 16-bit increments and it is probably
// the simplest. However, this both halves the throughput of the
// hasher and incurs conversion costs.
// c) Detect #2 and convert those cases to #1 (UTF-16 to Latin1
// conversion), and apart from that, juts hash bytes.
//
// b) is used in e.g. CaseFoldingHash, but c) is the one we use the most
// often. Most strings that we want to hash are 8-bit (e.g. HTML tags), so
// that makes the common case fast. And for UChar strings, it is fairly fast
// to make a pre-pass over the string to check for the existence of any
// non-Latin1 characters. Of course, #1 and #3 can just be hashed as raw
// bytes, as strings from those groups can never be equal anyway.
//
// To support these 8-to-16 and 16-to-8 conversions, and also things like
// lowercasing on the fly, we have modified rapidhash to be templatized
// on a HashReader, supplying bytes to the hash function. For the default
// case of just hashing raw bytes, this is simply reading. But for other
// conversions, the reader is doing slightly more work, such as packing
// or unpacking. See e.g. ConvertTo8BitHashReader in string_hasher.h.
//
// Note that this reader could be made constexpr if we needed to evaluate
// hashes at compile-time.
struct PlainHashReader {
// If this is different from 1 (only 1, 2 and 4 are really reasonable
// options), it means the reader consumes its input at a faster rate than
// would be normally expected. For instance, if it is 2, it means that when
// the reader produces 64 bits, it needs to move its input 128 bits
// ahead. This is used when e.g. a reader converts from UTF-16 to ASCII,
// by removing zeros. The input length must divide the compression factor.
static constexpr unsigned kCompressionFactor = 1;
// This is the opposite of kExpansionFactor. It does not make sense to have
// both kCompressionFactor and kExpansionFactor different from 1.
// The output length must divide the expansion factor.
static constexpr unsigned kExpansionFactor = 1;
// Read 64 little-endian bits from the input, taking into account
// the expansion/compression if relevant. Unaligned reads must be
// supported.
static inline uint64_t Read64(const uint8_t* p) {
uint64_t v;
memcpy(&v, p, 8);
return v;
}
// Similarly, read 32 little-endian (unaligned) bits. Note that
// this must return uint64_t, _not_ uint32_t, as the hasher assumes
// it can freely shift such numbers up and down.
static inline uint64_t Read32(const uint8_t* p) {
uint32_t v;
memcpy(&v, p, 4);
return v;
}
// Read 1, 2 or 3 bytes from the input, and distribute them somewhat
// reasonably across the resulting 64-bit number. Note that this is
// done in a branch-free fashion, so that it always reads three bytes
// but never reads past the end.
//
// This is only used in the case where we hash a string of exactly
// 1, 2 or 3 bytes; if the hash is e.g. 7 bytes, two overlapping 32-bit
// reads will be done instead. Note that if you have kCompressionFactor == 2,
// this means that this will only ever be called with k = 2.
static inline uint64_t ReadSmall(const uint8_t* p, size_t k) {
return (uint64_t{p[0]} << 56) | (uint64_t{p[k >> 1]} << 32) | p[k - 1];
}
};
/*
* Likely and unlikely macros.
*/
#ifdef _MSC_VER
#define _likely_(x) (x)
#define _unlikely_(x) (x)
#else
#define _likely_(x) __builtin_expect(x, 1)
#define _unlikely_(x) __builtin_expect(x, 0)
#endif
/*
* 64*64 -> 128bit multiply function.
*
* @param A Address of 64-bit number.
* @param B Address of 64-bit number.
*
* Calculates 128-bit C = *A * *B.
*/
inline std::pair<uint64_t, uint64_t> rapid_mul128(uint64_t A, uint64_t B) {
#if defined(__SIZEOF_INT128__)
__uint128_t r = A;
r *= B;
return {static_cast<uint64_t>(r), static_cast<uint64_t>(r >> 64)};
#else
// High and low 32 bits of A and B.
uint64_t a_high = A >> 32, b_high = B >> 32, a_low = (uint32_t)A,
b_low = (uint32_t)B;
// Intermediate products.
uint64_t result_high = a_high * b_high;
uint64_t result_m0 = a_high * b_low;
uint64_t result_m1 = b_high * a_low;
uint64_t result_low = a_low * b_low;
// Final sum. The lower 64-bit addition can overflow twice,
// so accumulate carry as we go.
uint64_t high = result_high + (result_m0 >> 32) + (result_m1 >> 32);
uint64_t t = result_low + (result_m0 << 32);
high += (t < result_low); // Carry.
uint64_t low = t + (result_m1 << 32);
high += (low < t); // Carry.
return {low, high};
#endif
}
/*
* Multiply and xor mix function.
*
* @param A 64-bit number.
* @param B 64-bit number.
*
* Calculates 128-bit C = A * B.
* Returns 64-bit xor between high and low 64 bits of C.
*/
inline uint64_t rapid_mix(uint64_t A, uint64_t B) {
std::tie(A, B) = rapid_mul128(A, B);
return A ^ B;
}
/*
* rapidhash main function.
*
* @param key Buffer to be hashed.
* @param len Number of input bytes coming from the reader.
* @param seed 64-bit seed used to alter the hash result predictably.
* @param secret Triplet of 64-bit secrets used to alter hash result
* predictably.
*
* Returns a 64-bit hash.
*
* The data flow is separated so that we never mix input data with pointers;
*
* a, b, seed, secret[0], secret[1], secret[2], see1 and see2 are affected
* by the input data.
*
* p is only ever indexed by len, delta (comes from len only), i (comes from
* len only) or integral constants. len is const, so no data can flow into it.
*
* No other reads from memory take place. No writes to memory take place.
*/
template <class Reader = PlainHashReader>
V8_INLINE uint64_t rapidhash(const uint8_t* p, const size_t len, uint64_t seed,
const uint64_t secret[3]) {
// For brevity.
constexpr unsigned x = Reader::kCompressionFactor;
constexpr unsigned y = Reader::kExpansionFactor;
DCHECK_EQ(len % y, 0u);
seed ^= rapid_mix(seed ^ secret[0], secret[1]) ^ len;
uint64_t a, b;
if (_likely_(len <= 16)) {
if (_likely_(len >= 4)) {
// Read the first and last 32 bits (they may overlap).
const uint8_t* plast = p + (len - 4) * x / y;
a = (Reader::Read32(p) << 32) | Reader::Read32(plast);
// This is equivalent to: delta = (len >= 8) ? 4 : 0;
const uint64_t delta = ((len & 24) >> (len >> 3)) * x / y;
b = ((Reader::Read32(p + delta) << 32) | Reader::Read32(plast - delta));
} else if (_likely_(len > 0)) {
// 1, 2 or 3 bytes.
a = Reader::ReadSmall(p, len);
b = 0;
} else {
a = b = 0;
}
} else {
size_t i = len;
if (_unlikely_(i > 48)) {
uint64_t see1 = seed, see2 = seed;
do {
seed = rapid_mix(Reader::Read64(p) ^ secret[0],
Reader::Read64(p + 8 * x / y) ^ seed);
see1 = rapid_mix(Reader::Read64(p + 16 * x / y) ^ secret[1],
Reader::Read64(p + 24 * x / y) ^ see1);
see2 = rapid_mix(Reader::Read64(p + 32 * x / y) ^ secret[2],
Reader::Read64(p + 40 * x / y) ^ see2);
p += 48 * x / y;
i -= 48;
} while (_likely_(i >= 48));
seed ^= see1 ^ see2;
}
if (i > 16) {
seed = rapid_mix(Reader::Read64(p) ^ secret[2],
Reader::Read64(p + 8 * x / y) ^ seed ^ secret[1]);
if (i > 32) {
seed = rapid_mix(Reader::Read64(p + 16 * x / y) ^ secret[2],
Reader::Read64(p + 24 * x / y) ^ seed);
}
}
// Read the last 2x64 bits. Note that due to the division by y,
// this must be a signed quantity (or the division would become
// unsigned, possibly moving the sign bit down into the address).
// Similarly for x.
a = Reader::Read64(p + (static_cast<ptrdiff_t>(i) - 16) *
static_cast<int>(x) / static_cast<int>(y));
b = Reader::Read64(p + (static_cast<ptrdiff_t>(i) - 8) *
static_cast<int>(x) / static_cast<int>(y));
}
a ^= secret[1];
b ^= seed;
std::tie(a, b) = rapid_mul128(a, b);
return rapid_mix(a ^ secret[0] ^ len, b ^ secret[1]);
}
#undef _likely_
#undef _unlikely_
#endif // _THIRD_PARTY_RAPIDHASH_H
+198
View File
@@ -0,0 +1,198 @@
/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <https://unlicense.org/>
*
* author: 王一 Wang Yi <godspeed_china@yeah.net>
* contributors: Reini Urban, Dietrich Epp, Joshua Haberman, Tommy Ettinger,
* Daniel Lemire, Otmar Ertl, cocowalla, leo-yuriev, Diego Barrios Romero,
* paulie-g, dumblob, Yann Collet, ivte-ms, hyb, James Z.M. Gao, easyaspi314
* (Devin), TheOneric
*/
#ifndef _THIRD_PARTY_RAPIDHASH_SECRET_H
#define _THIRD_PARTY_RAPIDHASH_SECRET_H 1
#include "rapidhash.h"
#include "src/base/bits.h"
// Default secret parameters. If we wanted to, we could generate our own
// versions of these at renderer startup in order to perturb the hash
// and make it more DoS-resistant (similar to what base/hash.h does),
// but generating new ones takes a little bit of time (~200 µs on a desktop
// machine as of 2024), and good-quality random numbers may not be copious
// from within the sandbox. The secret concept is inherited from wyhash,
// described by the wyhash author here:
//
// https://github.com/wangyi-fudan/wyhash/issues/139
//
// The rules are:
//
// 1. Each byte must be “balanced”, i.e., have exactly 4 bits set.
// (This is trivially done by just precompting a LUT with the
// possible bytes and picking from those.)
//
// 2. Each 64-bit group should have a Hamming distance of 32 to
// all the others (i.e., popcount(secret[i] ^ secret[j]) == 32).
// This is just done by rejection sampling.
//
// 3. Each 64-bit group should be prime. It's not obvious that this
// is really needed for the hash, as opposed to wyrand which also
// uses the same secret, but according to the author, it is
// “a feeling to be perfect”. This naturally means the last byte
// cannot be divisible by 2, but apart from that, is easiest
// checked by testing a few small factors and then the Miller-Rabin
// test, which rejects nearly all bad candidates in the first iteration
// and is fast as long as we have 64x64 -> 128 bit muls and modulos.
static constexpr uint64_t RAPIDHASH_DEFAULT_SECRET[3] = {
0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, 0x4b33a62ed433d4a3ull};
#if !V8_USE_DEFAULT_HASHER_SECRET
namespace detail {
static inline uint64_t wyrand(uint64_t* seed) {
*seed += 0x2d358dccaa6c78a5ull;
return rapid_mix(*seed, *seed ^ 0x8bb84b93962eacc9ull);
}
static inline unsigned long long mul_mod(unsigned long long a,
unsigned long long b,
unsigned long long m) {
unsigned long long r = 0;
while (b) {
if (b & 1) {
unsigned long long r2 = r + a;
if (r2 < r) r2 -= m;
r = r2 % m;
}
b >>= 1;
if (b) {
unsigned long long a2 = a + a;
if (a2 < a) a2 -= m;
a = a2 % m;
}
}
return r;
}
static inline unsigned long long pow_mod(unsigned long long a,
unsigned long long b,
unsigned long long m) {
unsigned long long r = 1;
while (b) {
if (b & 1) r = mul_mod(r, a, m);
b >>= 1;
if (b) a = mul_mod(a, a, m);
}
return r;
}
static unsigned sprp(unsigned long long n, unsigned long long a) {
unsigned long long d = n - 1;
unsigned char s = 0;
while (!(d & 0xff)) {
d >>= 8;
s += 8;
}
if (!(d & 0xf)) {
d >>= 4;
s += 4;
}
if (!(d & 0x3)) {
d >>= 2;
s += 2;
}
if (!(d & 0x1)) {
d >>= 1;
s += 1;
}
unsigned long long b = pow_mod(a, d, n);
if ((b == 1) || (b == (n - 1))) return 1;
unsigned char r;
for (r = 1; r < s; r++) {
b = mul_mod(b, b, n);
if (b <= 1) return 0;
if (b == (n - 1)) return 1;
}
return 0;
}
static unsigned is_prime(unsigned long long n) {
if (n < 2 || !(n & 1)) return 0;
if (n < 4) return 1;
if (!sprp(n, 2)) return 0;
if (n < 2047) return 1;
if (!sprp(n, 3)) return 0;
if (!sprp(n, 5)) return 0;
if (!sprp(n, 7)) return 0;
if (!sprp(n, 11)) return 0;
if (!sprp(n, 13)) return 0;
if (!sprp(n, 17)) return 0;
if (!sprp(n, 19)) return 0;
if (!sprp(n, 23)) return 0;
if (!sprp(n, 29)) return 0;
if (!sprp(n, 31)) return 0;
if (!sprp(n, 37)) return 0;
return 1;
}
} // namespace detail
static inline void rapidhash_make_secret(uint64_t seed, uint64_t* secret) {
uint8_t c[] = {15, 23, 27, 29, 30, 39, 43, 45, 46, 51, 53, 54,
57, 58, 60, 71, 75, 77, 78, 83, 85, 86, 89, 90,
92, 99, 101, 102, 105, 106, 108, 113, 114, 116, 120, 135,
139, 141, 142, 147, 149, 150, 153, 154, 156, 163, 165, 166,
169, 170, 172, 177, 178, 180, 184, 195, 197, 198, 201, 202,
204, 209, 210, 212, 216, 225, 226, 228, 232, 240};
for (size_t i = 0; i < 3; i++) {
uint8_t ok;
do {
ok = 1;
secret[i] = 0;
for (size_t j = 0; j < 64; j += 8)
secret[i] |= static_cast<uint64_t>(c[detail::wyrand(&seed) % sizeof(c)])
<< j;
if (secret[i] % 2 == 0) {
ok = 0;
continue;
}
for (size_t j = 0; j < i; j++) {
if (v8::base::bits::CountPopulation(secret[j] ^ secret[i]) != 32) {
ok = 0;
break;
}
}
if (ok && !detail::is_prime(secret[i])) {
ok = 0;
}
} while (!ok);
}
}
#endif // !V8_USE_DEFAULT_HASHER_SECRET
#endif // _THIRD_PARTY_RAPIDHASH_SECRET_H