• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / jorgen.edelbo_402

21 Aug 2024 11:10AM CUT coverage: 91.054% (-0.03%) from 91.085%
jorgen.edelbo_402

Pull #7803

Evergreen

jedelbo
Small fix to Table::typed_write

When writing the realm to a new file from a write transaction,
the Table may be COW so that the top ref is changed. So don't
use the ref that is present in the group when the operation starts.
Pull Request #7803: Feature/string compression

103494 of 181580 branches covered (57.0%)

1929 of 1999 new or added lines in 46 files covered. (96.5%)

695 existing lines in 51 files now uncovered.

220142 of 241772 relevant lines covered (91.05%)

7344461.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.99
/src/realm/util/encrypted_file_mapping.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include <realm/util/encrypted_file_mapping.hpp>
20

21
#include <realm/util/backtrace.hpp>
22
#include <realm/util/file_mapper.hpp>
23

24
#include <sstream>
25

26
#if REALM_ENABLE_ENCRYPTION
27
#include <realm/util/aes_cryptor.hpp>
28
#include <realm/util/errno.hpp>
29
#include <realm/util/sha_crypto.hpp>
30
#include <realm/util/terminate.hpp>
31
#include <realm/utilities.hpp>
32

33
#include <algorithm>
34
#include <array>
35
#include <chrono>
36
#include <cstdlib>
37
#include <cstring>
38
#include <iostream>
39
#include <string_view>
40
#include <thread>
41

42
#ifdef REALM_DEBUG
43
#include <cstdio>
44
#endif
45

46
#if defined(_WIN32)
47
#include <Windows.h>
48
#include <bcrypt.h>
49
#pragma comment(lib, "bcrypt.lib")
50
#else
51
#include <sys/mman.h>
52
#include <unistd.h>
53
#endif
54

