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

realm / realm-core / jorgen.edelbo_334

01 Jul 2024 07:22AM UTC coverage: 90.829% (-0.04%) from 90.865%
jorgen.edelbo_334

Pull #7803

Evergreen

jedelbo
Merge branch 'next-major' into feature/string-compression
Pull Request #7803: Feature/string compression

102912 of 180568 branches covered (56.99%)

1141 of 1267 new or added lines in 33 files covered. (90.06%)

172 existing lines in 24 files now uncovered.

218291 of 240332 relevant lines covered (90.83%)

7818396.4 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,624✔
141
        return iv1 == other.iv1 && iv2 == other.iv2 && hmac1 == other.hmac1 && hmac2 == other.hmac2;
144,624✔
142
    }
144,624✔
143
    bool operator!=(const IVTable& other) const
144
    {
144,624✔
145
        return !(*this == other);
144,624✔
146
    }
144,624✔
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,604,216✔
176
    REALM_ASSERT_DEBUG(!int_cast_has_overflow<To>(from));
2,604,216✔
177
    return static_cast<To>(from);
2,604,216✔
178
}
2,604,216✔
179

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

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

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

199
// Number of metadata blocks required to store `size` bytes
200
constexpr size_t block_count(SizeType data_size) noexcept
201
{
36,471✔
202
    return (page_count(data_size) + pages_per_block - 1) / pages_per_block;
36,471✔
203
}
36,471✔
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
{
502,377✔
208
    REALM_ASSERT(data_pos >= 0);
502,377✔
209
    return data_pos + (block_index(data_pos) + 1) * encryption_page_size;
502,377✔
210
}
502,377✔
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,715✔
215
    REALM_ASSERT(file_pos >= 0);
2,715✔
216
    const size_t metadata_page_count = (page_index(file_pos) + pages_per_block) / (pages_per_block + 1);
2,715✔
217
    return file_pos - metadata_page_count * encryption_page_size;
2,715✔
218
}
2,715✔
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,099✔
223
    REALM_ASSERT(data_pos >= 0);
186,099✔
224
    const size_t index = page_index(data_pos);
186,099✔
225
    const size_t metadata_block = block_index(data_pos);
186,099✔
226
    const size_t metadata_index = index & (pages_per_block - 1);
186,099✔
227
    return metadata_block * (pages_per_block + 1) * encryption_page_size + metadata_index * metadata_size;
186,099✔
228
}
186,099✔
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,497✔
233
    REALM_ASSERT(data_pos >= 0);
151,497✔
234
    return block_index(data_pos) * (pages_per_block + 1) * encryption_page_size;
151,497✔
235
}
151,497✔
236

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

242
// not actually checked any more
243
size_t check_read(FileDesc fd, SizeType pos, void* dst)
244
{
467,361✔
245
    return File::read_static(fd, pos, static_cast<char*>(dst), encryption_page_size);
467,361✔
246
}
467,361✔
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
{
720✔
255
    return {{ptr[I]...}};
720✔
256
}
720✔
257
template <class T, size_t N>
258
constexpr auto to_array(const T* ptr)
259
{
720✔
260
    return to_array_impl<T, N>(ptr, std::make_index_sequence<N>{});
720✔
261
}
720✔
262

263
void memcpy_if_changed(void* dst, const void* src, size_t n)
264
{
465,324✔
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);
465,324✔
281
#endif
465,324✔
282
}
465,324✔
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)))
369✔
288
    , m_rw_buffer(new char[encryption_page_size])
369✔
289
    , m_dst_buffer(new char[encryption_page_size])
369✔
290
{
720✔
291
#if REALM_PLATFORM_APPLE
351✔
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];
351✔
296
    arc4random_buf(u_iv, kCCKeySizeAES256);
351✔
297
    void* iv = u_iv;
351✔
298
    CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES, 0 /* options */, key, kCCKeySizeAES256, iv, &m_encr);
351✔
299
    CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES, 0 /* options */, key, kCCKeySizeAES256, iv, &m_decr);
351✔
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();
369✔
314
    if (!m_ctx)
