Commit 0188634e authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[ptr-compr][ubsan] Use [Read/Write]UnalignedValue for unaligned fields

When pointer compression is enabled the [u]intptr_t and double fields are
only kTaggedSize aligned so in order to avoid undefined behavior in C++ code
we have to access these values in an unaligned pointer friendly way although
both x64 and arm64 architectures (where pointer compression is supported)
allow unaligned access.

These changes will be removed once v8:8875 is fixed and all the
kSystemPointerSize fields are properly aligned.

Bug: v8:7703
Change-Id: I4df477cbdeab806303bb4f675d52b61c06342c8e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1528996
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60321}
parent da66158f
......@@ -7,6 +7,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <type_traits>
#include "v8-version.h" // NOLINT(build/include)
......@@ -274,6 +275,17 @@ class Internals {
V8_INLINE static T ReadRawField(internal::Address heap_object_ptr,
int offset) {
internal::Address addr = heap_object_ptr + offset - kHeapObjectTag;
#ifdef V8_COMPRESS_POINTERS
if (sizeof(T) > kApiTaggedSize) {
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only
// kTaggedSize aligned so we have to use unaligned pointer friendly way of
// accessing them in order to avoid undefined behavior in C++ code.
T r;
memcpy(&r, reinterpret_cast<void*>(addr), sizeof(T));
return r;
}
#endif
return *reinterpret_cast<const T*>(addr);
}
......
......@@ -118,6 +118,7 @@ class Arguments;
class DeferredHandles;
class Heap;
class HeapObject;
class ExternalString;
class Isolate;
class LocalEmbedderHeapTracer;
class MicrotaskQueue;
......@@ -2797,7 +2798,7 @@ class V8_EXPORT String : public Name {
void operator=(const ExternalStringResourceBase&) = delete;
private:
friend class internal::Heap;
friend class internal::ExternalString;
friend class v8::String;
friend class internal::ScopedExternalStringLock;
};
......
......@@ -300,15 +300,7 @@ void Heap::FinalizeExternalString(String string) {
ExternalBackingStoreType::kExternalString,
ext_string->ExternalPayloadSize());
v8::String::ExternalStringResourceBase** resource_addr =
reinterpret_cast<v8::String::ExternalStringResourceBase**>(
string->address() + ExternalString::kResourceOffset);
// Dispose of the C++ object if it has not already been disposed.
if (*resource_addr != nullptr) {
(*resource_addr)->Dispose();
*resource_addr = nullptr;
}
ext_string->DisposeResource();
}
Address Heap::NewSpaceTop() { return new_space_->top(); }
......
......@@ -206,8 +206,7 @@ class MutableBigInt : public FreshlyAllocatedBigInt {
}
inline void set_digit(int n, digit_t value) {
SLOW_DCHECK(0 <= n && n < length());
Address address = FIELD_ADDR(*this, kDigitsOffset + n * kDigitSize);
(*reinterpret_cast<digit_t*>(address)) = value;
WRITE_UINTPTR_FIELD(*this, kDigitsOffset + n * kDigitSize, value);
}
void set_64_bits(uint64_t bits);
......
......@@ -87,8 +87,7 @@ class BigIntBase : public HeapObject {
inline digit_t digit(int n) const {
SLOW_DCHECK(0 <= n && n < length());
Address address = FIELD_ADDR(*this, kDigitsOffset + n * kDigitSize);
return *reinterpret_cast<digit_t*>(address);
return READ_UINTPTR_FIELD(*this, kDigitsOffset + n * kDigitSize);
}
bool is_zero() const { return length() == 0; }
......
......@@ -11,6 +11,7 @@
#include "src/objects-inl.h"
#include "src/objects/embedder-data-array.h"
#include "src/objects/js-objects-inl.h"
#include "src/v8memory.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
......@@ -71,7 +72,15 @@ bool EmbedderDataSlot::ToAlignedPointer(void** out_pointer) const {
// are accessed this way only from the main thread via API during "mutator"
// phase which is propely synched with GC (concurrent marker may still look
// at the tagged part of the embedder slot but read-only access is ok).
#ifdef V8_COMPRESS_POINTERS
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only kTaggedSize
// aligned so we have to use unaligned pointer friendly way of accessing them
// in order to avoid undefined behavior in C++ code.
Address raw_value = ReadUnalignedValue<Address>(address());
#else
Address raw_value = *location();
#endif
*out_pointer = reinterpret_cast<void*>(raw_value);
return HAS_SMI_TAG(raw_value);
}
......@@ -89,7 +98,15 @@ EmbedderDataSlot::RawData EmbedderDataSlot::load_raw(
// are accessed this way only by serializer from the main thread when
// GC is not active (concurrent marker may still look at the tagged part
// of the embedder slot but read-only access is ok).
#ifdef V8_COMPRESS_POINTERS
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only kTaggedSize
// aligned so we have to use unaligned pointer friendly way of accessing them
// in order to avoid undefined behavior in C++ code.
return ReadUnalignedValue<Address>(address());
#else
return *location();
#endif
}
void EmbedderDataSlot::store_raw(EmbedderDataSlot::RawData data,
......
......@@ -653,7 +653,17 @@ typename Traits::ElementType FixedTypedArray<Traits>::get_scalar_from_data_ptr(
// JavaScript memory model to have tear-free reads of overlapping accesses,
// and using relaxed atomics may introduce overhead.
TSAN_ANNOTATE_IGNORE_READS_BEGIN;
auto result = ptr[index];
ElementType result;
if (COMPRESS_POINTERS_BOOL && alignof(ElementType) > kTaggedSize) {
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only kTaggedSize
// aligned so we have to use unaligned pointer friendly way of accessing
// them in order to avoid undefined behavior in C++ code.
result = ReadUnalignedValue<ElementType>(reinterpret_cast<Address>(ptr) +
index * sizeof(ElementType));
} else {
result = ptr[index];
}
TSAN_ANNOTATE_IGNORE_READS_END;
return result;
}
......@@ -664,7 +674,16 @@ void FixedTypedArray<Traits>::set(int index, ElementType value) {
// See the comment in FixedTypedArray<Traits>::get_scalar.
auto* ptr = reinterpret_cast<ElementType*>(DataPtr());
TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
ptr[index] = value;
if (COMPRESS_POINTERS_BOOL && alignof(ElementType) > kTaggedSize) {
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size
// fields (external pointers, doubles and BigInt data) are only kTaggedSize
// aligned so we have to use unaligned pointer friendly way of accessing
// them in order to avoid undefined behavior in C++ code.
WriteUnalignedValue<ElementType>(
reinterpret_cast<Address>(ptr) + index * sizeof(ElementType), value);
} else {
ptr[index] = value;
}
TSAN_ANNOTATE_IGNORE_WRITES_END;
}
......
......@@ -15,6 +15,7 @@
#undef DECL_INT_ACCESSORS
#undef DECL_INT32_ACCESSORS
#undef DECL_UINT16_ACCESSORS
#undef DECL_INT16_ACCESSORS
#undef DECL_UINT8_ACCESSORS
#undef DECL_ACCESSORS
#undef DECL_CAST
......@@ -42,6 +43,7 @@
#undef BIT_FIELD_ACCESSORS
#undef INSTANCE_TYPE_CHECKER
#undef TYPE_CHECKER
#undef RELAXED_INT16_ACCESSORS
#undef FIELD_ADDR
#undef READ_FIELD
#undef READ_WEAK_FIELD
......@@ -52,6 +54,7 @@
#undef WRITE_WEAK_FIELD
#undef RELEASE_WRITE_FIELD
#undef RELAXED_WRITE_FIELD
#undef RELAXED_WRITE_WEAK_FIELD
#undef WRITE_BARRIER
#undef WEAK_WRITE_BARRIER
#undef CONDITIONAL_WRITE_BARRIER
......@@ -60,14 +63,7 @@
#undef WRITE_DOUBLE_FIELD
#undef READ_INT_FIELD
#undef WRITE_INT_FIELD
#undef ACQUIRE_READ_INTPTR_FIELD
#undef RELAXED_READ_INTPTR_FIELD
#undef READ_INTPTR_FIELD
#undef RELEASE_WRITE_INTPTR_FIELD
#undef RELAXED_WRITE_INTPTR_FIELD
#undef WRITE_INTPTR_FIELD
#undef READ_UINTPTR_FIELD
#undef WRITE_UINTPTR_FIELD
#undef ACQUIRE_READ_INT32_FIELD
#undef READ_UINT8_FIELD
#undef WRITE_UINT8_FIELD
#undef RELAXED_WRITE_INT8_FIELD
......@@ -78,18 +74,25 @@
#undef WRITE_UINT16_FIELD
#undef READ_INT16_FIELD
#undef WRITE_INT16_FIELD
#undef RELAXED_READ_INT16_FIELD
#undef RELAXED_WRITE_INT16_FIELD
#undef READ_UINT32_FIELD
#undef RELAXED_READ_UINT32_FIELD
#undef WRITE_UINT32_FIELD
#undef RELAXED_WRITE_UINT32_FIELD
#undef READ_INT32_FIELD
#undef RELAXED_READ_INT32_FIELD
#undef WRITE_INT32_FIELD
#undef RELEASE_WRITE_INT32_FIELD
#undef RELAXED_WRITE_INT32_FIELD
#undef READ_FLOAT_FIELD
#undef WRITE_FLOAT_FIELD
#undef READ_INTPTR_FIELD
#undef WRITE_INTPTR_FIELD
#undef READ_UINTPTR_FIELD
#undef WRITE_UINTPTR_FIELD
#undef READ_UINT64_FIELD
#undef WRITE_UINT64_FIELD
#undef READ_INT64_FIELD
#undef WRITE_INT64_FIELD
#undef READ_BYTE_FIELD
#undef RELAXED_READ_BYTE_FIELD
#undef WRITE_BYTE_FIELD
......
......@@ -323,40 +323,10 @@
#define WRITE_INT_FIELD(p, offset, value) \
(*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value)
#define ACQUIRE_READ_INTPTR_FIELD(p, offset) \
static_cast<intptr_t>(base::Acquire_Load( \
reinterpret_cast<const base::AtomicWord*>(FIELD_ADDR(p, offset))))
#define ACQUIRE_READ_INT32_FIELD(p, offset) \
static_cast<int32_t>(base::Acquire_Load( \
reinterpret_cast<const base::Atomic32*>(FIELD_ADDR(p, offset))))
#define RELAXED_READ_INTPTR_FIELD(p, offset) \
static_cast<intptr_t>(base::Relaxed_Load( \
reinterpret_cast<const base::AtomicWord*>(FIELD_ADDR(p, offset))))
#define READ_INTPTR_FIELD(p, offset) \
(*reinterpret_cast<const intptr_t*>(FIELD_ADDR(p, offset)))
#define RELEASE_WRITE_INTPTR_FIELD(p, offset, value) \
base::Release_Store( \
reinterpret_cast<base::AtomicWord*>(FIELD_ADDR(p, offset)), \
static_cast<base::AtomicWord>(value));
#define RELAXED_WRITE_INTPTR_FIELD(p, offset, value) \
base::Relaxed_Store( \
reinterpret_cast<base::AtomicWord*>(FIELD_ADDR(p, offset)), \
static_cast<base::AtomicWord>(value));
#define WRITE_INTPTR_FIELD(p, offset, value) \
(*reinterpret_cast<intptr_t*>(FIELD_ADDR(p, offset)) = value)
#define READ_UINTPTR_FIELD(p, offset) \
(*reinterpret_cast<const uintptr_t*>(FIELD_ADDR(p, offset)))
#define WRITE_UINTPTR_FIELD(p, offset, value) \
(*reinterpret_cast<uintptr_t*>(FIELD_ADDR(p, offset)) = value)
#define READ_UINT8_FIELD(p, offset) \
(*reinterpret_cast<const uint8_t*>(FIELD_ADDR(p, offset)))
......@@ -439,17 +409,51 @@
#define WRITE_FLOAT_FIELD(p, offset, value) \
(*reinterpret_cast<float*>(FIELD_ADDR(p, offset)) = value)
// TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size fields
// (external pointers, doubles and BigInt data) are only kTaggedSize aligned so
// we have to use unaligned pointer friendly way of accessing them in order to
// avoid undefined behavior in C++ code.
#ifdef V8_COMPRESS_POINTERS
#define READ_INTPTR_FIELD(p, offset) \
ReadUnalignedValue<intptr_t>(FIELD_ADDR(p, offset))
#define WRITE_INTPTR_FIELD(p, offset, value) \
WriteUnalignedValue<intptr_t>(FIELD_ADDR(p, offset), value)
#define READ_UINTPTR_FIELD(p, offset) \
ReadUnalignedValue<uintptr_t>(FIELD_ADDR(p, offset))
#define WRITE_UINTPTR_FIELD(p, offset, value) \
WriteUnalignedValue<uintptr_t>(FIELD_ADDR(p, offset), value)
#define READ_UINT64_FIELD(p, offset) \
ReadUnalignedValue<uint64_t>(FIELD_ADDR(p, offset))
#define WRITE_UINT64_FIELD(p, offset, value) \
WriteUnalignedValue<uint64_t>(FIELD_ADDR(p, offset), value)
#else // V8_COMPRESS_POINTERS
#define READ_INTPTR_FIELD(p, offset) \
(*reinterpret_cast<const intptr_t*>(FIELD_ADDR(p, offset)))
#define WRITE_INTPTR_FIELD(p, offset, value) \
(*reinterpret_cast<intptr_t*>(FIELD_ADDR(p, offset)) = value)
#define READ_UINTPTR_FIELD(p, offset) \
(*reinterpret_cast<const uintptr_t*>(FIELD_ADDR(p, offset)))
#define WRITE_UINTPTR_FIELD(p, offset, value) \
(*reinterpret_cast<uintptr_t*>(FIELD_ADDR(p, offset)) = value)
#define READ_UINT64_FIELD(p, offset) \
(*reinterpret_cast<const uint64_t*>(FIELD_ADDR(p, offset)))
#define WRITE_UINT64_FIELD(p, offset, value) \
(*reinterpret_cast<uint64_t*>(FIELD_ADDR(p, offset)) = value)
#define READ_INT64_FIELD(p, offset) \
(*reinterpret_cast<const int64_t*>(FIELD_ADDR(p, offset)))
#define WRITE_INT64_FIELD(p, offset, value) \
(*reinterpret_cast<int64_t*>(FIELD_ADDR(p, offset)) = value)
#endif // V8_COMPRESS_POINTERS
#define READ_BYTE_FIELD(p, offset) \
(*reinterpret_cast<const byte*>(FIELD_ADDR(p, offset)))
......
......@@ -553,11 +553,11 @@ bool ExternalString::is_uncached() const {
}
Address ExternalString::resource_as_address() {
return *reinterpret_cast<Address*>(FIELD_ADDR(*this, kResourceOffset));
return READ_UINTPTR_FIELD(*this, kResourceOffset);
}
void ExternalString::set_address_as_resource(Address address) {
*reinterpret_cast<Address*>(FIELD_ADDR(*this, kResourceOffset)) = address;
WRITE_UINTPTR_FIELD(*this, kResourceOffset, address);
if (IsExternalOneByteString()) {
ExternalOneByteString::cast(*this)->update_data_cache();
} else {
......@@ -566,27 +566,36 @@ void ExternalString::set_address_as_resource(Address address) {
}
uint32_t ExternalString::resource_as_uint32() {
return static_cast<uint32_t>(
*reinterpret_cast<uintptr_t*>(FIELD_ADDR(*this, kResourceOffset)));
return static_cast<uint32_t>(READ_UINTPTR_FIELD(*this, kResourceOffset));
}
void ExternalString::set_uint32_as_resource(uint32_t value) {
*reinterpret_cast<uintptr_t*>(FIELD_ADDR(*this, kResourceOffset)) = value;
WRITE_UINTPTR_FIELD(*this, kResourceOffset, value);
if (is_uncached()) return;
const char** data_field =
reinterpret_cast<const char**>(FIELD_ADDR(*this, kResourceDataOffset));
*data_field = nullptr;
WRITE_UINTPTR_FIELD(*this, kResourceDataOffset, kNullAddress);
}
void ExternalString::DisposeResource() {
v8::String::ExternalStringResourceBase* resource =
reinterpret_cast<v8::String::ExternalStringResourceBase*>(
READ_UINTPTR_FIELD(*this, ExternalString::kResourceOffset));
// Dispose of the C++ object if it has not already been disposed.
if (resource != nullptr) {
resource->Dispose();
WRITE_UINTPTR_FIELD(*this, ExternalString::kResourceOffset, kNullAddress);
}
}
const ExternalOneByteString::Resource* ExternalOneByteString::resource() {
return *reinterpret_cast<Resource**>(FIELD_ADDR(*this, kResourceOffset));
return reinterpret_cast<Resource*>(
READ_UINTPTR_FIELD(*this, kResourceOffset));
}
void ExternalOneByteString::update_data_cache() {
if (is_uncached()) return;
const char** data_field =
reinterpret_cast<const char**>(FIELD_ADDR(*this, kResourceDataOffset));
*data_field = resource()->data();
WRITE_UINTPTR_FIELD(*this, kResourceDataOffset,
reinterpret_cast<Address>(resource()->data()));
}
void ExternalOneByteString::SetResource(
......@@ -600,8 +609,8 @@ void ExternalOneByteString::SetResource(
void ExternalOneByteString::set_resource(
const ExternalOneByteString::Resource* resource) {
*reinterpret_cast<const Resource**>(FIELD_ADDR(*this, kResourceOffset)) =
resource;
WRITE_UINTPTR_FIELD(*this, kResourceOffset,
reinterpret_cast<Address>(resource));
if (resource != nullptr) update_data_cache();
}
......@@ -615,14 +624,14 @@ uint16_t ExternalOneByteString::ExternalOneByteStringGet(int index) {
}
const ExternalTwoByteString::Resource* ExternalTwoByteString::resource() {
return *reinterpret_cast<Resource**>(FIELD_ADDR(*this, kResourceOffset));
return reinterpret_cast<Resource*>(
READ_UINTPTR_FIELD(*this, kResourceOffset));
}
void ExternalTwoByteString::update_data_cache() {
if (is_uncached()) return;
const uint16_t** data_field = reinterpret_cast<const uint16_t**>(
FIELD_ADDR(*this, kResourceDataOffset));
*data_field = resource()->data();
WRITE_UINTPTR_FIELD(*this, kResourceDataOffset,
reinterpret_cast<Address>(resource()->data()));
}
void ExternalTwoByteString::SetResource(
......@@ -636,8 +645,8 @@ void ExternalTwoByteString::SetResource(
void ExternalTwoByteString::set_resource(
const ExternalTwoByteString::Resource* resource) {
*reinterpret_cast<const Resource**>(FIELD_ADDR(*this, kResourceOffset)) =
resource;
WRITE_UINTPTR_FIELD(*this, kResourceOffset,
reinterpret_cast<Address>(resource));
if (resource != nullptr) update_data_cache();
}
......
......@@ -713,6 +713,9 @@ class ExternalString : public String {
inline uint32_t resource_as_uint32();
inline void set_uint32_as_resource(uint32_t value);
// Disposes string's resource object if it has not already been disposed.
inline void DisposeResource();
STATIC_ASSERT(kResourceOffset == Internals::kStringResourceOffset);
OBJECT_CONSTRUCTORS(ExternalString, String);
......
......@@ -711,8 +711,8 @@ void Serializer::ObjectSerializer::VisitEmbeddedPointer(Code host,
void Serializer::ObjectSerializer::VisitExternalReference(Foreign host,
Address* p) {
Address target = *p;
auto encoded_reference = serializer_->EncodeExternalReference(target);
auto encoded_reference =
serializer_->EncodeExternalReference(host->foreign_address());
if (encoded_reference.is_from_api()) {
sink_->Put(kApiReference, "ApiRef");
} else {
......
......@@ -57,18 +57,30 @@ CAST_ACCESSOR(AsmWasmData)
} \
ACCESSORS(holder, name, type, offset)
#define READ_PRIMITIVE_FIELD(p, type, offset) \
(*reinterpret_cast<type const*>(FIELD_ADDR(p, offset)))
#define WRITE_PRIMITIVE_FIELD(p, type, offset, value) \
(*reinterpret_cast<type*>(FIELD_ADDR(p, offset)) = value)
#define PRIMITIVE_ACCESSORS(holder, name, type, offset) \
type holder::name() const { \
return READ_PRIMITIVE_FIELD(*this, type, offset); \
} \
void holder::set_##name(type value) { \
WRITE_PRIMITIVE_FIELD(*this, type, offset, value); \
#define PRIMITIVE_ACCESSORS(holder, name, type, offset) \
type holder::name() const { \
if (COMPRESS_POINTERS_BOOL && alignof(type) > kTaggedSize) { \
/* TODO(ishell, v8:8875): When pointer compression is enabled 8-byte */ \
/* size fields (external pointers, doubles and BigInt data) are only */ \
/* kTaggedSize aligned so we have to use unaligned pointer friendly */ \
/* way of accessing them in order to avoid undefined behavior in C++ */ \
/* code. */ \
return ReadUnalignedValue<type>(FIELD_ADDR(*this, offset)); \
} else { \
return *reinterpret_cast<type const*>(FIELD_ADDR(*this, offset)); \
} \
} \
void holder::set_##name(type value) { \
if (COMPRESS_POINTERS_BOOL && alignof(type) > kTaggedSize) { \
/* TODO(ishell, v8:8875): When pointer compression is enabled 8-byte */ \
/* size fields (external pointers, doubles and BigInt data) are only */ \
/* kTaggedSize aligned so we have to use unaligned pointer friendly */ \
/* way of accessing them in order to avoid undefined behavior in C++ */ \
/* code. */ \
WriteUnalignedValue<type>(FIELD_ADDR(*this, offset), value); \
} else { \
*reinterpret_cast<type*>(FIELD_ADDR(*this, offset)) = value; \
} \
}
// WasmModuleObject
......
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