55
namespace realm::util {
56
// When Realm's file encryption was originally designed, we had the constraint
57
// that all encryption and decryption had to happen in aligned system page size
58
// sized blocks due to the use of signal handlers to lazily decrypt data and
59
// track where writes occurrs. This is no longer the case, but may still help
60
// explain why the file layout looks the way it does.
61
//
62
// Encryption is performed on 4096 byte data pages. Each group of 64 data pages
63
// is arranged into a "block", which has a 4096 byte header containing the IVs
64
// and HMACs for the following pages. Each page has *two* IVs and HMACs stored.
65
// iv2/hmac2 contain the values which were last used to successfully decrypt
66
// the page, while iv1/hmac1 is the values which were used to last encrypt the
67
// page.
68
//
69
// Writing new encrypted data has the following steps:
70
//
71
// 1. Copy iv1/hmac1 to iv2/hmac2 in the IVTable
72
// 2. Increment iv1
73
// 3. Encrypt the page in memory
74
// 4. Compute the hmac for the new encrypted data.
75
// 5. If the hmac matches the previous hmac, goto 2 (this will not ever actually happen)
76
// 6. Write the new IVTable for the page.
77
// 7. fsync() (or F_BARRIERFSYNC on Apple)
78
// 8. Write the new encrypted data
79
//
80
// If we are interrupted before #6, no i/o has happened and the data on disk is
81
// fine. If we are interrupted between #6 and #8, then when we next try to read
82
// the page the hmac check using hmac1 will fail, but the check using hmac2
83
// will succeed and we will be able to read the old data. We then copy
84
// iv2/hmac2 back to the active fields and continue as normal.
85
//
86
// This scheme breaks if we have a partial write of the 4k page. This is
87
// impossible with SSDs, which can only write in their atomic block size, and
88
// it would be extremely unusual for that to be smaller than 4k. It may be a
89
// problem when running on HDDs, though.
90
//
91
// Reading from an encrypted file is done by creating a mapping and then
92
// calling `read_barrier(addr, size)` to mark the section of the mapping which
93
// needs to be populated. This decrypts each of the pages which cover that
94
// range and places the plaintext into memory. If any of the pages were already
95
// decrypted, this is a no-op that skips reading anything and just assumes that
96
// the data was up-to-date.
97
//
98
// Writing is done with `read_barrier(addr, size, true)` before performing any
99
// writes to mark the range as writeable, and then `write_barrier(addr, size)`
100
// to mark bytes which were actually written to. `write_barrier()` eagerly
101
// copies all of the written bytes to any other active mappings on the same
102
// file which have those pages decrypted in memory. This is spooky
103
// threading-wise, and is only made safe by Realm's MVCC semantics - if we're
104
// writing to a section of the file we know that no one can be legally reading
105
// those exact bytes, and we must be writing to different bytes in the same
106
// page. This copying makes it so that we never have to recheck the disk; once
107
// we have read and decrypted a page for a mapping, that page is forevermore
108
// valid and up-to-date.
109
//
110
// All dirty data is kept buffered in memory until `flush()` is called.
111
//
112
// In multi-process scenarios (or just multiple File instances for a single
113
// file in a single process, which doesn't happen when using the public API
114
// normally), eagerly keeping decrypted pages up to date is impossible, and we
115
// sometimes need to recheck the disk. Here we once again take advantage of
116
// Realm being MVCC with discrete points where we may need to see newer
117
// versions of the data on disk. When the reader view is updated, if there have
118
// been any external writes to the file SlabAlloc calls
119
// `mark_pages_for_iv_check()`, which puts all up-to-date pages into a
120
// potentially-stale state. The next time each page is accessed, we reread the
121
// IVTable for that page. If it's the same as the IVTable for the plaintext we
122
// have in memory then the page is marked as being up-to-date, and if it's
123
// different we reread the page.
124
//
125
// Another source of complexity in multiprocess scenarios is that while we
126
// assume that the actual i/o is atomic in 4k chunks, writing to the in-memory
127
// buffers is distinctly not atomic. One process reading from a memory mapping
128
// while another process is writing to that position in the file can see
129
// incomplete writes. Rather than doing page-level locking, we assume that this
130
// will be very rare and perform optimistic unlocked reads. If decryption fails
131
// and we are in a potentially-multiprocess scenario we retry the read several
132
// times before reporting an error.
133

134
struct IVTable {
135
    uint32_t iv1 = 0;
136
    std::array<uint8_t, 28> hmac1 = {};
137
    uint32_t iv2 = 0;
138
    std::array<uint8_t, 28> hmac2 = {};
139
    bool operator==(const IVTable& other) const
140
    {
144,621✔
141
        return iv1 == other.iv1 && iv2 == other.iv2 && hmac1 == other.hmac1 && hmac2 == other.hmac2;
144,621✔
142
    }
144,621✔
143
    bool operator!=(const IVTable& other) const
144
    {
144,621✔
145
        return !(*this == other);
144,621✔
146
    }
144,621✔
147
};
148
// We read this via memcpy and need it to be packed
149
static_assert(sizeof(IVTable) == 64);
150

151
namespace {
152
constexpr uint8_t aes_block_size = 16;
153
constexpr uint16_t encryption_page_size = 4096;
154
constexpr uint8_t metadata_size = sizeof(IVTable);
155
constexpr uint8_t pages_per_block = encryption_page_size / metadata_size;
156
static_assert(metadata_size == 64,
157
              "changing the size of the metadata breaks compatibility with existing Realm files");
158

159
using SizeType = File::SizeType;
160

161
template <typename To, typename From>
162
To checked_cast(From from)
163
{
164
    To to;
165
    if (REALM_UNLIKELY(int_cast_with_overflow_detect(from, to))) {
166
        throw MaximumFileSizeExceeded(util::format("File size %1 is larger than can be represented", from));
167
    }
168
    return to;
169
}
170

171
// Overflows when converting from file positions (always 64-bits) to size_t
172
// (sometimes 32-bits) should all be caught by set_file_size()
173
template <typename To, typename From>
174
constexpr To assert_cast(From from)
175
{
2,602,065✔
176
    REALM_ASSERT_DEBUG(!int_cast_has_overflow<To>(from));
2,602,065✔
177
    return static_cast<To>(from);
2,602,065✔
178
}
2,602,065✔
179

180
// Index of page which contains `data_pos`
181
constexpr size_t page_index(SizeType data_pos) noexcept
182
{
2,565,348✔
183
    SizeType index = data_pos / encryption_page_size;
2,565,348✔
184
    return assert_cast<size_t>(index);
2,565,348✔
185
}
2,565,348✔
186

187
// Number of pages required to store `size` bytes
188
constexpr size_t page_count(SizeType size) noexcept
189
{
36,717✔
190
    return assert_cast<size_t>((size + encryption_page_size - 1) / encryption_page_size);
36,717✔
191
}
36,717✔
192

193
// Index of the metadata block which contains `data_pos`
194
constexpr size_t block_index(SizeType data_pos) noexcept
195
{
1,663,068✔
196
    return page_index(data_pos) / pages_per_block;
1,663,068✔
197
}
1,663,068✔
198

199
// Number of metadata blocks required to store `size` bytes
200
constexpr size_t block_count(SizeType data_size) noexcept
201
{
36,336✔
202
    return (page_count(data_size) + pages_per_block - 1) / pages_per_block;
36,336✔
203
}
36,336✔
204

205
// map an offset in the data to the actual location in the file
206
SizeType data_pos_to_file_pos(SizeType data_pos)
207
{
501,912✔
208
    REALM_ASSERT(data_pos >= 0);
501,912✔
209
    return data_pos + (block_index(data_pos) + 1) * encryption_page_size;
501,912✔
210
}
501,912✔
211

212
// map a location in the file to the offset in the data
213
SizeType file_pos_to_data_pos(SizeType file_pos)
214
{
2,610✔
215
    REALM_ASSERT(file_pos >= 0);
2,610✔
216
    const size_t metadata_page_count = (page_index(file_pos) + pages_per_block) / (pages_per_block + 1);
2,610✔
217
    return file_pos - metadata_page_count * encryption_page_size;
2,610✔
218
}
2,610✔
219

220
// get the location of the IVTable for the given data (not file) position
221
SizeType iv_table_pos(SizeType data_pos)
222
{
186,000✔
223
    REALM_ASSERT(data_pos >= 0);
186,000✔
224
    const size_t index = page_index(data_pos);
186,000✔
225
    const size_t metadata_block = block_index(data_pos);
186,000✔
226
    const size_t metadata_index = index & (pages_per_block - 1);
186,000✔
227
    return metadata_block * (pages_per_block + 1) * encryption_page_size + metadata_index * metadata_size;
186,000✔
228
}
186,000✔
229

230
// get the file location of the IVTable block for the given data (not file) position
231
SizeType iv_table_block_pos(SizeType data_pos)
232
{
151,449✔
233
    REALM_ASSERT(data_pos >= 0);
151,449✔
234
    return block_index(data_pos) * (pages_per_block + 1) * encryption_page_size;
151,449✔
235
}
151,449✔
236

237
constexpr size_t iv_table_size(SizeType data_pos)
238
{
36,336✔
239
    return block_count(data_pos) * pages_per_block;
36,336✔
240
}
36,336✔
241

242
// not actually checked any more
243
size_t check_read(FileDesc fd, SizeType pos, void* dst)
244
{
466,956✔
245
    return File::read_static(fd, pos, static_cast<char*>(dst), encryption_page_size);
466,956✔
246
}
466,956✔
247

248
// first block is iv data, second page is data
249
static_assert(c_min_encrypted_file_size == 2 * encryption_page_size,
250
              "chaging the block size breaks encrypted file portability");
251

252
template <class T, size_t N, std::size_t... I>
253
constexpr std::array<T, N> to_array_impl(const T* ptr, std::index_sequence<I...>)
254
{
699✔
255
    return {{ptr[I]...}};
699✔
256
}
699✔
257
template <class T, size_t N>
258
constexpr auto to_array(const T* ptr)
259
{
699✔
260
    return to_array_impl<T, N>(ptr, std::make_index_sequence<N>{});
699✔
261
}
699✔
262

263
void memcpy_if_changed(void* dst, const void* src, size_t n)
264
{
464,682✔
265
#if REALM_SANITIZE_THREAD
266
    // Because our copying is page-level granularity, we have some benign races
267
    // where the byte ranges in each page that weren't modified get overwritten
268
    // with the same values as they already had. TSan correctly reports this as
269
    // a data race, so when using TSan do (much slower) byte-level checking for
270
    // modifications and only write the ones which changed. Unlike suppressing
271
    // the warning entirely, this will still produce tsan errors if we actually
272
    // change any bytes that another thread is reading.
273
    auto dst_2 = static_cast<char*>(dst);
274
    auto src_2 = static_cast<const char*>(src);
275
    for (size_t i = 0; i < n; ++i) {
276
        if (dst_2[i] != src_2[i])
277
            dst_2[i] = src_2[i];
278
    }
279
#else
280
    memcpy(dst, src, n);
464,682✔
281
#endif
464,682✔
282
}
464,682✔
283

284
} // anonymous namespace
285

286
AESCryptor::AESCryptor(const char* key)
287
    : m_key(to_array<uint8_t, 64>(reinterpret_cast<const uint8_t*>(key)))
342✔
288
    , m_rw_buffer(new char[encryption_page_size])
342✔
289
    , m_dst_buffer(new char[encryption_page_size])
342✔
290
{
699✔
291
#if REALM_PLATFORM_APPLE
357✔
292
    // A random iv is passed to CCCryptorReset. This iv is *not used* by Realm; we set it manually prior to
293
    // each call to BCryptEncrypt() and BCryptDecrypt(). We pass this random iv as an attempt to
294
    // suppress a false encryption security warning from the IBM Bluemix Security Analyzer (PR[#2911])
295
    unsigned char u_iv[kCCKeySizeAES256];
357✔
296
    arc4random_buf(u_iv, kCCKeySizeAES256);
357✔
297
    void* iv = u_iv;
357✔
298
    CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES, 0 /* options */, key, kCCKeySizeAES256, iv, &m_encr);
357✔
299
    CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES, 0 /* options */, key, kCCKeySizeAES256, iv, &m_decr);
