Commit f180d9fb authored by Eric Holk's avatar Eric Holk Committed by Commit Bot

[wasm] check trap handler consistency in debug builds; simplify structures

This CL includes validation code for the trap handler data structures in debug
mode to help catch issues like v8:6841 sooner in the future.

We also now eagerly initialize the free list pointers to make the logic of
finding the next free entry more obvious.

Bug: v8:5277
Change-Id: I13c3180c59b6152508c480e2042072a91e6ca977
Reviewed-on: https://chromium-review.googlesource.com/674128
Commit-Queue: Eric Holk <eholk@chromium.org>
Reviewed-by: 's avatarMircea Trofin <mtrofin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48186}
parent c431c974
...@@ -33,6 +33,12 @@ ...@@ -33,6 +33,12 @@
namespace { namespace {
size_t gNextCodeObject = 0; size_t gNextCodeObject = 0;
#if defined(DEBUG)
const bool kEnableDebug = true;
#else
const bool kEnableDebug = false;
#endif
} }
namespace v8 { namespace v8 {
...@@ -47,6 +53,66 @@ constexpr size_t HandlerDataSize(size_t num_protected_instructions) { ...@@ -47,6 +53,66 @@ constexpr size_t HandlerDataSize(size_t num_protected_instructions) {
num_protected_instructions * sizeof(ProtectedInstructionData); num_protected_instructions * sizeof(ProtectedInstructionData);
} }
namespace {
template <typename = std::enable_if<kEnableDebug>>
bool IsDisjoint(const CodeProtectionInfo* a, const CodeProtectionInfo* b) {
if (a == nullptr || b == nullptr) {
return true;
}
const auto a_base = reinterpret_cast<uintptr_t>(a->base);
const auto b_base = reinterpret_cast<uintptr_t>(b->base);
return a_base >= b_base + b->size || b_base >= a_base + a->size;
}
// Verify that the code range does not overlap any that have already been
// registered.
void VerifyCodeRangeIsDisjoint(const CodeProtectionInfo* code_info) {
for (size_t i = 0; i < gNumCodeObjects; ++i) {
DCHECK(IsDisjoint(code_info, gCodeObjects[i].code_info));
}
}
void ValidateCodeObjects() {
// Sanity-check the code objects
for (unsigned i = 0; i < gNumCodeObjects; ++i) {
const auto* data = gCodeObjects[i].code_info;
if (data == nullptr) continue;
// Do some sanity checks on the protected instruction data
for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
DCHECK_GE(data->instructions[i].instr_offset, 0);
DCHECK_LT(data->instructions[i].instr_offset, data->size);
DCHECK_GE(data->instructions[i].landing_offset, 0);
DCHECK_LT(data->instructions[i].landing_offset, data->size);
DCHECK_GT(data->instructions[i].landing_offset,
data->instructions[i].instr_offset);
}
}
// Check the validity of the free list.
size_t free_count = 0;
for (size_t i = gNextCodeObject; i != gNumCodeObjects;
i = gCodeObjects[i].next_free) {
DCHECK_LT(i, gNumCodeObjects);
++free_count;
// This check will fail if we encounter a cycle.
DCHECK_LE(free_count, gNumCodeObjects);
}
// Check that all free entries are reachable via the free list.
size_t free_count2 = 0;
for (size_t i = 0; i < gNumCodeObjects; ++i) {
if (gCodeObjects[i].code_info == nullptr) {
++free_count2;
}
}
DCHECK_EQ(free_count, free_count2);
}
} // namespace
CodeProtectionInfo* CreateHandlerData( CodeProtectionInfo* CreateHandlerData(
void* base, size_t size, size_t num_protected_instructions, void* base, size_t size, size_t num_protected_instructions,
ProtectedInstructionData* protected_instructions) { ProtectedInstructionData* protected_instructions) {
...@@ -91,6 +157,10 @@ int RegisterHandlerData(void* base, size_t size, ...@@ -91,6 +157,10 @@ int RegisterHandlerData(void* base, size_t size,
MetadataLock lock; MetadataLock lock;
if (kEnableDebug) {
VerifyCodeRangeIsDisjoint(data);
}
size_t i = gNextCodeObject; size_t i = gNextCodeObject;
// Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid // Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
...@@ -125,24 +195,24 @@ int RegisterHandlerData(void* base, size_t size, ...@@ -125,24 +195,24 @@ int RegisterHandlerData(void* base, size_t size,
memset(gCodeObjects + gNumCodeObjects, 0, memset(gCodeObjects + gNumCodeObjects, 0,
sizeof(*gCodeObjects) * (new_size - gNumCodeObjects)); sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
for (size_t j = gNumCodeObjects; j < new_size; ++j) {
gCodeObjects[j].next_free = j + 1;
}
gNumCodeObjects = new_size; gNumCodeObjects = new_size;
} }
DCHECK(gCodeObjects[i].code_info == nullptr); DCHECK(gCodeObjects[i].code_info == nullptr);
// Find out where the next entry should go. // Find out where the next entry should go.
if (gCodeObjects[i].next_free == 0) { gNextCodeObject = gCodeObjects[i].next_free;
// if this is a fresh entry, use the next one.
gNextCodeObject = i + 1;
DCHECK(gNextCodeObject == gNumCodeObjects ||
(gCodeObjects[gNextCodeObject].code_info == nullptr &&
gCodeObjects[gNextCodeObject].next_free == 0));
} else {
gNextCodeObject = gCodeObjects[i].next_free - 1;
}
if (i <= int_max) { if (i <= int_max) {
gCodeObjects[i].code_info = data; gCodeObjects[i].code_info = data;
if (kEnableDebug) {
ValidateCodeObjects();
}
return static_cast<int>(i); return static_cast<int>(i);
} else { } else {
return kInvalidIndex; return kInvalidIndex;
...@@ -163,12 +233,16 @@ void ReleaseHandlerData(int index) { ...@@ -163,12 +233,16 @@ void ReleaseHandlerData(int index) {
data = gCodeObjects[index].code_info; data = gCodeObjects[index].code_info;
gCodeObjects[index].code_info = nullptr; gCodeObjects[index].code_info = nullptr;
// +1 because we reserve {next_entry == 0} to indicate a fresh list entry. gCodeObjects[index].next_free = gNextCodeObject;
gCodeObjects[index].next_free = gNextCodeObject + 1;
gNextCodeObject = index; gNextCodeObject = index;
if (kEnableDebug) {
ValidateCodeObjects();
}
} }
// TODO(eholk): on debug builds, ensure there are no more copies in // TODO(eholk): on debug builds, ensure there are no more copies in
// the list. // the list.
DCHECK_NOT_NULL(data); // make sure we're releasing legitimate handler data.
free(data); free(data);
} }
......
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