369✔
315
        handle_error();
316
#endif
369✔
317
}
720✔
318

319
AESCryptor::~AESCryptor() noexcept
320
{
720✔
321
#if REALM_PLATFORM_APPLE
351✔
322
    CCCryptorRelease(m_encr);
351✔
323
    CCCryptorRelease(m_decr);
351✔
324
#elif defined(_WIN32)
325
#else
326
    EVP_CIPHER_CTX_cleanup(m_ctx);
369✔
327
    EVP_CIPHER_CTX_free(m_ctx);
369✔
328
#endif
369✔
329
}
720✔
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,471✔
338
    REALM_ASSERT(new_data_size >= 0);
36,471✔
339
    m_iv_buffer.reserve(iv_table_size(new_data_size));
36,471✔
340
    m_iv_buffer_cache.reserve(m_iv_buffer.capacity());
36,471✔
341
    m_iv_blocks_read.resize(m_iv_buffer.capacity() / 64);
36,471✔
342
}
36,471✔
343

344
IVTable& AESCryptor::get_iv_table(FileDesc fd, SizeType data_pos, IVLookupMode mode) noexcept
345
{
528,183✔
346
    size_t idx = page_index(data_pos);
528,183✔
347
    REALM_ASSERT(idx < m_iv_buffer.capacity()); // required space should have been preallocated
528,183✔
348
    if (mode != IVLookupMode::UseCache || idx >= m_iv_buffer.size() || !m_iv_blocks_read[block_index(data_pos)]) {
528,183✔
349
        read_iv_block(fd, data_pos);
14,235✔
350
    }
14,235✔
351
    m_iv_buffer_cache[idx] = m_iv_buffer[idx];
528,183✔
352
    return m_iv_buffer[idx];
528,183✔
353
}
528,183✔
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,497✔
360
    size_t idx = block_index(data_pos) * pages_per_block;
151,497✔
361
    if (idx + pages_per_block > m_iv_buffer.size()) {
151,497✔
362
        m_iv_buffer.resize(idx + pages_per_block);
1,371✔
363
        m_iv_buffer_cache.resize(m_iv_buffer.size());
1,371✔
364
    }
1,371✔
365
    SizeType iv_pos = iv_table_block_pos(data_pos);
151,497✔
366
    check_read(fd, iv_pos, &m_iv_buffer[idx]);
151,497✔
367
    m_iv_blocks_read[block_index(data_pos)] = true;
151,497✔
368
}
151,497✔
369

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

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

385
bool AESCryptor::refresh_iv(FileDesc fd, size_t page_ndx)
386
{
144,624✔
387
    REALM_ASSERT(page_ndx < m_iv_buffer.capacity());
144,624✔
388
    if (page_ndx >= m_iv_buffer.size() || !m_iv_blocks_read[page_ndx / pages_per_block]) {
144,624✔
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,624✔
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,490✔
397
}
144,624✔
398

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

404
AESCryptor::ReadResult AESCryptor::read(FileDesc fd, SizeType pos, char* dst, WriteObserver* observer)
405
{
336,561✔
406
    uint32_t iv = 0;
336,561✔
407
    Hmac hmac;
336,561✔
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,561✔
411
        return attempt_read(fd, pos, dst, IVLookupMode::UseCache, iv, hmac);
335,151✔
412
    }
335,151✔
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,410✔
421
    std::pair<uint32_t, Hmac> last_iv_and_data_hash;
1,410✔
422
    auto retry_start_time = std::chrono::steady_clock::now();
1,410✔
423
    size_t num_identical_reads = 1;
1,410✔
424
    ReadResult result = ReadResult::Success;
1,410✔
425
    while (retry_count <= 5 || (retry_count - num_identical_reads > 1 && retry_count < 20)) {
6,936✔
426
        result =
6,936✔
427
            attempt_read(fd, pos, dst, retry_count == 0 ? IVLookupMode::UseCache : IVLookupMode::Refetch, iv, hmac);
6,936✔
428
        switch (result) {
6,936✔
429
            case ReadResult::Success:
1,350✔
430
            case ReadResult::Eof:
1,350✔
431
            case ReadResult::Uninitialized:
1,362✔
432
                // Consistent and valid states that may or may not actually have data
433
                return result;
1,362✔
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,936✔
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,410✔
471

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

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

487
    calculate_hmac(hmac);
315,846✔
488
    if (!constant_time_equals(hmac, iv.hmac1)) {
315,846✔
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));
310,017✔
528
    memcpy_if_changed(dst, m_dst_buffer.get(), encryption_page_size);
310,017✔
529
    return ReadResult::Success;
310,017✔
530
}
315,846✔
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,099✔
569
    IVTable& iv = get_iv_table(fd, pos);
186,099✔
570

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

578
        crypt(mode_Encrypt, pos, m_rw_buffer.get(), src, reinterpret_cast<const char*>(&iv.iv1));
186,171✔
579
        hmac_sha224(Span(reinterpret_cast<uint8_t*>(m_rw_buffer.get()), encryption_page_size), iv.hmac1,
186,171✔
580
                    Span(m_key).sub_span<32>());
186,171✔
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,171✔
585

586
    if (marker)
186,099✔
587
        marker->mark(pos);
1,278✔
588
    File::write_static(fd, iv_table_pos(pos), reinterpret_cast<const char*>(&iv), sizeof(iv));
186,099✔
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,099✔
593
    if (marker)
186,099✔
594
        marker->unmark();
1,278✔
595
    m_iv_buffer_cache[page_index(pos)] = iv;
186,099✔
596
}
186,099✔
597

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

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