357✔
300
#elif defined(_WIN32)
301
    BCRYPT_ALG_HANDLE hAesAlg = NULL;
302
    int ret;
303
    ret = BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
304
    REALM_ASSERT_RELEASE_EX(ret == 0 && "BCryptOpenAlgorithmProvider()", ret);
305

306
    ret = BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC,
307
                            sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
308
    REALM_ASSERT_RELEASE_EX(ret == 0 && "BCryptSetProperty()", ret);
309

310
    ret = BCryptGenerateSymmetricKey(hAesAlg, &m_aes_key_handle, nullptr, 0, (PBYTE)key, 32, 0);
311
    REALM_ASSERT_RELEASE_EX(ret == 0 && "BCryptGenerateSymmetricKey()", ret);
312
#else
313
    m_ctx = EVP_CIPHER_CTX_new();
342✔
314
    if (!m_ctx)
342✔
315
        handle_error();
316
#endif
342✔
317
}
699✔
318

319
AESCryptor::~AESCryptor() noexcept
320
{
699✔
321
#if REALM_PLATFORM_APPLE
357✔
322
    CCCryptorRelease(m_encr);
357✔
323
    CCCryptorRelease(m_decr);
357✔
324
#elif defined(_WIN32)
325
#else
326
    EVP_CIPHER_CTX_cleanup(m_ctx);
342✔
327
    EVP_CIPHER_CTX_free(m_ctx);
342✔
328
#endif
342✔
329
}
699✔
330

331
void AESCryptor::handle_error()
332
{
×
333
    throw std::runtime_error("Error occurred in encryption layer");
×
334
}
×
335

336
void AESCryptor::set_data_size(SizeType new_data_size)
337
{
36,336✔
338
    REALM_ASSERT(new_data_size >= 0);
36,336✔
339
    m_iv_buffer.reserve(iv_table_size(new_data_size));
36,336✔
340
    m_iv_buffer_cache.reserve(m_iv_buffer.capacity());
36,336✔
341
    m_iv_blocks_read.resize(m_iv_buffer.capacity() / 64);
36,336✔
342
}
36,336✔
343

344
IVTable& AESCryptor::get_iv_table(FileDesc fd, SizeType data_pos, IVLookupMode mode) noexcept
345
{
527,685✔
346
    size_t idx = page_index(data_pos);
527,685✔
347
    REALM_ASSERT(idx < m_iv_buffer.capacity()); // required space should have been preallocated
527,685✔
348
    if (mode != IVLookupMode::UseCache || idx >= m_iv_buffer.size() || !m_iv_blocks_read[block_index(data_pos)]) {
527,685✔
349
        read_iv_block(fd, data_pos);
14,187✔
350
    }
14,187✔
351
    m_iv_buffer_cache[idx] = m_iv_buffer[idx];
527,685✔
352
    return m_iv_buffer[idx];
527,685✔
353
}
527,685✔
354

355
// We always read an entire block of IVTables at a time rather than just the
356
// one we need as it's likely to take about the same amount of time up front
357
// and greatly reduce the total number of read calls we have to make
358
void AESCryptor::read_iv_block(FileDesc fd, SizeType data_pos)
359
{
151,449✔
360
    size_t idx = block_index(data_pos) * pages_per_block;
151,449✔
361
    if (idx + pages_per_block > m_iv_buffer.size()) {
151,449✔
362
        m_iv_buffer.resize(idx + pages_per_block);
1,344✔
363
        m_iv_buffer_cache.resize(m_iv_buffer.size());
1,344✔
364
    }
1,344✔
365
    SizeType iv_pos = iv_table_block_pos(data_pos);
151,449✔
366
    check_read(fd, iv_pos, &m_iv_buffer[idx]);
151,449✔
367
    m_iv_blocks_read[block_index(data_pos)] = true;
151,449✔
368
}
151,449✔
369

370
void AESCryptor::calculate_hmac(Hmac& hmac) const
371
{
315,489✔
372
    hmac_sha224(Span(reinterpret_cast<const uint8_t*>(m_rw_buffer.get()), encryption_page_size), hmac,
315,489✔
373
                Span(m_key).sub_span<32>());
315,489✔
374
}
315,489✔
375

376
bool AESCryptor::constant_time_equals(const Hmac& a, const Hmac& b) const
377
{
315,729✔
378
    // Constant-time memcmp to avoid timing attacks
379
    uint8_t result = 0;
315,729✔
380
    for (size_t i = 0; i < a.size(); ++i)
9,156,141✔
381
        result |= a[i] ^ b[i];
8,840,412✔
382
    return result == 0;
315,729✔
383
}
315,729✔
384

385
bool AESCryptor::refresh_iv(FileDesc fd, size_t page_ndx)
386
{
144,621✔
387
    REALM_ASSERT(page_ndx < m_iv_buffer.capacity());
144,621✔
388
    if (page_ndx >= m_iv_buffer.size() || !m_iv_blocks_read[page_ndx / pages_per_block]) {
144,621✔
389
        read_iv_block(fd, SizeType(page_ndx) * encryption_page_size);
137,262✔
390
    }
137,262✔
391

392
    if (m_iv_buffer[page_ndx] != m_iv_buffer_cache[page_ndx]) {
144,621✔
393
        m_iv_buffer_cache[page_ndx] = m_iv_buffer[page_ndx];
133,134✔
394
        return true;
133,134✔
395
    }
133,134✔
396
    return false;
11,487✔
397
}
144,621✔
398

399
void AESCryptor::invalidate_ivs() noexcept
400
{
144,783✔
401
    m_iv_blocks_read.assign(m_iv_blocks_read.size(), false);
144,783✔
402
}
144,783✔
403

