Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RNET-1141 multiprocess encryption for writers with different page sizes #7689

Closed
wants to merge 3 commits into from

Conversation

ironage
Copy link
Contributor

@ironage ironage commented May 10, 2024

Fixes realm/realm-dotnet#3592
A customer sent us an encrypted Realm that had an unusual data pattern in it:

IVs/metadata
blocks 1-5 [valid data]
block 6-8 [zeros] 
block 9-13 [valid data]
block 14-17 [zeros]

This caused a Decryption failed exception when trying to access ref 16384 (block 5), because we could only read 4096 bytes, but 16k was requested. (The file can be read by faking a page size of 4k, but here I have a 16k page size).

I was not able to come up with a test case to create a hole in the data like our customer had, but I can create a close scenario with zeros at the end, by careful writes from a db with 16k page size and a db with 4k page size (see tests).

The fix is to change from returning early to allowing a block with all zeros as a valid read. The assumption still being that these blocks can only be created by fallocate extending the file.

I don't know how we could end up with zero'd blocks as holes in the middle of the file unless something wacky is going on with our allocator, but the changes here at least do allow us to read Realms with this kind of data pattern.

☑️ ToDos

  • 📝 Changelog update
  • 🚦 Tests (or not relevant)
  • C-API, if public C++ API changed
  • bindgen/spec.yml, if public C++ API changed

@ironage ironage requested review from tgoyne and finnschiermer May 10, 2024 00:42
@ironage ironage self-assigned this May 10, 2024
@cla-bot cla-bot bot added the cla: yes label May 10, 2024
@ironage ironage force-pushed the js/encryption-upgrade-crash branch from e024f60 to a321722 Compare May 10, 2024 02:15
Copy link

coveralls-official bot commented May 10, 2024

Pull Request Test Coverage Report for Build james.stone_543

Details

  • 202 of 223 (90.58%) changed or added relevant lines in 3 files are covered.
  • 119 unchanged lines in 13 files lost coverage.
  • Overall coverage decreased (-0.03%) to 90.808%

Changes Missing Coverage Covered Lines Changed/Added Lines %
test/test_encrypted_file_mapping.cpp 173 194 89.18%
Files with Coverage Reduction New Missed Lines %
src/realm/sync/noinst/changeset_index.cpp 1 79.47%
src/realm/cluster.cpp 2 75.6%
src/realm/sync/network/http.hpp 2 82.27%
test/object-store/util/test_file.cpp 2 86.29%
test/test_sync.cpp 2 93.07%
src/realm/sync/noinst/protocol_codec.hpp 3 74.03%
src/realm/sync/transform.cpp 3 61.11%
src/realm/unicode.cpp 3 83.83%
src/realm/sync/noinst/server/server.cpp 5 73.6%
src/realm/util/encrypted_file_mapping.cpp 6 81.28%
Totals Coverage Status
Change from base Build 2306: -0.03%
Covered Lines: 214742
Relevant Lines: 236479

💛 - Coveralls

ironage added 3 commits May 10, 2024 13:58
without assuming that the page size is the same
across devices. In particular, we now count reading
blocks of 0's as a valid block rather than returning
early. This supports data patterns of zero'd blocks
between two valid data blocks instead of always assuming
that the zero'd blocks are at the end.
@ironage ironage force-pushed the js/encryption-upgrade-crash branch from a321722 to dbccedd Compare May 10, 2024 20:59
Copy link
Member

@tgoyne tgoyne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the file which prompted this, is the uninitialized page entirely within a block in the freelist?

Comment on lines +386 to +396
size_t i;
for (i = 0; i < block_size; ++i) {
if (m_rw_buffer[i] != 0) {
break;
}
}
if (i != block_size) {
// at least one byte wasn't zero
return false;
}
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
size_t i;
for (i = 0; i < block_size; ++i) {
if (m_rw_buffer[i] != 0) {
break;
}
}
if (i != block_size) {
// at least one byte wasn't zero
return false;
}
return true;
for (size_t i = 0; i < block_size; ++i) {
if (m_rw_buffer[i] != 0) {
return false;
}
}
return true;

Comment on lines +409 to +412
// space. The code using this doesn't rely on pre-allocated
// space being zeroed, but we memset anyways to encourage out of bounds
// reads to error out rather than reading stale data.
memset(dst, '\0', block_size);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't zero-fill specifically to avoid masking errors. This makes it so that valgrind etc. can no longer report reads of uninitialized values, and zero specifically has an unfortunate tendency to work by coincidence. In other spots here we fill with 0x55 to reduce the chance of uninitialized data being accidentally valid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

valid points, I'll change this and document it

@ironage
Copy link
Contributor Author

ironage commented May 14, 2024

Yes, good intuition! The middle block of zeros is indeed within a freeblock. Does that suggest something suspicious with the allocation pattern?

section refs
IVs/metadata N/A
blocks 1-5 [valid data] 0-20480
block 6-8 [zeros] 20480-32768
block 9-13 [valid data] 32768-53248
block 14-17 [zeros] 53248-69632
./src/realm/exec/realm-trawler -f ... 
File name: /Users/james.stone/Downloads/sensor_events.realm
Current top ref: 0x3a48
File format version: 22
File size: 65536
Logical file size: 64K
Current version: 5
Free list size: 13
Free space size: 35.7K
History type: InRealm
History schema version: 0
History size: 14K
File ident: 0
Free space:
3536 to 3568, length: 32, version: 3
3616 to 3656, length: 40, version: 3
7856 to 8056, length: 200, version: 4
8064 to 8104, length: 40, version: 4
8104 to 8144, length: 40, version: 3
8144 to 8192, length: 48, version: 5
9864 to 10816, length: 952, version: 3
10816 to 10872, length: 56, version: 5
10872 to 11064, length: 192, version: 4
14264 to 14456, length: 192, version: 5
14976 to 16384, length: 1408, version: 0
17992 to 32768, length: 14776, version: 0        <<< contains block 6-8 [zeros]  (20480-32768)
47000 to 65536, length: 18536, version: 0
Free space sizes:
    Size: 1408 count: 1
    Size: 14776 count: 1
    Size: 18536 count: 1
Pinned sizes:
    Size: 32 count: 1
    Size: 40 count: 3
    Size: 48 count: 1
    Size: 56 count: 1
    Size: 192 count: 2
    Size: 200 count: 1
    Size: 952 count: 1
Total free space size:  36512
Pinned free space size: 1792

@tgoyne
Copy link
Member

tgoyne commented May 15, 2024

It's just another thing that had to be true for this to be a file in a valid-but-weird state. It does seem like this file was the result of the allocator just never using that space for whatever reason and everything else behaved correctly. Digging into the allocator to figure out why it could have done this might be useful, but it's also entirely plausible that there's some reasonable explanation for its behavior rather than it being a weird lurking bug.

@tgoyne
Copy link
Member

tgoyne commented May 15, 2024

It currently doesn't work, but when it does I think #7698 will incidentally fix this by making us just not care about page size at all in the encryption code.

@ironage
Copy link
Contributor Author

ironage commented Jun 10, 2024

Superseded due to #7698

@ironage ironage closed this Jun 10, 2024
@ironage ironage deleted the js/encryption-upgrade-crash branch June 10, 2024 21:57
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 10, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.