Skip to content

Commit 7587121

Browse files
committed
timeline: sanitize usage of meta in the TimelineInnerStateTransaction
Before this patch, the meta field would be mutated, even when the transaction would be aborted. This changes the update scheme to meta with the following: - when creating the transaction, clone the meta (but keep the pointer location to the previous one) - all the transaction's methods operate on the WIP meta - when committing, replace the previous meta with the current one
1 parent 4661ca8 commit 7587121

File tree

5 files changed

+33
-18
lines changed

5 files changed

+33
-18
lines changed

crates/matrix-sdk-ui/src/timeline/event_handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ impl<'a, 'o> TimelineEventHandler<'a, 'o> {
249249
state: &'a mut TimelineInnerStateTransaction<'o>,
250250
ctx: TimelineEventContext,
251251
) -> Self {
252-
let TimelineInnerStateTransaction { items, meta } = state;
252+
let TimelineInnerStateTransaction { items, meta, .. } = state;
253253
Self { items, meta, ctx, result: HandleEventResult::default() }
254254
}
255255

crates/matrix-sdk-ui/src/timeline/inner/state.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,8 @@ impl TimelineInnerState {
407407

408408
fn transaction(&mut self) -> TimelineInnerStateTransaction<'_> {
409409
let items = ManuallyDrop::new(self.items.transaction());
410-
TimelineInnerStateTransaction { items, meta: &mut self.meta }
410+
let meta = ManuallyDrop::new(self.meta.clone());
411+
TimelineInnerStateTransaction { items, previous_meta: &mut self.meta, meta }
411412
}
412413
}
413414

@@ -427,7 +428,14 @@ impl DerefMut for TimelineInnerState {
427428

428429
pub(in crate::timeline) struct TimelineInnerStateTransaction<'a> {
429430
pub items: ManuallyDrop<ObservableVectorTransaction<'a, Arc<TimelineItem>>>,
430-
pub meta: &'a mut TimelineInnerMetadata,
431+
432+
/// A clone of the previous meta, that we're operating on during the
433+
/// transaction, and that will be committed to the previous meta location in
434+
/// [`Self::commit`].
435+
pub meta: ManuallyDrop<TimelineInnerMetadata>,
436+
437+
/// Pointer to the previous meta, only used during [`Self::commit`].
438+
previous_meta: &'a mut TimelineInnerMetadata,
431439
}
432440

433441
impl TimelineInnerStateTransaction<'_> {
@@ -630,14 +638,17 @@ impl TimelineInnerStateTransaction<'_> {
630638
}
631639

632640
fn commit(mut self) {
633-
let Self {
634-
items,
635-
// meta is just a reference, does not any dropping
636-
meta: _,
637-
} = &mut self;
641+
let Self { items, previous_meta, meta } = &mut self;
642+
643+
// Safety: self is forgotten to avoid double free from drop.
644+
let meta = unsafe { ManuallyDrop::take(meta) };
638645

639-
// Safety: self is forgotten to avoid double free from drop
646+
// Replace the pointer to the previous meta with the new one.
647+
**previous_meta = meta;
648+
649+
// Safety: self is forgotten to avoid double free from drop.
640650
let items = unsafe { ManuallyDrop::take(items) };
651+
641652
mem::forget(self);
642653

643654
items.commit();
@@ -701,7 +712,7 @@ impl Drop for TimelineInnerStateTransaction<'_> {
701712
}
702713
}
703714

704-
#[derive(Debug)]
715+
#[derive(Clone, Debug)]
705716
pub(in crate::timeline) struct TimelineInnerMetadata {
706717
/// List of all the events as received in the timeline, even the ones that
707718
/// are discarded in the timeline items.

crates/matrix-sdk-ui/src/timeline/polls.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl From<PollState> for NewUnstablePollStartEventContent {
141141

142142
/// Acts as a cache for poll response and poll end events handled before their
143143
/// start event has been handled.
144-
#[derive(Debug, Default)]
144+
#[derive(Clone, Debug, Default)]
145145
pub(super) struct PollPendingEvents {
146146
pub(super) pending_poll_responses: HashMap<OwnedEventId, Vec<ResponseData>>,
147147
pub(super) pending_poll_ends: HashMap<OwnedEventId, MilliSecondsSinceUnixEpoch>,

crates/matrix-sdk-ui/src/timeline/reactions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct ReactionSenderData {
3333
pub timestamp: MilliSecondsSinceUnixEpoch,
3434
}
3535

36-
#[derive(Debug, Default)]
36+
#[derive(Clone, Debug, Default)]
3737
pub(super) struct Reactions {
3838
/// Reaction event / txn ID => sender and reaction data.
3939
pub(super) map: HashMap<EventItemIdentifier, (ReactionSenderData, Annotation)>,

crates/matrix-sdk-ui/src/timeline/read_receipts.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -370,10 +370,11 @@ impl TimelineInnerStateTransaction<'_> {
370370
receipt: &receipt,
371371
};
372372

373-
self.meta.read_receipts.maybe_update_read_receipt(
373+
let meta = &mut *self.meta;
374+
meta.read_receipts.maybe_update_read_receipt(
374375
full_receipt,
375376
is_own_user_id,
376-
&self.meta.all_events,
377+
&meta.all_events,
377378
&mut self.items,
378379
);
379380
}
@@ -404,10 +405,12 @@ impl TimelineInnerStateTransaction<'_> {
404405
receipt_type: ReceiptType::Read,
405406
receipt: &receipt,
406407
};
407-
self.meta.read_receipts.maybe_update_read_receipt(
408+
409+
let meta = &mut *self.meta;
410+
meta.read_receipts.maybe_update_read_receipt(
408411
full_receipt,
409412
user_id == own_user_id,
410-
&self.meta.all_events,
413+
&meta.all_events,
411414
&mut self.items,
412415
);
413416
}
@@ -433,10 +436,11 @@ impl TimelineInnerStateTransaction<'_> {
433436
let full_receipt =
434437
FullReceipt { event_id, user_id, receipt_type: ReceiptType::Read, receipt: &receipt };
435438

436-
self.meta.read_receipts.maybe_update_read_receipt(
439+
let meta = &mut *self.meta;
440+
meta.read_receipts.maybe_update_read_receipt(
437441
full_receipt,
438442
is_own_event,
439-
&self.meta.all_events,
443+
&meta.all_events,
440444
&mut self.items,
441445
);
442446
}

0 commit comments

Comments
 (0)