404
AESCryptor::ReadResult AESCryptor::read(FileDesc fd, SizeType pos, char* dst, WriteObserver* observer)
405
{
336,159✔
406
    uint32_t iv = 0;
336,159✔
407
    Hmac hmac;
336,159✔
408
    // We're in a single-process scenario (or other processes are only reading),
409
    // so we can trust our in-memory caches and never need to retry
410
    if (!observer || observer->no_concurrent_writer_seen()) {
336,159✔
411
        return attempt_read(fd, pos, dst, IVLookupMode::UseCache, iv, hmac);
334,860✔
412
    }
334,860✔
413

414
    // There's another process which might be trying to write to the file while
415
    // we're reading from it, which means that we might see invalid data due to
416
    // data races. When this happens we need to retry the read, and only throw
417
    // an error if the data either hasn't changed after the timeout has expired
418
    // or if we're in a reader starvation scenario where the writer is producing
419
    // new data faster than we can consume it.
420
    size_t retry_count = 0;
1,299✔
421
    std::pair<uint32_t, Hmac> last_iv_and_data_hash;
1,299✔
422
    auto retry_start_time = std::chrono::steady_clock::now();
1,299✔
423
    size_t num_identical_reads = 1;
1,299✔
424
    ReadResult result = ReadResult::Success;
1,299✔
425
    while (retry_count <= 5 || (retry_count - num_identical_reads > 1 && retry_count < 20)) {
6,825✔
426
        result =
6,825✔
427
            attempt_read(fd, pos, dst, retry_count == 0 ? IVLookupMode::UseCache : IVLookupMode::Refetch, iv, hmac);
6,825✔
428
        switch (result) {
6,825✔
429
            case ReadResult::Success:
1,239✔
430
            case ReadResult::Eof:
1,239✔
431
            case ReadResult::Uninitialized:
1,251✔
432
                // Consistent and valid states that may or may not actually have data
433
                return result;
1,251✔
434
            case ReadResult::InterruptedFirstWrite:
5,382✔
435
            case ReadResult::StaleHmac:
5,382✔
436
            case ReadResult::Failed:
5,574✔
437
                // Inconsistent states which may change if we retry
438
                break;
5,574✔
439
        }
6,825✔
440

441
        // Check if we've timed out, but always retry at least once in case
442
        // we got suspended while another process was writing or something
443
        constexpr auto max_retry_period = std::chrono::seconds(5);
5,574✔
444
        auto elapsed = std::chrono::steady_clock::now() - retry_start_time;
5,574✔
445
        if (retry_count > 0 && elapsed > max_retry_period) {
5,574✔
446
            auto str = util::format("unable to decrypt after %1 seconds (retry_count=%2)",
×
447
                                    std::chrono::duration_cast<std::chrono::seconds>(elapsed).count(), retry_count);
×
448
            // std::cerr << std::endl << "*Timeout: " << str << std::endl;
449
            throw DecryptionFailed(str);
×
450
        }
×
451

452
        // don't wait on the first retry as we want to optimize the case where the first read
453
        // from the iv table cache didn't validate and we are fetching the iv block from disk for the first time
454
        std::pair cur_iv_and_data_hash(iv, hmac);
5,574✔
455
        if (retry_count != 0) {
5,574✔
456
            if (last_iv_and_data_hash == cur_iv_and_data_hash) {
5,136✔
457
                ++num_identical_reads;
144✔
458
            }
144✔
459
            // don't retry right away if there are potentially other external writers
460
            std::this_thread::yield();
5,136✔
461
        }
5,136✔
462
        last_iv_and_data_hash = cur_iv_and_data_hash;
5,574✔
463
        ++retry_count;
5,574✔
464

465
        if (observer->no_concurrent_writer_seen())
5,574✔
466
            break;
48✔
467
    }
5,574✔
468

469
    return result;
48✔
470
}
1,299✔
471

472
AESCryptor::ReadResult AESCryptor::attempt_read(FileDesc fd, SizeType pos, char* dst, IVLookupMode iv_mode,
473
                                                uint32_t& iv_out, Hmac& hmac)
474
{
341,685✔
475
    IVTable& iv = get_iv_table(fd, pos, iv_mode);
341,685✔
476
    iv_out = iv.iv1;
341,685✔
477
    if (iv.iv1 == 0) {
341,685✔
478
        hmac.fill(0);
26,178✔
479
        return ReadResult::Uninitialized;
26,178✔
480
    }
26,178✔
481

482
    size_t actual = check_read(fd, data_pos_to_file_pos(pos), m_rw_buffer.get());
315,507✔
483
    if (actual < encryption_page_size) {
315,507✔
484
        return ReadResult::Eof;
18✔
485
    }
18✔
486

487
    calculate_hmac(hmac);
315,489✔
488
    if (!constant_time_equals(hmac, iv.hmac1)) {
315,489✔
489
        // Either the DB is corrupted or we were interrupted between writing the
490
        // new IV and writing the data
491
        if (iv.iv2 == 0) {
5,835✔
492
            return ReadResult::InterruptedFirstWrite;
5,595✔
493
        }
5,595✔
494

495
        if (constant_time_equals(hmac, iv.hmac2)) {
240✔
496
            // Un-bump the IV since the write with the bumped IV never actually
497
            // happened
498
            memcpy(&iv.iv1, &iv.iv2, 32);
6✔
499
        }
6✔
500
        else {
234✔
501
            // If the file has been shrunk and then re-expanded, we may have
502
            // old hmacs that don't go with this data. ftruncate() is
503
            // required to fill any added space with zeroes, so assume that's
504
            // what happened if the buffer is all zeroes
505
            bool all_zero = std::all_of(&m_rw_buffer[0], &m_rw_buffer[actual], [](char c) {
172,224✔
506
                return c == 0;
172,224✔
507
            });
172,224✔
508
            if (all_zero)
234✔
509
                return ReadResult::StaleHmac;
42✔
510
            return ReadResult::Failed;
192✔
511
        }
234✔
512
    }
240✔
513

514
    // We may expect some address ranges of the destination buffer of
515
    // AESCryptor::read() to stay unmodified, i.e. being overwritten with
516
    // the same bytes as already present, and may have read-access to these
517
    // from other threads while decryption is taking place.
518
    //
519
    // However, some implementations of AES_cbc_encrypt(), in particular
520
    // OpenSSL, will put garbled bytes as an intermediate step during the
521
    // operation which will lead to incorrect data being read by other
522
    // readers concurrently accessing that page. Incorrect data leads to
523
    // crashes.
524
    //
525
    // We therefore decrypt to a temporary buffer first and then copy the
526
    // completely decrypted data after.
527
    crypt(mode_Decrypt, pos, m_dst_buffer.get(), m_rw_buffer.get(), reinterpret_cast<const char*>(&iv.iv1));
309,660✔
528
    memcpy_if_changed(dst, m_dst_buffer.get(), encryption_page_size);
309,660✔
529
    return ReadResult::Success;
309,660✔
530
}
315,489✔
531

532
void AESCryptor::try_read_block(FileDesc fd, SizeType pos, char* dst) noexcept
533
{
×
534
    size_t bytes_read = check_read(fd, data_pos_to_file_pos(pos), m_rw_buffer.get());
×
535

536
    if (bytes_read == 0) {
×
537
        std::cerr << "Read failed: 0x" << std::hex << pos << std::endl;
×
538
        memset(dst, 0x55, encryption_page_size);
×
539
        return;
×
540
    }
×
541

542
    IVTable& iv = get_iv_table(fd, pos, IVLookupMode::Refetch);
×
543
    if (iv.iv1 == 0) {
×
544
        std::cerr << "Block never written: 0x" << std::hex << pos << std::endl;
×
545
        memset(dst, 0xAA, encryption_page_size);
×
546
        return;
×
547
    }
×
548

549
    Hmac hmac;
×
550
    calculate_hmac(hmac);
×
551
    if (!constant_time_equals(hmac, iv.hmac1)) {
×
552
        if (iv.iv2 == 0) {
×
553
            std::cerr << "First write interrupted: 0x" << std::hex << pos << std::endl;
×
554
        }
×
555

556
        if (constant_time_equals(hmac, iv.hmac2)) {
×
557
            std::cerr << "Restore old IV: 0x" << std::hex << pos << std::endl;
×
558
            memcpy(&iv.iv1, &iv.iv2, 32);
×
559
        }
×
560
        else {
×
561
            std::cerr << "Checksum failed: 0x" << std::hex << pos << std::endl;
×
562
        }
×
563
    }
×
564
    crypt(mode_Decrypt, pos, dst, m_rw_buffer.get(), reinterpret_cast<const char*>(&iv.iv1));
×
565
}
×
566

