Skip to content

Commit 28d7ca4

Browse files
joyeecheungbmeck
authored andcommitted
src: make sure that memcpy-ed structs in snapshot have no padding
To make the snapshots reproducible, this patch updates the size of a few types and adds some static assertions to ensure that there are no padding in the memcpy-ed structs. PR-URL: nodejs#50983 Refs: nodejs/build#3043 Reviewed-By: Daniel Lemire <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 35b14ab commit 28d7ca4

9 files changed

+71
-14
lines changed

src/aliased_buffer-inl.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace node {
1010

11-
typedef size_t AliasedBufferIndex;
11+
typedef uint64_t AliasedBufferIndex;
1212

1313
template <typename NativeT, typename V8T>
1414
AliasedBufferBase<NativeT, V8T>::AliasedBufferBase(

src/aliased_buffer.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace node {
1111

12-
typedef size_t AliasedBufferIndex;
12+
typedef uint64_t AliasedBufferIndex;
1313

1414
/**
1515
* Do not use this class directly when creating instances of it - use the

src/base_object_types.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,13 @@ namespace node {
4242
SERIALIZABLE_NON_BINDING_TYPES(V)
4343

4444
#define V(TypeId, NativeType) k_##TypeId,
45-
enum class BindingDataType : uint8_t { BINDING_TYPES(V) kBindingDataTypeCount };
45+
// To avoid padding, the enums are uint64_t.
46+
enum class BindingDataType : uint64_t {
47+
BINDING_TYPES(V) kBindingDataTypeCount
48+
};
4649
// Make sure that we put the bindings first so that we can also use the enums
4750
// for the bindings as index to the binding data store.
48-
enum class EmbedderObjectType : uint8_t {
51+
enum class EmbedderObjectType : uint64_t {
4952
BINDING_TYPES(V) SERIALIZABLE_NON_BINDING_TYPES(V)
5053
// We do not need to know about all the unserializable non-binding types for
5154
// now so we do not list them.

src/encoding_binding.h

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ class BindingData : public SnapshotableObject {
1818
AliasedBufferIndex encode_into_results_buffer;
1919
};
2020

21+
// Make sure that there's no padding in the struct since we will memcpy
22+
// them into the snapshot blob and they need to be reproducible.
23+
static_assert(sizeof(InternalFieldInfo) ==
24+
sizeof(InternalFieldInfoBase) + sizeof(AliasedBufferIndex),
25+
"InternalFieldInfo should have no padding");
26+
2127
BindingData(Realm* realm,
2228
v8::Local<v8::Object> obj,
2329
InternalFieldInfo* info = nullptr);

src/node_file.h

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ class BindingData : public SnapshotableObject {
6464
AliasedBufferIndex statfs_field_bigint_array;
6565
};
6666

67+
// Make sure that there's no padding in the struct since we will memcpy
68+
// them into the snapshot blob and they need to be reproducible.
69+
static_assert(sizeof(InternalFieldInfo) == sizeof(InternalFieldInfoBase) +
70+
sizeof(AliasedBufferIndex) * 4,
71+
"InternalFieldInfo should have no padding");
72+
6773
enum class FilePathIsFileReturnType {
6874
kIsFile = 0,
6975
kIsNotFile,

src/node_process.h

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ class BindingData : public SnapshotableObject {
5454
AliasedBufferIndex hrtime_buffer;
5555
};
5656

57+
// Make sure that there's no padding in the struct since we will memcpy
58+
// them into the snapshot blob and they need to be reproducible.
59+
static_assert(sizeof(InternalFieldInfo) ==
60+
sizeof(InternalFieldInfoBase) + sizeof(AliasedBufferIndex),
61+
"InternalFieldInfo should have no padding");
62+
5763
static void AddMethods(v8::Isolate* isolate,
5864
v8::Local<v8::ObjectTemplate> target);
5965
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);

src/node_snapshotable.cc

+6-6
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,9 @@ size_t SnapshotSerializer::Write(const PropInfo& data) {
282282
}
283283

284284
// Layout of AsyncHooks::SerializeInfo
285-
// [ 4/8 bytes ] snapshot index of async_ids_stack
286-
// [ 4/8 bytes ] snapshot index of fields
287-
// [ 4/8 bytes ] snapshot index of async_id_fields
285+
// [ 8 bytes ] snapshot index of async_ids_stack
286+
// [ 8 bytes ] snapshot index of fields
287+
// [ 8 bytes ] snapshot index of async_id_fields
288288
// [ 4/8 bytes ] snapshot index of js_execution_async_resources
289289
// [ 4/8 bytes ] length of native_execution_async_resources
290290
// [ ... ] snapshot indices of each element in
@@ -387,9 +387,9 @@ size_t SnapshotSerializer::Write(const ImmediateInfo::SerializeInfo& data) {
387387
}
388388

389389
// Layout of PerformanceState::SerializeInfo
390-
// [ 4/8 bytes ] snapshot index of root
391-
// [ 4/8 bytes ] snapshot index of milestones
392-
// [ 4/8 bytes ] snapshot index of observers
390+
// [ 8 bytes ] snapshot index of root
391+
// [ 8 bytes ] snapshot index of milestones
392+
// [ 8 bytes ] snapshot index of observers
393393
template <>
394394
performance::PerformanceState::SerializeInfo SnapshotDeserializer::Read() {
395395
Debug("Read<PerformanceState::SerializeInfo>()\n");

src/node_snapshotable.h

+33-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
66

7+
#include <cassert> // For static_assert
8+
#include <cstddef> // For offsetof
79
#include "aliased_buffer.h"
810
#include "base_object.h"
911
#include "util.h"
@@ -33,13 +35,13 @@ bool WithoutCodeCache(const SnapshotConfig& config);
3335
// and pass it into the V8 callback as the payload of StartupData.
3436
// The memory chunk looks like this:
3537
//
36-
// [ type ] - EmbedderObjectType (a uint8_t)
37-
// [ length ] - a size_t
38+
// [ type ] - EmbedderObjectType (a uint64_t)
39+
// [ length ] - a uint64_t
3840
// [ ... ] - custom bytes of size |length - header size|
3941
struct InternalFieldInfoBase {
4042
public:
4143
EmbedderObjectType type;
42-
size_t length;
44+
uint64_t length;
4345

4446
template <typename T>
4547
static T* New(EmbedderObjectType type) {
@@ -71,14 +73,35 @@ struct InternalFieldInfoBase {
7173
InternalFieldInfoBase() = default;
7274
};
7375

76+
// Make sure that there's no padding in the struct since we will memcpy
77+
// them into the snapshot blob and they need to be reproducible.
78+
static_assert(offsetof(InternalFieldInfoBase, type) == 0,
79+
"InternalFieldInfoBase::type should start from offset 0");
80+
static_assert(offsetof(InternalFieldInfoBase, length) ==
81+
sizeof(EmbedderObjectType),
82+
"InternalFieldInfoBase::type should have no padding");
83+
7484
struct EmbedderTypeInfo {
75-
enum class MemoryMode : uint8_t { kBaseObject, kCppGC };
85+
// To avoid padding, the enum is uint64_t.
86+
enum class MemoryMode : uint64_t { kBaseObject = 0, kCppGC };
7687
EmbedderTypeInfo(EmbedderObjectType t, MemoryMode m) : type(t), mode(m) {}
7788
EmbedderTypeInfo() = default;
89+
7890
EmbedderObjectType type;
7991
MemoryMode mode;
8092
};
8193

94+
// Make sure that there's no padding in the struct since we will memcpy
95+
// them into the snapshot blob and they need to be reproducible.
96+
static_assert(offsetof(EmbedderTypeInfo, type) == 0,
97+
"EmbedderTypeInfo::type should start from offset 0");
98+
static_assert(offsetof(EmbedderTypeInfo, mode) == sizeof(EmbedderObjectType),
99+
"EmbedderTypeInfo::type should have no padding");
100+
static_assert(sizeof(EmbedderTypeInfo) ==
101+
sizeof(EmbedderObjectType) +
102+
sizeof(EmbedderTypeInfo::MemoryMode),
103+
"EmbedderTypeInfo::mode should have no padding");
104+
82105
// An interface for snapshotable native objects to inherit from.
83106
// Use the SERIALIZABLE_OBJECT_METHODS() macro in the class to define
84107
// the following methods to implement:
@@ -150,6 +173,12 @@ class BindingData : public SnapshotableObject {
150173
AliasedBufferIndex is_building_snapshot_buffer;
151174
};
152175

176+
// Make sure that there's no padding in the struct since we will memcpy
177+
// them into the snapshot blob and they need to be reproducible.
178+
static_assert(sizeof(InternalFieldInfo) ==
179+
sizeof(InternalFieldInfoBase) + sizeof(AliasedBufferIndex),
180+
"InternalFieldInfo should have no padding");
181+
153182
BindingData(Realm* realm,
154183
v8::Local<v8::Object> obj,
155184
InternalFieldInfo* info = nullptr);

src/node_v8.h

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ class BindingData : public SnapshotableObject {
2323
AliasedBufferIndex heap_space_statistics_buffer;
2424
AliasedBufferIndex heap_code_statistics_buffer;
2525
};
26+
27+
// Make sure that there's no padding in the struct since we will memcpy
28+
// them into the snapshot blob and they need to be reproducible.
29+
static_assert(sizeof(InternalFieldInfo) == sizeof(InternalFieldInfoBase) +
30+
sizeof(AliasedBufferIndex) * 3,
31+
"InternalFieldInfo should have no padding");
32+
2633
BindingData(Realm* realm,
2734
v8::Local<v8::Object> obj,
2835
InternalFieldInfo* info = nullptr);

0 commit comments

Comments
 (0)