608
    size_t bytesEncrypted = 0;
265,551✔
609
    CCCryptorStatus err =
265,551✔
610
        CCCryptorUpdate(cryptor, src, encryption_page_size, dst, encryption_page_size, &bytesEncrypted);
265,551✔
611
    REALM_ASSERT(err == kCCSuccess);
265,551✔
612
    REALM_ASSERT(bytesEncrypted == encryption_page_size);
265,551✔
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,637✔
635
        handle_error();
636

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

641
    if (!EVP_CipherUpdate(m_ctx, reinterpret_cast<uint8_t*>(dst), &len, reinterpret_cast<const uint8_t*>(src),
230,637✔
642
                          encryption_page_size))
230,637✔
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,637✔
647
        handle_error();
648
#endif
230,637✔
649
}
496,188✔
650

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

657
std::unique_ptr<EncryptedFileMapping> EncryptedFile::add_mapping(SizeType file_offset, void* addr, size_t size,
658
                                                                 File::AccessMode access)
659
{
36,012✔
660
    auto mapping = std::make_unique<EncryptedFileMapping>(*this, file_offset, addr, size, access);
36,012✔
661
    CheckedLockGuard lock(mutex);
36,012✔
662
    mappings.push_back(mapping.get());
36,012✔
663
    return mapping;
36,012✔
664
}
36,012✔
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,731✔
670
    , m_access(access)
10,731✔
671
    , m_observer(observer)
10,731✔
672
    , m_marker(marker)
10,731✔
673
#ifdef REALM_DEBUG
674
    , m_validate_buffer(new char[encryption_page_size])
10,731✔
675
#endif
676
{
36,012✔
677
    set(addr, size, file_offset); // throws
36,012✔
678
}
36,012✔
679

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

690
    auto it = std::find(m_file.mappings.begin(), m_file.mappings.end(), this);
36,012✔
691
    REALM_ASSERT(it != m_file.mappings.end());
36,012✔
692
    if (it != m_file.mappings.end()) {
36,012✔
693
        m_file.mappings.erase(it);
36,012✔
694
    }
36,012✔
695
}
36,012✔
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,018,671✔
705
    REALM_ASSERT_EX(addr >= m_addr, addr, m_addr);
16,018,671✔
706
    return (reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(m_addr) + offset) / encryption_page_size;
16,018,671✔
707
}
16,018,671✔
708

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

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

720
SizeType EncryptedFileMapping::page_pos(size_t local_ndx) const noexcept
721
{
518,388✔
722
    return SizeType(local_ndx + m_first_page) * encryption_page_size;
518,388✔
723
}
518,388✔
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,562✔
730
    REALM_ASSERT_EX(local_ndx < m_page_state.size(), local_ndx, m_page_state.size());