567
void AESCryptor::write(FileDesc fd, SizeType pos, const char* src, WriteMarker* marker) noexcept
568
{
186,000✔
569
    IVTable& iv = get_iv_table(fd, pos);
186,000✔
570

571
    memcpy(&iv.iv2, &iv.iv1, 32); // this is also copying the hmac
186,000✔
572
    do {
186,072✔
573
        ++iv.iv1;
186,072✔
574
        // 0 is reserved for never-been-used, so bump if we just wrapped around
575
        if (iv.iv1 == 0)
186,072✔
576
            ++iv.iv1;
×
577

578
        crypt(mode_Encrypt, pos, m_rw_buffer.get(), src, reinterpret_cast<const char*>(&iv.iv1));
186,072✔
579
        hmac_sha224(Span(reinterpret_cast<uint8_t*>(m_rw_buffer.get()), encryption_page_size), iv.hmac1,
186,072✔
580
                    Span(m_key).sub_span<32>());
186,072✔
581
        // In the extremely unlikely case that both the old and new versions have
582
        // the same hash we won't know which IV to use, so bump the IV until
583
        // they're different.
584
    } while (REALM_UNLIKELY(iv.hmac1 == iv.hmac2));
186,072✔
585

586
    if (marker)
186,000✔
587
        marker->mark(pos);
1,251✔
588
    File::write_static(fd, iv_table_pos(pos), reinterpret_cast<const char*>(&iv), sizeof(iv));
186,000✔
589
    // FIXME: doesn't this need a barrier? The IV table is very likely to
590
    // make it to disk first due to being issued first and being earlier in
591
    // the file, but not guaranteed
592
    File::write_static(fd, data_pos_to_file_pos(pos), m_rw_buffer.get(), encryption_page_size);
186,000✔
593
    if (marker)
186,000✔
594
        marker->unmark();
1,251✔
595
    m_iv_buffer_cache[page_index(pos)] = iv;
186,000✔
596
}
186,000✔
597

598
void AESCryptor::crypt(EncryptionMode mode, SizeType pos, char* dst, const char* src, const char* stored_iv) noexcept
599
{
495,732✔
600
    uint8_t iv[aes_block_size] = {0};
495,732✔
601
    memcpy(iv, stored_iv, 4);
495,732✔
602
    memcpy(iv + 4, &pos, sizeof(pos));
495,732✔
603

604
#if REALM_PLATFORM_APPLE
265,500✔
605
    CCCryptorRef cryptor = mode == mode_Encrypt ? m_encr : m_decr;
265,500✔
606
    CCCryptorReset(cryptor, iv);
265,500✔
607

608
    size_t bytesEncrypted = 0;
265,500✔
609
    CCCryptorStatus err =
265,500✔
610
        CCCryptorUpdate(cryptor, src, encryption_page_size, dst, encryption_page_size, &bytesEncrypted);
265,500✔
611
    REALM_ASSERT(err == kCCSuccess);
265,500✔
612
    REALM_ASSERT(bytesEncrypted == encryption_page_size);
265,500✔
613
#elif defined(_WIN32)
614
    ULONG cbData;
615
    int i;
616

617
    if (mode == mode_Encrypt) {
618
        i = BCryptEncrypt(m_aes_key_handle, (PUCHAR)src, encryption_page_size, nullptr, (PUCHAR)iv, sizeof(iv),
619
                          (PUCHAR)dst, encryption_page_size, &cbData, 0);
620
        REALM_ASSERT_RELEASE_EX(i == 0 && "BCryptEncrypt()", i);
621
        REALM_ASSERT_RELEASE_EX(cbData == encryption_page_size && "BCryptEncrypt()", cbData);
622
    }
623
    else if (mode == mode_Decrypt) {
624
        i = BCryptDecrypt(m_aes_key_handle, (PUCHAR)src, encryption_page_size, nullptr, (PUCHAR)iv, sizeof(iv),
625
                          (PUCHAR)dst, encryption_page_size, &cbData, 0);
626
        REALM_ASSERT_RELEASE_EX(i == 0 && "BCryptDecrypt()", i);
627
        REALM_ASSERT_RELEASE_EX(cbData == encryption_page_size && "BCryptDecrypt()", cbData);
628
    }
629
    else {
630
        REALM_UNREACHABLE();
631
    }
632

633
#else
634
    if (!EVP_CipherInit_ex(m_ctx, EVP_aes_256_cbc(), NULL, m_key.data(), iv, mode))
230,232✔
635
        handle_error();
636

637
    int len;
230,232✔
638
    // Use zero padding - we always write a whole page
639
    EVP_CIPHER_CTX_set_padding(m_ctx, 0);
230,232✔
640

641
    if (!EVP_CipherUpdate(m_ctx, reinterpret_cast<uint8_t*>(dst), &len, reinterpret_cast<const uint8_t*>(src),
230,232✔
642
                          encryption_page_size))
230,232✔
643
        handle_error();
644

645
    // Finalize the encryption. Should not output further data.
646
    if (!EVP_CipherFinal_ex(m_ctx, reinterpret_cast<uint8_t*>(dst) + len, &len))
230,232✔
647
        handle_error();
648
#endif
230,232✔
649
}
495,732✔
650

651
EncryptedFile::EncryptedFile(const char* key, FileDesc fd)
652
    : fd(fd)
315✔
653
    , cryptor(key)
315✔
654
{
645✔
655
}
645✔
656

657
std::unique_ptr<EncryptedFileMapping> EncryptedFile::add_mapping(SizeType file_offset, void* addr, size_t size,
658
                                                                 File::AccessMode access)
659
{
35,901✔
660
    auto mapping = std::make_unique<EncryptedFileMapping>(*this, file_offset, addr, size, access);
35,901✔
661
    CheckedLockGuard lock(mutex);
35,901✔
662
    mappings.push_back(mapping.get());
35,901✔
663
    return mapping;
35,901✔
664
}
35,901✔
665

666
EncryptedFileMapping::EncryptedFileMapping(EncryptedFile& file, SizeType file_offset, void* addr, size_t size,
667
                                           File::AccessMode access, util::WriteObserver* observer,
668
                                           util::WriteMarker* marker)
669
    : m_file(file)
10,629✔
670
    , m_access(access)
10,629✔
671
    , m_observer(observer)
10,629✔
672
    , m_marker(marker)
10,629✔
673
#ifdef REALM_DEBUG
674
    , m_validate_buffer(new char[encryption_page_size])
10,629✔
675
#endif
676
{
35,901✔
677
    set(addr, size, file_offset); // throws
35,901✔
678
}
35,901✔
679

