[heap] Fix i-cache flushing operation order.

This unifies the order of i-cache flushing and permission changing
throughout V8. According to cctest/test-icache flushing after the
permission change is not robust on some ARM32 and ARM64 devices.

There have been observed failures of {TestFlushICacheOfExecutable} on
some devices. So far there haven't been any observed failures of the
corresponding {TestFlushICacheOfWritable} test.

Also the order of flushing before the permission change is the natural
order in which the GC currently performs operations. Until we see
concrete data substantiating the opposite, the following is the
supported and intended order throughout V8:

  exec -> perm(RW) -> patch -> flush -> perm(RX) -> exec

This CL tries to establish said order throughout the codebase.

R=ulan@chromium.org
TEST=cctest/test-icache
BUG=v8:8507,chromium:845877

Change-Id: Ic945082e643aa2d142d222a7913a99816aff4644
Reviewed-on: https://chromium-review.googlesource.com/c/1351025
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57869}
This commit is contained in:
Michael Starzinger 2018-11-27 11:54:07 +01:00 committed by Commit Bot
parent cbe1cfa249
commit 64d373e51e
4 changed files with 23 additions and 9 deletions

View File

@ -2726,9 +2726,14 @@ MaybeHandle<Code> Factory::TryNewCode(
source_position_table, deopt_data, reloc_info,
data_container, stub_key, is_turbofanned, stack_slots,
safepoint_table_offset, handler_table_offset);
// Flush the instruction cache before changing the permissions.
// Note: we do this before setting permissions to ReadExecute because on
// some older ARM kernels there is a bug which causes an access error on
// cache flush instructions to trigger access error on non-writable memory.
// See https://bugs.chromium.org/p/v8/issues/detail?id=8157
code->FlushICache();
}
// Flush the instruction cache after changing the permissions.
code->FlushICache();
return code;
}
@ -2775,9 +2780,14 @@ Handle<Code> Factory::NewCode(
source_position_table, deopt_data, reloc_info,
data_container, stub_key, is_turbofanned, stack_slots,
safepoint_table_offset, handler_table_offset);
// Flush the instruction cache before changing the permissions.
// Note: we do this before setting permissions to ReadExecute because on
// some older ARM kernels there is a bug which causes an access error on
// cache flush instructions to trigger access error on non-writable memory.
// See https://bugs.chromium.org/p/v8/issues/detail?id=8157
code->FlushICache();
}
// Flush the instruction cache after changing the permissions.
code->FlushICache();
return code;
}

View File

@ -324,7 +324,7 @@ class Code : public HeapObjectPtr {
const CodeDesc& desc);
// Flushes the instruction cache for the executable instructions of this code
// object.
// object. Make sure to call this while the code is still writable.
void FlushICache() const;
// Returns the object size for a given body (used for allocation).

View File

@ -116,13 +116,17 @@ TEST(TestFlushICacheOfWritable) {
}
}
#if V8_TARGET_ARCH_ARM64
// Note that this order of operations is not supported on ARM64 because on
// some older Arm64 kernels there is a bug which causes an access error on
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64
// Note that this order of operations is not supported on ARM32/64 because on
// some older ARM32/64 kernels there is a bug which causes an access error on
// cache flush instructions to trigger access error on non-writable memory.
// See https://bugs.chromium.org/p/v8/issues/detail?id=8157
//
// Also note that this requires {kBufferSize == 8 * KB} to reproduce.
//
// The order of operations in V8 is akin to {TestFlushICacheOfWritable} above.
// It is hence OK to disable the below test on some architectures. Only the
// above test case should remain enabled on all architectures.
#define CONDITIONAL_TEST DISABLED_TEST
#else
#define CONDITIONAL_TEST TEST

View File

@ -27,7 +27,7 @@ static inline void MakeAssemblerBufferExecutable(uint8_t* buffer,
size_t allocated) {
// Flush the instruction cache as part of making the buffer executable.
// Note: we do this before setting permissions to ReadExecute because on
// some older Arm64 kernels there is a bug which causes an access error on
// some older ARM kernels there is a bug which causes an access error on
// cache flush instructions to trigger access error on non-writable memory.
// See https://bugs.chromium.org/p/v8/issues/detail?id=8157
Assembler::FlushICache(buffer, allocated);