227,562✔
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,562✔
734
    size_t ndx_in_file = local_ndx + m_first_page;
227,562✔
735
    for (auto& m : m_file.mappings) {
236,571✔
736
        m->assert_locked();
236,571✔
737
        if (m == this || !m->contains_page(ndx_in_file))
236,571✔
738
            continue;
220,152✔
739

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

745
        memcpy_if_changed(page_addr(local_ndx), m->page_addr(other_mapping_ndx), encryption_page_size);
8,496✔
746
        set(m_page_state[local_ndx], UpToDate);
8,496✔
747
        clear(m_page_state[local_ndx], StaleIV);
8,496✔
748
        return true;
8,496✔
749
    }
16,419✔
750
    return false;
219,066✔
751
}
227,562✔
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
{
219,066✔
759
    if (is_not(m_page_state[local_ndx], StaleIV))
219,066✔
760
        return false;
82,122✔
761
    size_t ndx_in_file = local_ndx + m_first_page;
136,944✔
762
    bool did_change = m_file.cryptor.refresh_iv(m_file.fd, ndx_in_file);
136,944✔
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,944✔
768
        m->assert_locked();
136,944✔
769
        if (!m->contains_page(ndx_in_file))
136,944✔
UNCOV
770
            continue;
×
771
        auto& state = m->m_page_state[ndx_in_file - m->m_first_page];
136,944✔
772
        if (is(state, StaleIV)) {
136,944✔
773
            REALM_ASSERT(is_not(state, UpToDate));
136,944✔
774
            clear(state, StaleIV);
136,944✔
775
            if (!did_change)
136,944✔
776
                set(state, UpToDate);
6,114✔
777
        }
136,944✔
778
    }
136,944✔
779
    return !did_change;
136,944✔
780
}
219,066✔
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,562✔
792
    REALM_ASSERT_EX(local_ndx < m_page_state.size(), local_ndx, m_page_state.size());
227,562✔
793
    REALM_ASSERT(is_not(m_page_state[local_ndx], Dirty));
227,562✔
794
    REALM_ASSERT(is_not(m_page_state[local_ndx], Writable));
227,562✔
795
    if (copy_up_to_date_page(local_ndx) || check_possibly_stale_page(local_ndx)) {
227,562✔
796
        return;
14,610✔
797
    }
14,610✔
798

799
    char* addr = page_addr(local_ndx);
212,952✔
800
    switch (m_file.cryptor.read(m_file.fd, page_pos(local_ndx), addr, m_observer)) {
212,952✔
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,223✔
806
            if (!to_modify)
26,223✔
807
                throw_decryption_error(local_ndx, "has never been written to");
402✔
808
            break;
26,223✔
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,456✔
819
            break;
186,456✔
820
    }
212,952✔
821
    set(m_page_state[local_ndx], UpToDate);
212,466✔
822
}
212,466✔
823

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

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

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

