librbd/migration: prune snapshot extents in RawFormat::list_snaps()

list-snaps is exempt from clipping in ImageDispatcher::PreprocessVisitor
because it's considered to be an internal API.  Further, reads issued
by ObjectCopyRequest based on list-snaps results may also be exempt
because of READ_FLAG_DISABLE_CLIPPING.

Since RawFormat allows specifying a set of snapshots (possibly of
varying size!) to be imported, it needs to compensate for that in its
list-snaps implementation.  Otherwise, an out-of-bounds read will
eventually be submitted to the stream.

Fixes: https://tracker.ceph.com/issues/67845
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
Ilya Dryomov 2024-08-31 12:33:55 +02:00
parent 5d64c9c5cd
commit d9192b5aca
3 changed files with 9 additions and 3 deletions

View File

@ -27,7 +27,10 @@ expect_false() {
create_base_image() {
local image=$1
rbd create --size 1G ${image}
# size is not a multiple of object size to trigger an edge case in
# list-snaps
rbd create --size 1025M ${image}
rbd bench --io-type write --io-pattern rand --io-size=4K --io-total 256M ${image}
rbd snap create ${image}@1
rbd bench --io-type write --io-pattern rand --io-size=4K --io-total 64M ${image}

View File

@ -9,6 +9,7 @@
#include "librbd/Utils.h"
#include "librbd/io/AioCompletion.h"
#include "librbd/io/ReadResult.h"
#include "librbd/io/Utils.h"
#include "librbd/migration/SnapshotInterface.h"
#include "librbd/migration/SourceSpecBuilder.h"
#include "librbd/migration/Utils.h"
@ -209,7 +210,9 @@ void RawFormat<I>::list_snaps(io::Extents&& image_extents,
&previous_size, &sparse_extents);
// build set of data/zeroed extents for the current snapshot
snapshot->list_snap(io::Extents{image_extents}, list_snaps_flags,
auto snapshot_extents = image_extents;
io::util::prune_extents(snapshot_extents, snap_info.size);
snapshot->list_snap(std::move(snapshot_extents), list_snaps_flags,
&sparse_extents, parent_trace, gather_ctx->new_sub());
}

View File

@ -466,7 +466,7 @@ TEST_F(TestMockMigrationRawFormat, ListSnapsMerge) {
expect_snapshot_get_info(*mock_snapshot_interface_2, snap_info_2);
io::SparseExtents sparse_extents_2;
sparse_extents_2.insert(0, 32, {io::SPARSE_EXTENT_STATE_DATA, 32});
expect_snapshot_list_snap(*mock_snapshot_interface_2, {{0, 123}},
expect_snapshot_list_snap(*mock_snapshot_interface_2, {{0, 64}},
sparse_extents_2, 0);
expect_snapshot_get_info(*mock_snapshot_interface_head, snap_info_head);