680
EncryptedFileMapping::~EncryptedFileMapping()
681
{
35,901✔
682
    CheckedLockGuard lock(m_file.mutex);
35,901✔
683
    for (auto& e : m_page_state) {
170,304✔
684
        REALM_ASSERT(is_not(e, Writable));
170,304✔
685
    }
170,304✔
686
    if (m_access == File::access_ReadWrite) {
35,901✔
687
        do_flush();
15,150✔
688
    }
15,150✔
689

690
    auto it = std::find(m_file.mappings.begin(), m_file.mappings.end(), this);
35,901✔
691
    REALM_ASSERT(it != m_file.mappings.end());
35,901✔
692
    if (it != m_file.mappings.end()) {
35,901✔
693
        m_file.mappings.erase(it);
35,901✔
694
    }
35,901✔
695
}
35,901✔
696

697
// offset within page, not within file
698
uint16_t EncryptedFileMapping::get_offset_of_address(const void* addr) const noexcept
699
{
×
700
    return reinterpret_cast<uintptr_t>(addr) & (encryption_page_size - 1);
×
701
}
×
702

703
size_t EncryptedFileMapping::get_local_index_of_address(const void* addr, size_t offset) const noexcept
704
{
16,009,395✔
705
    REALM_ASSERT_EX(addr >= m_addr, addr, m_addr);
16,009,395✔
706
    return (reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(m_addr) + offset) / encryption_page_size;
16,009,395✔
707
}
16,009,395✔
708

709
bool EncryptedFileMapping::contains_page(size_t block_in_file) const noexcept
710
{
409,827✔
711
    return block_in_file - m_first_page < m_page_state.size();
409,827✔
712
}
409,827✔
713

714
char* EncryptedFileMapping::page_addr(size_t local_ndx) const noexcept
715
{
3,179,538✔
716
    REALM_ASSERT_DEBUG(local_ndx < m_page_state.size());
3,179,538✔
717
    return static_cast<char*>(m_addr) + (local_ndx * encryption_page_size);
3,179,538✔
718
}
3,179,538✔
719

720
SizeType EncryptedFileMapping::page_pos(size_t local_ndx) const noexcept
721
{
517,887✔
722
    return SizeType(local_ndx + m_first_page) * encryption_page_size;
517,887✔
723
}
517,887✔
724

725
// If we have multiple mappings for the same part of the file, one of them may
726
// already contain the page we're about to read and if so we can skip reading
727
// it and instead just memcpy it.
728
bool EncryptedFileMapping::copy_up_to_date_page(size_t local_ndx) noexcept
729
{
227,394✔
730
    REALM_ASSERT_EX(local_ndx < m_page_state.size(), local_ndx, m_page_state.size());
227,394✔
731
    // Precondition: this method must never be called for a page which
732
    // is already up to date.
733
    REALM_ASSERT(is_not(m_page_state[local_ndx], UpToDate));
227,394✔
734
    size_t ndx_in_file = local_ndx + m_first_page;
227,394✔
735
    for (auto& m : m_file.mappings) {
236,337✔
736
        m->assert_locked();
236,337✔
737
        if (m == this || !m->contains_page(ndx_in_file))
236,337✔
738
            continue;
219,942✔
739

740
        size_t other_mapping_ndx = ndx_in_file - m->m_first_page;
16,395✔
741
        auto other_state = m->m_page_state[other_mapping_ndx];
16,395✔
742
        if (is(other_state, Writable) || is_not(other_state, UpToDate))
16,395✔
743
            continue;
7,923✔
744

745
        memcpy_if_changed(page_addr(local_ndx), m->page_addr(other_mapping_ndx), encryption_page_size);
8,472✔
746
        set(m_page_state[local_ndx], UpToDate);
8,472✔
747
        clear(m_page_state[local_ndx], StaleIV);
8,472✔
748
        return true;
8,472✔
749
    }
16,395✔
750
    return false;
218,922✔
751
}
227,394✔
752

753
// Whenever we advance our reader view of the file we mark all previously
754
// up-to-date pages as being possibly stale. On the next access of the page we
755
// then check if the IV for that page has changed to determine if the page has
756
// actually changed or if we can just mark it as being up-to-date again.
757
bool EncryptedFileMapping::check_possibly_stale_page(size_t local_ndx) noexcept
758
{
218,922✔
759
    if (is_not(m_page_state[local_ndx], StaleIV))
218,922✔
760
        return false;
81,981✔
761
    size_t ndx_in_file = local_ndx + m_first_page;
136,941✔
762
    bool did_change = m_file.cryptor.refresh_iv(m_file.fd, ndx_in_file);
136,941✔
763
    // Update the page state in all mappings and not just the current one because
764
    // refresh_iv() only returns true once per page per write. Deferring this
765
    // until copy_up_to_date_page() almost works, but this mapping could be
766
    // removed before the other mapping copies the page.
767
    for (auto& m : m_file.mappings) {
136,941✔
768
        m->assert_locked();
136,941✔
769
        if (!m->contains_page(ndx_in_file))
136,941✔
UNCOV
770
            continue;
×
771
        auto& state = m->m_page_state[ndx_in_file - m->m_first_page];
136,941✔
772
        if (is(state, StaleIV)) {
136,941✔
773
            REALM_ASSERT(is_not(state, UpToDate));
136,941✔
774
            clear(state, StaleIV);
136,941✔
775
            if (!did_change)
136,941✔
776
                set(state, UpToDate);
6,111✔
777
        }
136,941✔
778
    }
136,941✔
779
    return !did_change;
136,941✔
780
}
218,922✔
781

782
REALM_NORETURN
783
REALM_COLD
784
void EncryptedFileMapping::throw_decryption_error(size_t local_ndx, std::string_view msg)
785
{
486✔
786
    size_t fs = to_size_t(File::get_size_static(m_file.fd));
486✔
787
    throw DecryptionFailed(util::format("page %1 in file of size %2 %3", local_ndx + m_first_page, fs, msg));
486✔
788
}
486✔
789

790
void EncryptedFileMapping::refresh_page(size_t local_ndx, bool to_modify)
791
{
227,394✔
792
    REALM_ASSERT_EX(local_ndx < m_page_state.size(), local_ndx, m_page_state.size());
227,394✔
793
    REALM_ASSERT(is_not(m_page_state[local_ndx], Dirty));
227,394✔
794
    REALM_ASSERT(is_not(m_page_state[local_ndx], Writable));
227,394✔
795
    if (copy_up_to_date_page(local_ndx) || check_possibly_stale_page(local_ndx)) {
227,394✔
796
        return;
14,583✔
797
    }
14,583✔
798

799
    char* addr = page_addr(local_ndx);
212,811✔
800
    switch (m_file.cryptor.read(m_file.fd, page_pos(local_ndx), addr, m_observer)) {
212,811✔
801
        case AESCryptor::ReadResult::Eof:
18✔
802
            if (!to_modify)
18✔
803
                throw_decryption_error(local_ndx, "is out of bounds");
18✔
804
            break;
18✔
805
        case AESCryptor::ReadResult::Uninitialized:
26,178✔
806
            if (!to_modify)
26,178✔
807
                throw_decryption_error(local_ndx, "has never been written to");
402✔
808
            break;
26,178✔
809
        case AESCryptor::ReadResult::InterruptedFirstWrite:
213✔
810
            if (!to_modify)
213✔
811
                throw_decryption_error(local_ndx, "has never been successfully written to, but a write was begun");
18✔
812
            break;
213✔
813
        case AESCryptor::ReadResult::StaleHmac:
42✔
814
            break;
42✔
815
        case AESCryptor::ReadResult::Failed:
48✔
816
            throw_decryption_error(
48✔
817
                local_ndx, "failed the HMAC check. Either the encryption key is incorrect or data is corrupted");
48✔
818
        case AESCryptor::ReadResult::Success:
186,360✔
819
            break;
186,360✔
820
    }
212,811✔
821
    set(m_page_state[local_ndx], UpToDate);
212,325✔
822
}
212,325✔
823

