Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f999e15

Browse files
mralephCommit Bot
authored andcommitted
[vm] Avoid UB in CompressedStackMap payloads
Having uint32_t field in UntaggedCompressedStackMaps::Payload implies 4 byte alignment of the whole structure and the field itself. We would prefer, however, to avoid this requirement because we are packing these structures tight in AOT snapshots without any padding in between. This change rewrites the implementation to use `memcpy(...)` to remove any alignment requirements. Note that this `memcpy(...)` will be optimized down to a single memory move on all platforms we currently support as they support necessary unaligned load/store instructions. This also means that the original code worked just fine, except when running under UBSAN. Fixes dart-lang/sdk#47971 TEST=ubsan bots Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-ubsan-linux-release-x64-try,vm-kernel-ubsan-linux-release-x64-try Change-Id: I26a30123fcbc6d2c711f039f15f749913ce4d7bd Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/226100 Reviewed-by: Alexander Aprelev <[email protected]> Commit-Queue: Slava Egorov <[email protected]>
1 parent 26f5c2a commit f999e15

File tree

7 files changed

+59
-39
lines changed

7 files changed

+59
-39
lines changed

runtime/platform/globals.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ static inline void USE(T&&) {}
653653
// gcc enough that it can no longer see that you have cast one pointer
654654
// type to another thus avoiding the warning.
655655
template <class D, class S>
656-
inline D bit_cast(const S& source) {
656+
DART_FORCE_INLINE D bit_cast(const S& source) {
657657
static_assert(sizeof(D) == sizeof(S),
658658
"Source and destination must have the same size");
659659

@@ -669,7 +669,7 @@ inline D bit_cast(const S& source) {
669669
// obscure details in the C++ standard that make reinterpret_cast
670670
// virtually useless.
671671
template <class D, class S>
672-
inline D bit_copy(const S& source) {
672+
DART_FORCE_INLINE D bit_copy(const S& source) {
673673
D destination;
674674
// This use of memcpy is safe: source and destination cannot overlap.
675675
memcpy(&destination, reinterpret_cast<const void*>(&source),

runtime/vm/app_snapshot.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2820,7 +2820,7 @@ class CompressedStackMapsSerializationCluster : public SerializationCluster {
28202820
s->AssignRef(map);
28212821
AutoTraceObject(map);
28222822
const intptr_t length = UntaggedCompressedStackMaps::SizeField::decode(
2823-
map->untag()->payload()->flags_and_size);
2823+
map->untag()->payload()->flags_and_size());
28242824
s->WriteUnsigned(length);
28252825
target_memory_size_ +=
28262826
compiler::target::CompressedStackMaps::InstanceSize(length);
@@ -2832,9 +2832,9 @@ class CompressedStackMapsSerializationCluster : public SerializationCluster {
28322832
for (intptr_t i = 0; i < count; i++) {
28332833
CompressedStackMapsPtr map = objects_[i];
28342834
AutoTraceObject(map);
2835-
s->WriteUnsigned(map->untag()->payload()->flags_and_size);
2835+
s->WriteUnsigned(map->untag()->payload()->flags_and_size());
28362836
const intptr_t length = UntaggedCompressedStackMaps::SizeField::decode(
2837-
map->untag()->payload()->flags_and_size);
2837+
map->untag()->payload()->flags_and_size());
28382838
uint8_t* cdata =
28392839
reinterpret_cast<uint8_t*>(map->untag()->payload()->data());
28402840
s->WriteBytes(cdata, length);
@@ -2874,7 +2874,7 @@ class CompressedStackMapsDeserializationCluster
28742874
static_cast<CompressedStackMapsPtr>(d->Ref(id));
28752875
Deserializer::InitializeHeader(map, kCompressedStackMapsCid,
28762876
CompressedStackMaps::InstanceSize(length));
2877-
map->untag()->payload()->flags_and_size = flags_and_size;
2877+
map->untag()->payload()->set_flags_and_size(flags_and_size);
28782878
uint8_t* cdata =
28792879
reinterpret_cast<uint8_t*>(map->untag()->payload()->data());
28802880
d->ReadBytes(cdata, length);
@@ -6978,7 +6978,7 @@ void Serializer::PrepareInstructions(
69786978

69796979
// Now write collected stack maps after the binary search table.
69806980
auto write_stack_map = [&](CompressedStackMapsPtr smap) {
6981-
const auto flags_and_size = smap->untag()->payload()->flags_and_size;
6981+
const auto flags_and_size = smap->untag()->payload()->flags_and_size();
69826982
const auto payload_size =
69836983
UntaggedCompressedStackMaps::SizeField::decode(flags_and_size);
69846984
pc_mapping.WriteFixed<uint32_t>(flags_and_size);

runtime/vm/compiler/runtime_offsets_list.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@
350350
SIZEOF(CodeSourceMap, HeaderSize, UntaggedCodeSourceMap) \
351351
SIZEOF(CompressedStackMaps, ObjectHeaderSize, UntaggedCompressedStackMaps) \
352352
SIZEOF(CompressedStackMaps, PayloadHeaderSize, \
353-
UntaggedCompressedStackMaps::Payload) \
353+
UntaggedCompressedStackMaps::Payload::FlagsAndSizeHeader) \
354354
SIZEOF(Context, header_size, UntaggedContext) \
355355
SIZEOF(Double, InstanceSize, UntaggedDouble) \
356356
SIZEOF(DynamicLibrary, InstanceSize, UntaggedDynamicLibrary) \

runtime/vm/image_snapshot.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ void ImageWriter::WriteROData(NonStreamingWriteStream* stream, bool vm) {
529529
const CompressedStackMaps& map = CompressedStackMaps::Cast(obj);
530530
const intptr_t payload_size = map.payload_size();
531531
stream->WriteFixed<uint32_t>(
532-
map.ptr()->untag()->payload()->flags_and_size);
532+
map.ptr()->untag()->payload()->flags_and_size());
533533
stream->WriteBytes(map.ptr()->untag()->payload()->data(), payload_size);
534534
} else if (obj.IsCodeSourceMap()) {
535535
const CodeSourceMap& map = CodeSourceMap::Cast(obj);

runtime/vm/object.cc

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,8 +1035,7 @@ void Object::Init(IsolateGroup* isolate_group) {
10351035
CompressedStackMaps::initializeHandle(
10361036
empty_compressed_stackmaps_,
10371037
static_cast<CompressedStackMapsPtr>(address + kHeapObjectTag));
1038-
empty_compressed_stackmaps_->StoreNonPointer(
1039-
&empty_compressed_stackmaps_->untag()->payload()->flags_and_size, 0);
1038+
empty_compressed_stackmaps_->untag()->payload()->set_flags_and_size(0);
10401039
empty_compressed_stackmaps_->SetCanonical();
10411040
}
10421041

@@ -15074,12 +15073,10 @@ CompressedStackMapsPtr CompressedStackMaps::New(const void* payload,
1507415073
Heap::kOld, CompressedStackMaps::ContainsCompressedPointers());
1507515074
NoSafepointScope no_safepoint;
1507615075
result ^= raw;
15077-
result.StoreNonPointer(
15078-
&result.untag()->payload()->flags_and_size,
15076+
result.untag()->payload()->set_flags_and_size(
1507915077
UntaggedCompressedStackMaps::GlobalTableBit::encode(is_global_table) |
15080-
UntaggedCompressedStackMaps::UsesTableBit::encode(
15081-
uses_global_table) |
15082-
UntaggedCompressedStackMaps::SizeField::encode(size));
15078+
UntaggedCompressedStackMaps::UsesTableBit::encode(uses_global_table) |
15079+
UntaggedCompressedStackMaps::SizeField::encode(size));
1508315080
auto cursor =
1508415081
result.UnsafeMutableNonPointer(result.untag()->payload()->data());
1508515082
memcpy(cursor, payload, size); // NOLINT

runtime/vm/object.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5883,16 +5883,16 @@ class CompressedStackMaps : public Object {
58835883
uintptr_t payload_size() const { return PayloadSizeOf(ptr()); }
58845884
static uintptr_t PayloadSizeOf(const CompressedStackMapsPtr raw) {
58855885
return UntaggedCompressedStackMaps::SizeField::decode(
5886-
raw->untag()->payload()->flags_and_size);
5886+
raw->untag()->payload()->flags_and_size());
58875887
}
58885888

58895889
const uint8_t* data() const { return ptr()->untag()->payload()->data(); }
58905890

58915891
// Methods to allow use with PointerKeyValueTrait to create sets of CSMs.
58925892
bool Equals(const CompressedStackMaps& other) const {
58935893
// All of the table flags and payload size must match.
5894-
if (untag()->payload()->flags_and_size !=
5895-
other.untag()->payload()->flags_and_size) {
5894+
if (untag()->payload()->flags_and_size() !=
5895+
other.untag()->payload()->flags_and_size()) {
58965896
return false;
58975897
}
58985898
NoSafepointScope no_safepoint;
@@ -5902,7 +5902,7 @@ class CompressedStackMaps : public Object {
59025902

59035903
static intptr_t HeaderSize() {
59045904
return sizeof(UntaggedCompressedStackMaps) +
5905-
sizeof(UntaggedCompressedStackMaps::Payload);
5905+
sizeof(UntaggedCompressedStackMaps::Payload::FlagsAndSizeHeader);
59065906
}
59075907
static intptr_t UnroundedSize(CompressedStackMapsPtr maps) {
59085908
return UnroundedSize(CompressedStackMaps::PayloadSizeOf(maps));
@@ -5920,13 +5920,13 @@ class CompressedStackMaps : public Object {
59205920
bool UsesGlobalTable() const { return UsesGlobalTable(ptr()); }
59215921
static bool UsesGlobalTable(const CompressedStackMapsPtr raw) {
59225922
return UntaggedCompressedStackMaps::UsesTableBit::decode(
5923-
raw->untag()->payload()->flags_and_size);
5923+
raw->untag()->payload()->flags_and_size());
59245924
}
59255925

59265926
bool IsGlobalTable() const { return IsGlobalTable(ptr()); }
59275927
static bool IsGlobalTable(const CompressedStackMapsPtr raw) {
59285928
return UntaggedCompressedStackMaps::GlobalTableBit::decode(
5929-
raw->untag()->payload()->flags_and_size);
5929+
raw->untag()->payload()->flags_and_size());
59305930
}
59315931

59325932
static CompressedStackMapsPtr NewInlined(const void* payload, intptr_t size) {
@@ -5976,18 +5976,18 @@ class CompressedStackMaps : public Object {
59765976

59775977
uintptr_t payload_size() const {
59785978
return UntaggedCompressedStackMaps::SizeField::decode(
5979-
payload()->flags_and_size);
5979+
payload()->flags_and_size());
59805980
}
59815981
const uint8_t* data() const { return payload()->data(); }
59825982

59835983
bool UsesGlobalTable() const {
59845984
return UntaggedCompressedStackMaps::UsesTableBit::decode(
5985-
payload()->flags_and_size);
5985+
payload()->flags_and_size());
59865986
}
59875987

59885988
bool IsGlobalTable() const {
59895989
return UntaggedCompressedStackMaps::GlobalTableBit::decode(
5990-
payload()->flags_and_size);
5990+
payload()->flags_and_size());
59915991
}
59925992

59935993
private:

runtime/vm/raw_object.h

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,11 +1987,26 @@ class UntaggedCompressedStackMaps : public UntaggedObject {
19871987
VISIT_NOTHING();
19881988

19891989
public:
1990-
struct Payload {
1990+
// Note: AOT snapshots pack these structures without any padding in between
1991+
// so payload structure should not have any alignment requirements.
1992+
// alignas(1) is here to trigger a compiler error if we violate this.
1993+
struct alignas(1) Payload {
1994+
using FlagsAndSizeHeader = uint32_t;
1995+
19911996
// The most significant bits are the length of the encoded payload, in
19921997
// bytes (excluding the header itself). The low bits determine the
19931998
// expected payload contents, as described below.
1994-
uint32_t flags_and_size;
1999+
DART_FORCE_INLINE FlagsAndSizeHeader flags_and_size() const {
2000+
// Note: |this| does not necessarily satisfy alignment requirements
2001+
// of uint32_t so we should use bit_cast.
2002+
return bit_copy<FlagsAndSizeHeader, Payload>(*this);
2003+
}
2004+
2005+
DART_FORCE_INLINE void set_flags_and_size(FlagsAndSizeHeader value) {
2006+
// Note: |this| does not necessarily satisfy alignment requirements
2007+
// of uint32_t hence the byte copy below.
2008+
memcpy(reinterpret_cast<void*>(this), &value, sizeof(value)); // NOLINT
2009+
}
19952010

19962011
// Variable length data follows here. The contents of the payload depend on
19972012
// the type of CompressedStackMaps (CSM) being represented. There are three
@@ -2044,28 +2059,36 @@ class UntaggedCompressedStackMaps : public UntaggedObject {
20442059
// done where the payload is decoded up to the entry whose PC offset
20452060
// is greater or equal to the given PC.
20462061

2047-
uint8_t* data() { OPEN_ARRAY_START(uint8_t, uint8_t); }
2048-
const uint8_t* data() const { OPEN_ARRAY_START(uint8_t, uint8_t); }
2062+
uint8_t* data() {
2063+
return reinterpret_cast<uint8_t*>(this) + sizeof(FlagsAndSizeHeader);
2064+
}
2065+
2066+
const uint8_t* data() const {
2067+
return reinterpret_cast<const uint8_t*>(this) +
2068+
sizeof(FlagsAndSizeHeader);
2069+
}
20492070
};
2050-
static_assert(sizeof(Payload) == sizeof(uint32_t));
20512071

20522072
private:
20532073
// We are using OPEN_ARRAY_START rather than embedding Payload directly into
20542074
// the UntaggedCompressedStackMaps as a field because that would introduce a
20552075
// padding at the end of UntaggedCompressedStackMaps - so we would not be
20562076
// able to use sizeof(UntaggedCompressedStackMaps) as the size of the header
20572077
// anyway.
2058-
Payload* payload() { OPEN_ARRAY_START(Payload, uint32_t); }
2059-
const Payload* payload() const { OPEN_ARRAY_START(Payload, uint32_t); }
2060-
2061-
class GlobalTableBit : public BitField<uint32_t, bool, 0, 1> {};
2062-
class UsesTableBit
2063-
: public BitField<uint32_t, bool, GlobalTableBit::kNextBit, 1> {};
2078+
Payload* payload() { OPEN_ARRAY_START(Payload, uint8_t); }
2079+
const Payload* payload() const { OPEN_ARRAY_START(Payload, uint8_t); }
2080+
2081+
class GlobalTableBit
2082+
: public BitField<Payload::FlagsAndSizeHeader, bool, 0, 1> {};
2083+
class UsesTableBit : public BitField<Payload::FlagsAndSizeHeader,
2084+
bool,
2085+
GlobalTableBit::kNextBit,
2086+
1> {};
20642087
class SizeField
2065-
: public BitField<uint32_t,
2066-
uint32_t,
2088+
: public BitField<Payload::FlagsAndSizeHeader,
2089+
Payload::FlagsAndSizeHeader,
20672090
UsesTableBit::kNextBit,
2068-
sizeof(Payload::flags_and_size) * kBitsPerByte -
2091+
sizeof(Payload::FlagsAndSizeHeader) * kBitsPerByte -
20692092
UsesTableBit::kNextBit> {};
20702093

20712094
friend class Object;

0 commit comments

Comments
 (0)