Skip to content

Commit 293c5c2

Browse files
mikelehenpaulb777
authored andcommitted
Fix use-after-free in DocumentReference::AddSnapshotListener (#2686) (#2688)
1 parent c035147 commit 293c5c2

File tree

3 files changed

+39
-8
lines changed

3 files changed

+39
-8
lines changed

Firestore/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# Unreleased
2+
- [fixed] Fixed a use-after-free bug that could be observed when using snapshot
3+
listeners on temporary document references (#2682).
4+
5+
# 1.2.0
26
- [fixed] Fixed the way gRPC certificates are loaded on macOS (#2604).
37

48
# 1.1.0

Firestore/Example/Tests/Integration/API/FIRListenerRegistrationTests.mm

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,30 @@ - (void)testCanBeRemovedIndependently {
128128
[two remove];
129129
}
130130

131+
- (void)testCanOutliveDocumentReference {
132+
FIRCollectionReference *collectionRef = [self collectionRef];
133+
134+
XCTestExpectation *seen = [self expectationWithDescription:@"seen document"];
135+
136+
__block id<FIRListenerRegistration> registration;
137+
NSString *documentID;
138+
@autoreleasepool {
139+
FIRDocumentReference *docRef = [collectionRef documentWithAutoID];
140+
documentID = docRef.documentID;
141+
registration = [docRef addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
142+
if (snapshot.exists) {
143+
[seen fulfill];
144+
}
145+
}];
146+
docRef = nil;
147+
}
148+
149+
XCTAssertNotNil(registration);
150+
151+
FIRDocumentReference *docRef2 = [collectionRef documentWithPath:documentID];
152+
[self writeDocumentRef:docRef2 data:@{@"foo" : @"bar"}];
153+
154+
[registration remove];
155+
}
156+
131157
@end

Firestore/core/src/firebase/firestore/api/document_reference.mm

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,34 +202,35 @@ void Resolve(ListenerRegistration&& registration) {
202202
public:
203203
Converter(DocumentReference* parent,
204204
DocumentSnapshot::Listener&& user_listener)
205-
: parent_(parent), user_listener_(std::move(user_listener)) {
205+
: firestore_(parent->firestore_),
206+
key_(parent->key_),
207+
user_listener_(std::move(user_listener)) {
206208
}
207209

208210
void OnEvent(StatusOr<ViewSnapshot> maybe_snapshot) override {
209211
if (!maybe_snapshot.ok()) {
210212
user_listener_->OnEvent(maybe_snapshot.status());
211213
return;
212214
}
213-
Firestore* firestore = parent_->firestore_;
214-
DocumentKey key = parent_->key_;
215215

216216
ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie();
217217
HARD_ASSERT(snapshot.documents().size() <= 1,
218218
"Too many documents returned on a document query");
219-
FSTDocument* document = snapshot.documents().GetDocument(key);
219+
FSTDocument* document = snapshot.documents().GetDocument(key_);
220220

221221
bool has_pending_writes =
222-
document ? snapshot.mutated_keys().contains(key)
222+
document ? snapshot.mutated_keys().contains(key_)
223223
// We don't raise `has_pending_writes` for deleted documents.
224224
: false;
225225

226-
DocumentSnapshot result{firestore, std::move(key), document,
227-
snapshot.from_cache(), has_pending_writes};
226+
DocumentSnapshot result{firestore_, key_, document, snapshot.from_cache(),
227+
has_pending_writes};
228228
user_listener_->OnEvent(std::move(result));
229229
}
230230

231231
private:
232-
DocumentReference* parent_;
232+
Firestore* firestore_;
233+
DocumentKey key_;
233234
DocumentSnapshot::Listener user_listener_;
234235
};
235236
auto view_listener =

0 commit comments

Comments
 (0)