encrypted_file_test: Test reads beyond decrypted file length

Add a test to reproduce a bug in the read DMA API of
`encrypted_file_impl` (the file implementation for Encryption-at-Rest).

The test creates an encrypted file that contains padding, and then
attempts to read from an offset within the padding area. Although this
offset is invalid on the decrypted file, the `encrypted_file_impl` makes
no checks and proceeds with the decryption of padding data, which
eventually leads to bogus results.

Refs #22236.

Signed-off-by: Nikos Dragazis <nikolaos.dragazis@scylladb.com>
(cherry picked from commit 8f936b2cbc87c8bd6575458850290615d9eec18c)
This commit is contained in:
Nikos Dragazis
2025-01-16 12:45:28 +02:00
committed by Calle Wilund
parent e96cc52668
commit 2fb95e4e2f

View File

@@ -15,12 +15,14 @@
#include <seastar/core/seastar.hh>
#include <seastar/core/shared_ptr.hh>
#include <seastar/core/thread.hh>
#include <seastar/core/fstream.hh>
#include <seastar/testing/test_case.hh>
#include "ent/encryption/encryption.hh"
#include "ent/encryption/symmetric_key.hh"
#include "ent/encryption/encrypted_file_impl.hh"
#include "test/lib/log.hh"
#include "test/lib/tmpdir.hh"
#include "test/lib/random_utils.hh"
#include "test/lib/exception_utils.hh"
@@ -323,3 +325,60 @@ SEASTAR_TEST_CASE(test_truncating_extend) {
co_await f.close();
}
// Reproducer for https://github.com/scylladb/scylladb/issues/22236
SEASTAR_TEST_CASE(test_read_from_padding) {
key_info kinfo {"AES/CBC/PKCSPadding", 128};
shared_ptr<symmetric_key> k = make_shared<symmetric_key>(kinfo);
testlog.info("Created symmetric key: info={} key={} ", k->info(), k->key());
size_t block_size;
size_t buf_size;
constexpr auto& filename = "encrypted_file";
const auto& filepath = dir.path() / filename;
testlog.info("Creating encrypted file {}", filepath.string());
{
auto [file, _] = co_await make_file(filename, open_flags::create | open_flags::wo, k);
auto ostream = co_await make_file_output_stream(file);
block_size = file.disk_write_dma_alignment();
buf_size = block_size - 1;
auto wbuf = seastar::temporary_buffer<char>::aligned(file.memory_dma_alignment(), buf_size);
co_await ostream.write(wbuf.get(), wbuf.size());
testlog.info("Wrote {} bytes to encrypted file {}", wbuf.size(), filepath.string());
co_await ostream.close();
testlog.info("Length of {}: {} bytes", filename, co_await file.size());
}
testlog.info("Testing DMA reads from padding area of file {}", filepath.string());
{
auto [file, _] = co_await make_file(filename, open_flags::ro, k);
// Triggering the bug requires reading from the padding area:
// `buf_size < read_pos < file.size()`
//
// For `dma_read()`, we have the additional requirement that `read_pos` must be aligned.
// For `dma_read_bulk()`, it doesn't have to.
uint64_t read_pos = block_size;
size_t read_len = block_size;
auto rbuf = seastar::temporary_buffer<char>::aligned(file.memory_dma_alignment(), read_len);
std::vector<iovec> iov {{static_cast<void*>(rbuf.get_write()), rbuf.size()}};
auto res = co_await file.dma_read_bulk<char>(read_pos, read_len);
BOOST_CHECK_MESSAGE(res.size() == 0, seastar::format(
"Bulk DMA read on pos {}, len {}: returned {} bytes instead of zero", read_pos, read_len, res.size()));
auto res_len = co_await file.dma_read(read_pos, iov);
BOOST_CHECK_MESSAGE(res_len == 0, seastar::format(
"IOV DMA read on pos {}, len {}: returned {} bytes instead of zero", read_pos, read_len, res_len));
res_len = co_await file.dma_read<char>(read_pos, rbuf.get_write(), read_len);
BOOST_CHECK_MESSAGE(res_len == 0, seastar::format(
"DMA read on pos {}, len {}: returned {} bytes instead of zero", read_pos, read_len, res_len));
co_await file.close();
}
}