824
void EncryptedFile::mark_data_as_possibly_stale()
825
{
143,229✔
826

827
    util::CheckedLockGuard lock(mutex);
143,229✔
828
    cryptor.invalidate_ivs();
143,229✔
829
    for (auto& m : mappings) {
143,229✔
830
        m->assert_locked();
143,229✔
831
        m->mark_pages_for_iv_check();
143,229✔
832
    }
143,229✔
833
}
143,229✔
834

835
void EncryptedFileMapping::mark_pages_for_iv_check()
836
{
143,229✔
837
    for (auto& state : m_page_state) {
36,252,687✔
838
        if (is(state, UpToDate) && is_not(state, Dirty | Writable)) {
36,252,687✔
839
            REALM_ASSERT(is_not(state, StaleIV));
148,638✔
840
            clear(state, UpToDate);
148,638✔
841
            set(state, StaleIV);
148,638✔
842
        }
148,638✔
843
    }
36,252,687✔
844
}
143,229✔
845

846
void EncryptedFileMapping::write_and_update_all(size_t local_ndx, uint16_t offset, uint16_t size) noexcept
847
{
2,356,395✔
848
    REALM_ASSERT(is(m_page_state[local_ndx], Writable));
2,356,395✔
849
    REALM_ASSERT(is(m_page_state[local_ndx], UpToDate));
2,356,395✔
850
    REALM_ASSERT(is_not(m_page_state[local_ndx], StaleIV));
2,356,395✔
851
    REALM_ASSERT(offset + size <= encryption_page_size);
2,356,395✔
852
    // Go through all other mappings of this file and copy changes into those mappings
853
    size_t ndx_in_file = local_ndx + m_first_page;
2,356,395✔
854
    for (auto& m : m_file.mappings) {
2,594,691✔
855
        m->assert_locked();
2,594,691✔
856
        if (m == this || !m->contains_page(ndx_in_file))
2,594,691✔
857
            continue;
2,361,456✔
858

859
        size_t other_local_ndx = ndx_in_file - m->m_first_page;
233,235✔
860
        auto& state = m->m_page_state[other_local_ndx];
233,235✔
861
        if (is(state, UpToDate)) {
233,235✔
862
            memcpy_if_changed(m->page_addr(other_local_ndx) + offset, page_addr(local_ndx) + offset, size);
146,433✔
863
        }
146,433✔
864
        // If the target page is possibly stale then we need to copy the entire
865
        // page and not just the bytes we just touched as other parts of the
866
        // page may be out of date
867
        else if (is(state, StaleIV)) {
86,802✔
868
            memcpy_if_changed(m->page_addr(other_local_ndx), page_addr(local_ndx), encryption_page_size);
117✔
869
            set(state, UpToDate);
117✔
870
            clear(state, StaleIV);
117✔
871
        }
117✔
872
    }
233,235✔
873
    set(m_page_state[local_ndx], Dirty);
2,356,395✔
874
    clear(m_page_state[local_ndx], Writable);
2,356,395✔
875
}
2,356,395✔
876

877
void EncryptedFileMapping::validate_page(size_t local_ndx) noexcept
878
{
16,205,052✔
879
#ifdef REALM_DEBUG
16,205,052✔
880
    REALM_ASSERT(local_ndx < m_page_state.size());
16,205,052✔
881
    if (is_not(m_page_state[local_ndx], UpToDate))
16,205,052✔
882
        return;
16,082,490✔
883

884
    switch (m_file.cryptor.read(m_file.fd, page_pos(local_ndx), m_validate_buffer.get(), m_observer)) {
122,562✔
885
        case AESCryptor::ReadResult::Eof:
✔
886
        case AESCryptor::ReadResult::Uninitialized:
✔
887
        case AESCryptor::ReadResult::InterruptedFirstWrite:
✔
888
        case AESCryptor::ReadResult::StaleHmac:
✔
889
            return;
×
890
        case AESCryptor::ReadResult::Failed:
✔
891
            abort();
×
892
        case AESCryptor::ReadResult::Success:
122,562✔
893
            break;
122,562✔
894
    }
122,562✔
895

896
    const size_t ndx_in_file = local_ndx + m_first_page;
122,562✔
897
    for (auto& m : m_file.mappings) {
135,897✔
898
        m->assert_locked();
135,897✔
899
        size_t other_local_ndx = ndx_in_file - m->m_first_page;
135,897✔
900
        if (m != this && m->contains_page(ndx_in_file) && is(m->m_page_state[other_local_ndx], Dirty)) {
135,897✔
901
            memcpy(m_validate_buffer.get(), m->page_addr(other_local_ndx), encryption_page_size);
3,840✔
902
            break;
3,840✔
903
        }
3,840✔
904
    }
135,897✔
905

906
    if (memcmp(m_validate_buffer.get(), page_addr(local_ndx), encryption_page_size) != 0) {
122,562✔
907
        util::format(std::cerr, "mismatch %1: fd(%2) page(%3/%4) %5 %6\n", this, m_file.fd, local_ndx,
×
908
                     m_page_state.size(), m_validate_buffer.get(), page_addr(local_ndx));
×
909
        REALM_TERMINATE("");
910
    }
×
911
#else
912
    static_cast<void>(local_ndx);
913
#endif
914
}
122,562✔
915

916
void EncryptedFileMapping::validate() noexcept
917
{
116,457✔
918
#ifdef REALM_DEBUG
116,457✔
919
    for (size_t i = 0; i < m_page_state.size(); ++i)
8,270,913✔
920
        validate_page(i);
8,154,456✔
921
#endif
116,457✔
922
}
116,457✔
923

924
void EncryptedFileMapping::do_flush(bool skip_validate) noexcept
925
{
195,111✔
926
    for (size_t i = 0; i < m_page_state.size(); ++i) {
36,540,537✔
927
        if (is_not(m_page_state[i], Dirty)) {
36,345,426✔
928
            if (!skip_validate) {
36,162,915✔
929
                validate_page(i);
8,050,596✔
930
            }
8,050,596✔
931
            continue;
36,162,915✔
932
        }
36,162,915✔
933
        m_file.cryptor.write(m_file.fd, page_pos(i), page_addr(i), m_marker);
182,511✔
934
        clear(m_page_state[i], Dirty);
182,511✔
935
    }
182,511✔
936

937
    // some of the tests call flush() on very small writes which results in
938
    // validating on every flush being unreasonably slow
939
    if (!skip_validate) {
195,111✔
940
        validate();
116,457✔
941
    }
116,457✔
942
}
195,111✔
943

