Skip to content

Commit 9ce6637

Browse files
committed
Clean up a bunch of old encryption cruft
The global shared cache of encrypted file maps was originally required because we actually opened Realm files mulitple times in normal usage, so each of the open files had to know about each other to copy things around. #4839 made it so that in normal usage we only ever have one DB instance per file per process, so it became dead code. Multiprocess encryption made it unneccesary even when the one-DB-per-process rule is violated, as the multiprocess code path covers that. This eliminates our last reliance on file UniqueIDs, so it lets us get rid of hacks related to that. The encryption page reclaimer mostly never actually worked. It used a very conserative page reclaimation rule that meant that pages would never be reclaimed if there was a long-lived Transaction, even if it was frozen or kept refreshed. This is very common in practice, and when it doesn't happen the DB usually isn't kept open either, making it redundant. Encryption used to rely on handling BAD_EXEC signals (or mach exceptions) rather than explicit barriers, so it had to read and write in page-sized chunks. That's no longer the case, so we can eliminate a lot of complexity by always reading and writing in 4k blocks.
1 parent 948949a commit 9ce6637

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1999
-3287
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
### Fixed
99
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
1010
* Fix some client resets (such as migrating to flexible sync) potentially failing with AutoClientResetFailed if a new client reset condition (such as rolling back a flexible sync migration) occurred before the first one completed. ([PR #7542](https://github.com/realm/realm-core/pull/7542), since v13.11.0)
11+
* Encrypted files on Windows had a maximum size of 2GB even on x64 due to internal usage of `off_t`, which is a 32-bit type on 64-bit Windows ([PR #7698](https://github.com/realm/realm-core/pull/7698), since the introduction of encryption support on Windows in v3.0.0).
12+
* The encryption code no longer behaves differently depending on the system page size, which should entirely eliminate a recurring source of bugs related to copying encrypted Realm files between platforms with different page sizes. One known outstanding bug was ([RNET-1141](https://github.com/realm/realm-dotnet/issues/3592)), where opening files on a system with a larger page size than the writing system would attempt to read sections of the file which had never been written to ([PR #7698](https://github.com/realm/realm-core/pull/7698)).
13+
* There were several complicated scenarios which could result in stale reads from encrypted files in multiprocess scenarios. These were very difficult to hit and would typically lead to a crash, either due to an assertion failure or DecryptionFailure being thrown ([PR #7698](https://github.com/realm/realm-core/pull/7698), since v13.9.0).
14+
* Encrypted files have some benign data races where we can memcpy a block of memory while another thread is writing to a limited range of it. It is logically impossible to ever read from that range when this happens, but Thread Sanitizer quite reasonably complains about this. We now perform a slower operations when running with TSan which avoids this benign race ([PR #7698](https://github.com/realm/realm-core/pull/7698)).
1115

1216
### Breaking changes
1317
* Any `stitch_` prefixed fields in the `BsonDocument` returned from `app::User::custom_data()` are being renamed on the server to have a `baas_` prefix instead ([PR #7769](https://github.com/realm/realm-core/pull/7769)).
@@ -21,6 +25,7 @@
2125
* Removed references to `stitch_` fields in access tokens in sync unit tests ([PR #7769](https://github.com/realm/realm-core/pull/7769)).
2226
* Added back iOS simulator testing to evergreen after Jenkins went away ([PR #7758](https://github.com/realm/realm-core/pull/7758)).
2327
* `realm-trawler -c` did not work on Realm using SyncClient history ([PR #7734](https://github.com/realm/realm-core/pull/7734)).
28+
* `File::Map`'s move constructor and assignment operator left `m_fd` unchanged, which appears to have never actually resulted in problems with how it was used ([PR #7698](https://github.com/realm/realm-core/pull/7698)).
2429

2530
----------------------------------------------
2631

src/realm/alloc.cpp

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,7 @@ char* Allocator::translate_less_critical(RefTranslation* ref_translation_ptr, re
119119
RefTranslation& txl = ref_translation_ptr[idx];
120120
size_t offset = ref - get_section_base(idx);
121121
char* addr = txl.mapping_addr + offset;
122-
#if REALM_ENABLE_ENCRYPTION
123-
realm::util::encryption_read_barrier(addr, NodeHeader::header_size, txl.encrypted_mapping, nullptr);
124-
#endif
122+
util::encryption_read_barrier(addr, NodeHeader::header_size, txl.encrypted_mapping);
125123
auto size = NodeHeader::get_byte_size_from_header(addr);
126124
bool crosses_mapping = offset + size > (1 << section_shift);
127125
// Move the limit on use of the existing primary mapping.
@@ -135,27 +133,21 @@ char* Allocator::translate_less_critical(RefTranslation* ref_translation_ptr, re
135133
}
136134
if (REALM_LIKELY(!crosses_mapping)) {
137135
// Array fits inside primary mapping, no new mapping needed.
138-
#if REALM_ENABLE_ENCRYPTION
139-
realm::util::encryption_read_barrier(addr, size, txl.encrypted_mapping, nullptr);
140-
#endif
136+
util::encryption_read_barrier(addr, size, txl.encrypted_mapping);
141137
return addr;
142138
}
143-
else {
144-
// we need a cross-over mapping. If one is already established, use that.
145-
auto xover_mapping_addr = txl.xover_mapping_addr.load(std::memory_order_acquire);
146-
if (!xover_mapping_addr) {
147-
// we need to establish a xover mapping - or wait for another thread to finish
148-
// establishing one:
149-
const_cast<Allocator*>(this)->get_or_add_xover_mapping(txl, idx, offset, size);
150-
// reload (can be relaxed since the call above synchronizes on a mutex)
151-
xover_mapping_addr = txl.xover_mapping_addr.load(std::memory_order_relaxed);
152-
}
153-
// array is now known to be inside the established xover mapping:
154-
addr = xover_mapping_addr + (offset - txl.xover_mapping_base);
155-
#if REALM_ENABLE_ENCRYPTION
156-
realm::util::encryption_read_barrier(addr, size, txl.xover_encrypted_mapping, nullptr);
157-
#endif
158-
return addr;
139+
// we need a cross-over mapping. If one is already established, use that.
140+
auto xover_mapping_addr = txl.xover_mapping_addr.load(std::memory_order_acquire);
141+
if (!xover_mapping_addr) {
142+
// we need to establish a xover mapping - or wait for another thread to finish
143+
// establishing one:
144+
const_cast<Allocator*>(this)->get_or_add_xover_mapping(txl, idx, offset, size);
145+
// reload (can be relaxed since the call above synchronizes on a mutex)
146+
xover_mapping_addr = txl.xover_mapping_addr.load(std::memory_order_relaxed);
159147
}
148+
// array is now known to be inside the established xover mapping:
149+
addr = xover_mapping_addr + (offset - txl.xover_mapping_base);
150+
util::encryption_read_barrier(addr, size, txl.xover_encrypted_mapping);
151+
return addr;
160152
}
161153
} // namespace realm

src/realm/alloc.hpp

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class Allocator {
171171
// into equal chunks.
172172
struct RefTranslation {
173173
char* mapping_addr;
174-
uint64_t cookie;
174+
uint64_t cookie = 0x1234567890;
175175
std::atomic<size_t> lowest_possible_xover_offset = 0;
176176

177177
// member 'xover_mapping_addr' is used for memory synchronization of the fields
@@ -183,14 +183,12 @@ class Allocator {
183183
#if REALM_ENABLE_ENCRYPTION
184184
util::EncryptedFileMapping* encrypted_mapping = nullptr;
185185
util::EncryptedFileMapping* xover_encrypted_mapping = nullptr;
186+
#else
187+
static inline util::EncryptedFileMapping* const encrypted_mapping = nullptr;
188+
static inline util::EncryptedFileMapping* const xover_encrypted_mapping = nullptr;
186189
#endif
187-
explicit RefTranslation(char* addr)
190+
explicit RefTranslation(char* addr = nullptr)
188191
: mapping_addr(addr)
189-
, cookie(0x1234567890)
190-
{
191-
}
192-
RefTranslation()
193-
: RefTranslation(nullptr)
194192
{
195193
}
196194
~RefTranslation()
@@ -222,7 +220,7 @@ class Allocator {
222220
};
223221
// This pointer may be changed concurrently with access, so make sure it is
224222
// atomic!
225-
std::atomic<RefTranslation*> m_ref_translation_ptr;
223+
std::atomic<RefTranslation*> m_ref_translation_ptr{nullptr};
226224

227225
/// The specified size must be divisible by 8, and must not be
228226
/// zero.
@@ -252,7 +250,7 @@ class Allocator {
252250
char* translate_critical(RefTranslation*, ref_type ref) const noexcept;
253251
char* translate_less_critical(RefTranslation*, ref_type ref) const noexcept;
254252
virtual void get_or_add_xover_mapping(RefTranslation&, size_t, size_t, size_t) = 0;
255-
Allocator() noexcept;
253+
Allocator() noexcept = default;
256254
size_t get_section_index(size_t pos) const noexcept;
257255
inline size_t get_section_base(size_t index) const noexcept;
258256

@@ -271,11 +269,9 @@ class Allocator {
271269
// used to detect if the allocator (and owning structure, e.g. Table)
272270
// is recycled. Mismatch on this counter will cause accesors
273271
// lower in the hierarchy to throw if access is attempted.
274-
std::atomic<uint_fast64_t> m_content_versioning_counter;
275-
276-
std::atomic<uint_fast64_t> m_storage_versioning_counter;
277-
278-
std::atomic<uint_fast64_t> m_instance_versioning_counter;
272+
std::atomic<uint_fast64_t> m_content_versioning_counter{0};
273+
std::atomic<uint_fast64_t> m_storage_versioning_counter{0};
274+
std::atomic<uint_fast64_t> m_instance_versioning_counter{0};
279275

280276
inline uint_fast64_t get_storage_version(uint64_t instance_version)
281277
{
@@ -547,14 +543,6 @@ inline bool Allocator::is_read_only(ref_type ref) const noexcept
547543
return ref < m_baseline.load(std::memory_order_relaxed);
548544
}
549545

550-
inline Allocator::Allocator() noexcept
551-
{
552-
m_content_versioning_counter = 0;
553-
m_storage_versioning_counter = 0;
554-
m_instance_versioning_counter = 0;
555-
m_ref_translation_ptr = nullptr;
556-
}
557-
558546
// performance critical part of the translation process. Less critical code is in translate_less_critical.
559547
inline char* Allocator::translate_critical(RefTranslation* ref_translation_ptr, ref_type ref) const noexcept
560548
{
@@ -566,30 +554,23 @@ inline char* Allocator::translate_critical(RefTranslation* ref_translation_ptr,
566554
if (REALM_LIKELY(offset < lowest_possible_xover_offset)) {
567555
// the lowest possible xover offset may grow concurrently, but that will not affect this code path
568556
char* addr = txl.mapping_addr + offset;
569-
#if REALM_ENABLE_ENCRYPTION
570-
realm::util::encryption_read_barrier(addr, NodeHeader::header_size, txl.encrypted_mapping,
571-
NodeHeader::get_byte_size_from_header);
572-
#endif
557+
util::encryption_read_barrier(addr, NodeHeader::header_size, txl.encrypted_mapping);
558+
size_t size = NodeHeader::get_byte_size_from_header(addr);
559+
util::encryption_read_barrier(addr, size, txl.encrypted_mapping);
573560
return addr;
574561
}
575-
else {
576-
// the lowest possible xover offset may grow concurrently, but that will be handled inside the call
577-
return translate_less_critical(ref_translation_ptr, ref);
578-
}
562+
// the lowest possible xover offset may grow concurrently, but that will be handled inside the call
563+
return translate_less_critical(ref_translation_ptr, ref);
579564
}
580565
realm::util::terminate("Invalid ref translation entry", __FILE__, __LINE__, txl.cookie, 0x1234567890, ref, idx);
581-
return nullptr;
582566
}
583567

584568
inline char* Allocator::translate(ref_type ref) const noexcept
585569
{
586-
auto ref_translation_ptr = m_ref_translation_ptr.load(std::memory_order_acquire);
587-
if (REALM_LIKELY(ref_translation_ptr)) {
588-
return translate_critical(ref_translation_ptr, ref);
589-
}
590-
else {
591-
return do_translate(ref);
570+
if (auto ptr = m_ref_translation_ptr.load(std::memory_order_acquire); REALM_LIKELY(ptr)) {
571+
return translate_critical(ptr, ref);
592572
}
573+
return do_translate(ref);
593574
}
594575

595576

0 commit comments

Comments
 (0)