846
void EncryptedFileMapping::write_and_update_all(size_t local_ndx, uint16_t offset, uint16_t size) noexcept
847
{
2,356,629✔
848
    REALM_ASSERT(is(m_page_state[local_ndx], Writable));
2,356,629✔
849
    REALM_ASSERT(is(m_page_state[local_ndx], UpToDate));
2,356,629✔
850
    REALM_ASSERT(is_not(m_page_state[local_ndx], StaleIV));
2,356,629✔
851
    REALM_ASSERT(offset + size <= encryption_page_size);
2,356,629✔
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,629✔
854
    for (auto& m : m_file.mappings) {
2,595,177✔
855
        m->assert_locked();
2,595,177✔
856
        if (m == this || !m->contains_page(ndx_in_file))
2,595,177✔
857
            continue;
2,361,702✔
858

859
        size_t other_local_ndx = ndx_in_file - m->m_first_page;
233,475✔
860
        auto& state = m->m_page_state[other_local_ndx];
233,475✔
861
        if (is(state, UpToDate)) {
233,475✔
862
            memcpy_if_changed(m->page_addr(other_local_ndx) + offset, page_addr(local_ndx) + offset, size);
146,694✔
863
        }
146,694✔
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,781✔
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,475✔
873
    set(m_page_state[local_ndx], Dirty);
2,356,629✔
874
    clear(m_page_state[local_ndx], Writable);
2,356,629✔
875
}
2,356,629✔
876

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

884
    switch (m_file.cryptor.read(m_file.fd, page_pos(local_ndx), m_validate_buffer.get(), m_observer)) {
122,823✔
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,823✔
893
            break;
122,823✔
894
    }
122,823✔
895

896
    const size_t ndx_in_file = local_ndx + m_first_page;
122,823✔
897
    for (auto& m : m_file.mappings) {
136,473✔
898
        m->assert_locked();
136,473✔
899
        size_t other_local_ndx = ndx_in_file - m->m_first_page;
136,473✔
900
        if (m != this && m->contains_page(ndx_in_file) && is(m->m_page_state[other_local_ndx], Dirty)) {
136,473✔
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
    }
136,473✔
905

906
    if (memcmp(m_validate_buffer.get(), page_addr(local_ndx), encryption_page_size) != 0) {
122,823✔
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,823✔
915

916
void EncryptedFileMapping::validate() noexcept
917
{
116,658✔
918
#ifdef REALM_DEBUG
116,658✔
919
    for (size_t i = 0; i < m_page_state.size(); ++i)
8,275,047✔
920
        validate_page(i);
8,158,389✔
921
#endif
116,658✔
922
}
116,658✔
923

924
void EncryptedFileMapping::do_flush(bool skip_validate) noexcept
925
{
195,312✔
926
    for (size_t i = 0; i < m_page_state.size(); ++i) {
36,544,617✔
927
        if (is_not(m_page_state[i], Dirty)) {
36,349,305✔
928
            if (!skip_validate) {
36,166,707✔
929
                validate_page(i);
8,054,430✔
930
            }
8,054,430✔
931
            continue;
36,166,707✔
932
        }
36,166,707✔
933
        m_file.cryptor.write(m_file.fd, page_pos(i), page_addr(i), m_marker);
182,598✔
934
        clear(m_page_state[i], Dirty);
182,598✔
935
    }
182,598✔
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,312✔
940
        validate();
116,658✔
941
    }
116,658✔
942
}
195,312✔
943

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

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

956
#ifdef _MSC_VER
957
#pragma warning(disable : 4297) // throw in noexcept
958
#endif
959
void EncryptedFileMapping::do_sync() noexcept
960
{
1,356✔
961
    do_flush();
1,356✔
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,356✔
969
#endif
1,356✔
970
}
1,356✔
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,965✔
977
    CheckedLockGuard lock(m_file.mutex);
2,347,965✔
978
    REALM_ASSERT(size > 0);
2,347,965✔
979
    REALM_ASSERT(m_access == File::access_ReadWrite);
2,347,965✔
980

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

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

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

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

1020
void EncryptedFileMapping::set(void* new_addr, size_t new_size, SizeType new_file_offset)
1021
{
36,012✔
1022
    CheckedLockGuard lock(m_file.mutex);
36,012✔
1023
    REALM_ASSERT(new_file_offset % encryption_page_size == 0);
36,012✔
1024
    REALM_ASSERT(new_size % encryption_page_size == 0);
36,012✔
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));
36,012✔
1031

1032
    do_flush();
36,012✔
1033
    m_addr = new_addr;
36,012✔
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);
36,012✔
1037
    m_page_state.clear();
36,012✔
1038
    m_page_state.resize(new_size / encryption_page_size, PageState::Clean);
36,012✔
1039
}
36,012✔
1040

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

1046
SizeType data_size_to_encrypted_size(SizeType size) noexcept
1047
{
414✔
1048
    SizeType r = size % encryption_page_size;
414✔
1049
    size += r ? encryption_page_size - r : 0;
414✔
1050
    return data_pos_to_file_pos(size);
414✔
1051
}
414✔
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