944
void EncryptedFileMapping::flush(bool skip_validate) noexcept
945
{
142,740✔
946
    util::CheckedLockGuard lock(m_file.mutex);
142,740✔
947
    do_flush(skip_validate);
142,740✔
948
}
142,740✔
949

950
void EncryptedFileMapping::sync() noexcept
951
{
1,320✔
952
    util::CheckedLockGuard lock(m_file.mutex);
1,320✔
953
    do_sync();
1,320✔
954
}
1,320✔
955

956
#ifdef _MSC_VER
957
#pragma warning(disable : 4297) // throw in noexcept
958
#endif
959
void EncryptedFileMapping::do_sync() noexcept
960
{
1,320✔
961
    do_flush();
1,320✔
962

963
#ifdef _WIN32
964
    if (FlushFileBuffers(m_file.fd))
965
        return;
966
    throw std::system_error(GetLastError(), std::system_category(), "FlushFileBuffers() failed");
967
#else
968
    fsync(m_file.fd);
1,320✔
969
#endif
1,320✔
970
}
1,320✔
971
#ifdef _MSC_VER
972
#pragma warning(default : 4297)
973
#endif
974

975
void EncryptedFileMapping::write_barrier(const void* addr, size_t size) noexcept
976
{
2,347,767✔
977
    CheckedLockGuard lock(m_file.mutex);
2,347,767✔
978
    REALM_ASSERT(size > 0);
2,347,767✔
979
    REALM_ASSERT(m_access == File::access_ReadWrite);
2,347,767✔
980

981
    size_t local_ndx = get_local_index_of_address(addr);
2,347,767✔
982
    auto offset_in_page = uint16_t(static_cast<const char*>(addr) - page_addr(local_ndx));
2,347,767✔
983
    size += offset_in_page;
2,347,767✔
984

985
    // Propagate changes to all other decrypted pages mapping the same memory
986
    while (size > 0) {
4,704,162✔
987
        REALM_ASSERT(local_ndx < m_page_state.size());
2,356,395✔
988
        REALM_ASSERT(is(m_page_state[local_ndx], PageState::Writable));
2,356,395✔
989
        auto bytes_in_page = uint16_t(std::min<size_t>(encryption_page_size, size) - offset_in_page);
2,356,395✔
990
        write_and_update_all(local_ndx, offset_in_page, bytes_in_page);
2,356,395✔
991
        size -= offset_in_page + bytes_in_page;
2,356,395✔
992
        offset_in_page = 0;
2,356,395✔
993
        ++local_ndx;
2,356,395✔
994
    }
2,356,395✔
995
}
2,347,767✔
996

997
void EncryptedFileMapping::read_barrier(const void* addr, size_t size, bool to_modify)
998
{
6,830,529✔
999
    CheckedLockGuard lock(m_file.mutex);
6,830,529✔
1000
    REALM_ASSERT(size > 0);
6,830,529✔
1001
    size_t begin = get_local_index_of_address(addr);
6,830,529✔
1002
    size_t end = get_local_index_of_address(addr, size - 1);
6,830,529✔
1003
    for (size_t local_ndx = begin; local_ndx <= end; ++local_ndx) {
13,712,346✔
1004
        PageState& ps = m_page_state[local_ndx];
6,881,817✔
1005
        if (is_not(ps, UpToDate))
6,881,817✔
1006
            refresh_page(local_ndx, to_modify);
227,394✔
1007
        if (to_modify)
6,881,817✔
1008
            set(ps, Writable);
2,356,395✔
1009
    }
6,881,817✔
1010
}
6,830,529✔
1011

1012
void EncryptedFileMapping::extend_to(SizeType offset, size_t new_size)
1013
{
381✔
1014
    CheckedLockGuard lock(m_file.mutex);
381✔
1015
    REALM_ASSERT_EX(new_size % encryption_page_size == 0, new_size, encryption_page_size);
381✔
1016
    m_page_state.resize(page_count(new_size), PageState::Clean);
381✔
1017
    m_file.cryptor.set_data_size(offset + SizeType(new_size));
381✔
1018
}
381✔
1019

1020
void EncryptedFileMapping::set(void* new_addr, size_t new_size, SizeType new_file_offset)
1021
{
35,901✔
1022
    CheckedLockGuard lock(m_file.mutex);
35,901✔
1023
    REALM_ASSERT(new_file_offset % encryption_page_size == 0);
35,901✔
1024
    REALM_ASSERT(new_size % encryption_page_size == 0);
35,901✔
1025

1026
    // This seems dangerous - correct operation in a setting with multiple (partial)
1027
    // mappings of the same file would rely on ordering of individual mapping requests.
1028
    // Currently we only ever extend the file - but when we implement continuous defrag,
1029
    // this design should be revisited.
1030
    m_file.cryptor.set_data_size(new_file_offset + SizeType(new_size));
35,901✔
1031

1032
    do_flush();
35,901✔
1033
    m_addr = new_addr;
35,901✔
1034

1035
    // set_data_size() would have thrown if this cast would overflow
1036
    m_first_page = size_t(new_file_offset / encryption_page_size);
35,901✔
1037
    m_page_state.clear();
35,901✔
1038
    m_page_state.resize(new_size / encryption_page_size, PageState::Clean);
35,901✔
1039
}
35,901✔
1040

1041
SizeType encrypted_size_to_data_size(SizeType size) noexcept
1042
{
2,832✔
1043
    return size == 0 ? 0 : file_pos_to_data_pos(size);
2,832✔
1044
}
2,832✔
1045

1046
SizeType data_size_to_encrypted_size(SizeType size) noexcept
1047
{
405✔
1048
    SizeType r = size % encryption_page_size;
405✔
1049
    size += r ? encryption_page_size - r : 0;
405✔
1050
    return data_pos_to_file_pos(size);
405✔
1051
}
405✔
1052
} // namespace realm::util
1053
#else
1054

1055
namespace realm::util {
1056
File::SizeType encrypted_size_to_data_size(File::SizeType size) noexcept
1057
{
1058
    return size;
1059
}
1060

1061
File::SizeType data_size_to_encrypted_size(File::SizeType size) noexcept
1062
{
1063
    return size;
1064
}
1065
} // namespace realm::util
1066
#endif // REALM_ENABLE_ENCRYPTION
1067

1068
namespace realm::util {
1069
std::string DecryptionFailed::get_message_with_bt(std::string_view msg)
1070
{
486✔
1071
    auto bt = Backtrace::capture();
486✔
1072
    std::stringstream ss;
486✔
1073
    bt.print(ss);
486✔
1074
    return util::format("Decryption failed: %1\n%2\n", msg, ss.str());
486✔
1075
}
486✔
1076
} // namespace realm::util
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc