Commit 06b59094 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[embedded] Split blob hash into data/code hashes

.. and add a --text-is-readable flag to support non-readable .text
sections.

This splits the embedded blob hash into two dedicated hashes for data
and code sections. The main benefit is that we can now keep at least a
partial hash even with non-readable .text sections.

The second part of this CL adds a --text-is-readable runtime flag to
support such platforms (with non-readable .text).

It currently doesn't do much; setting it enables a few additional
DCHECKs, disables the constant pool on x64, and and disables
verification of the embedded blob's *code* hash.

Bug: v8:10707
Change-Id: Ib91ed8b50b50f2cd81677f62920bea6fb92af453
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2504251Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70827}
parent df591efb
......@@ -246,6 +246,9 @@ bool ConstPool::AddSharedEntry(uint64_t data, int offset) {
bool ConstPool::TryRecordEntry(intptr_t data, RelocInfo::Mode mode) {
if (!FLAG_partial_constant_pool) return false;
DCHECK_WITH_MSG(
FLAG_text_is_readable,
"The partial constant pool requires a readable .text section");
if (!RelocInfo::IsShareableRelocMode(mode)) return false;
// Currently, partial constant pool only handles the following kinds of
......
......@@ -425,6 +425,8 @@ static int DecodeIt(Isolate* isolate, ExternalReferenceEncoder* ref_encoder,
int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
byte* end, CodeReference code, Address current_pc) {
DCHECK_WITH_MSG(FLAG_text_is_readable,
"Builtins disassembly requires a readable .text section");
V8NameConverter v8NameConverter(isolate, code);
if (isolate) {
// We have an isolate, so support external reference names.
......
......@@ -294,11 +294,20 @@ void Isolate::SetEmbeddedBlob(const uint8_t* code, uint32_t code_size,
// Verify that the contents of the embedded blob are unchanged from
// serialization-time, just to ensure the compiler isn't messing with us.
EmbeddedData d = EmbeddedData::FromBlob();
if (d.EmbeddedBlobHash() != d.CreateEmbeddedBlobHash()) {
if (d.EmbeddedBlobDataHash() != d.CreateEmbeddedBlobDataHash()) {
FATAL(
"Embedded blob checksum verification failed. This indicates that the "
"embedded blob has been modified since compilation time. A common "
"cause is a debugging breakpoint set within builtin code.");
"Embedded blob data section checksum verification failed. This "
"indicates that the embedded blob has been modified since compilation "
"time.");
}
if (FLAG_text_is_readable) {
if (d.EmbeddedBlobCodeHash() != d.CreateEmbeddedBlobCodeHash()) {
FATAL(
"Embedded blob code section checksum verification failed. This "
"indicates that the embedded blob has been modified since "
"compilation time. A common cause is a debugging breakpoint set "
"within builtin code.");
}
}
#endif // DEBUG
......
......@@ -1547,6 +1547,11 @@ DEFINE_STRING(turbo_profiling_log_file, nullptr,
"Path of the input file containing basic block counters for "
"builtins. (mksnapshot only)")
// On some platforms, the .text section only has execute permissions.
DEFINE_BOOL(text_is_readable, true,
"Whether the .text section of binary can be read")
DEFINE_NEG_NEG_IMPLICATION(text_is_readable, partial_constant_pool)
//
// Minor mark compact collector flags.
//
......
......@@ -306,13 +306,20 @@ EmbeddedData EmbeddedData::FromIsolate(Isolate* isolate) {
// Hash the blob and store the result.
{
STATIC_ASSERT(EmbeddedBlobHashSize() == kSizetSize);
const size_t hash = d.CreateEmbeddedBlobHash();
std::memcpy(blob_data + EmbeddedBlobHashOffset(), &hash,
EmbeddedBlobHashSize());
DCHECK_EQ(hash, d.CreateEmbeddedBlobHash());
DCHECK_EQ(hash, d.EmbeddedBlobHash());
STATIC_ASSERT(EmbeddedBlobDataHashSize() == kSizetSize);
const size_t data_hash = d.CreateEmbeddedBlobDataHash();
std::memcpy(blob_data + EmbeddedBlobDataHashOffset(), &data_hash,
EmbeddedBlobDataHashSize());
STATIC_ASSERT(EmbeddedBlobCodeHashSize() == kSizetSize);
const size_t code_hash = d.CreateEmbeddedBlobCodeHash();
std::memcpy(blob_data + EmbeddedBlobCodeHashOffset(), &code_hash,
EmbeddedBlobCodeHashSize());
DCHECK_EQ(data_hash, d.CreateEmbeddedBlobDataHash());
DCHECK_EQ(data_hash, d.EmbeddedBlobDataHash());
DCHECK_EQ(code_hash, d.CreateEmbeddedBlobCodeHash());
DCHECK_EQ(code_hash, d.EmbeddedBlobCodeHash());
}
if (FLAG_serialization_statistics) d.PrintStatistics();
......@@ -361,14 +368,23 @@ Address EmbeddedData::InstructionEndOfBytecodeHandlers() const {
InstructionSizeOfBuiltin(lastBytecodeHandler);
}
size_t EmbeddedData::CreateEmbeddedBlobHash() const {
STATIC_ASSERT(EmbeddedBlobHashOffset() == 0);
STATIC_ASSERT(EmbeddedBlobHashSize() == kSizetSize);
// Hash the entire blob except the hash field itself.
Vector<const byte> payload1(data_ + EmbeddedBlobHashSize(),
data_size_ - EmbeddedBlobHashSize());
Vector<const byte> payload2(code_, code_size_);
return Checksum(payload1, payload2);
size_t EmbeddedData::CreateEmbeddedBlobDataHash() const {
STATIC_ASSERT(EmbeddedBlobDataHashOffset() == 0);
STATIC_ASSERT(EmbeddedBlobCodeHashOffset() == EmbeddedBlobDataHashSize());
STATIC_ASSERT(IsolateHashOffset() ==
EmbeddedBlobCodeHashOffset() + EmbeddedBlobCodeHashSize());
static constexpr uint32_t kFirstHashedDataOffset = IsolateHashOffset();
// Hash the entire data section except the embedded blob hash fields
// themselves.
Vector<const byte> payload(data_ + kFirstHashedDataOffset,
data_size_ - kFirstHashedDataOffset);
return Checksum(payload);
}
size_t EmbeddedData::CreateEmbeddedBlobCodeHash() const {
CHECK(FLAG_text_is_readable);
Vector<const byte> payload(code_, code_size_);
return Checksum(payload);
}
void EmbeddedData::PrintStatistics() const {
......
......@@ -90,9 +90,15 @@ class EmbeddedData final {
return PadAndAlignCode(size);
}
size_t CreateEmbeddedBlobHash() const;
size_t EmbeddedBlobHash() const {
return *reinterpret_cast<const size_t*>(data_ + EmbeddedBlobHashOffset());
size_t CreateEmbeddedBlobDataHash() const;
size_t CreateEmbeddedBlobCodeHash() const;
size_t EmbeddedBlobDataHash() const {
return *reinterpret_cast<const size_t*>(data_ +
EmbeddedBlobDataHashOffset());
}
size_t EmbeddedBlobCodeHash() const {
return *reinterpret_cast<const size_t*>(data_ +
EmbeddedBlobCodeHashOffset());
}
size_t IsolateHash() const {
......@@ -124,9 +130,10 @@ class EmbeddedData final {
// The layout of the blob is as follows:
//
// data:
// [0] hash of the remaining blob
// [1] hash of embedded-blob-relevant heap objects
// [2] layout description of instruction stream 0
// [0] hash of the data section
// [1] hash of the code section
// [2] hash of embedded-blob-relevant heap objects
// [3] layout description of instruction stream 0
// ... layout descriptions
// [x] metadata section of builtin 0
// ... metadata sections
......@@ -136,10 +143,14 @@ class EmbeddedData final {
// ... instruction sections
static constexpr uint32_t kTableSize = Builtins::builtin_count;
static constexpr uint32_t EmbeddedBlobHashOffset() { return 0; }
static constexpr uint32_t EmbeddedBlobHashSize() { return kSizetSize; }
static constexpr uint32_t EmbeddedBlobDataHashOffset() { return 0; }
static constexpr uint32_t EmbeddedBlobDataHashSize() { return kSizetSize; }
static constexpr uint32_t EmbeddedBlobCodeHashOffset() {
return EmbeddedBlobDataHashOffset() + EmbeddedBlobDataHashSize();
}
static constexpr uint32_t EmbeddedBlobCodeHashSize() { return kSizetSize; }
static constexpr uint32_t IsolateHashOffset() {
return EmbeddedBlobHashOffset() + EmbeddedBlobHashSize();
return EmbeddedBlobCodeHashOffset() + EmbeddedBlobCodeHashSize();
}
static constexpr uint32_t IsolateHashSize() { return kSizetSize; }
static constexpr uint32_t LayoutDescriptionTableOffset() {
......
......@@ -21,20 +21,5 @@ uint32_t Checksum(Vector<const byte> payload) {
return static_cast<uint32_t>(adler32(0, payload.begin(), payload.length()));
}
V8_EXPORT_PRIVATE uint32_t Checksum(Vector<const byte> payload1,
Vector<const byte> payload2) {
#ifdef MEMORY_SANITIZER
// Computing the checksum includes padding bytes for objects like strings.
// Mark every object as initialized in the code serializer.
MSAN_MEMORY_IS_INITIALIZED(payload1.begin(), payload1.length());
MSAN_MEMORY_IS_INITIALIZED(payload2.begin(), payload2.length());
#endif // MEMORY_SANITIZER
// Priming the adler32 call so it can see what CPU features are available.
adler32(0, nullptr, 0);
auto sum = adler32(0, payload1.begin(), payload1.length());
sum = adler32(sum, payload2.begin(), payload2.length());
return static_cast<uint32_t>(sum);
}
} // namespace internal
} // namespace v8
......@@ -11,8 +11,6 @@ namespace v8 {
namespace internal {
V8_EXPORT_PRIVATE uint32_t Checksum(Vector<const byte> payload);
V8_EXPORT_PRIVATE uint32_t Checksum(Vector<const byte> payload1,
Vector<const byte> payload2);
} // namespace internal
} // namespace v8
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment