Skip to content

Commit 9aaa3d2

Browse files
authored
Avoid deeply nested timestamps that can cause stack overflow. (#10916)
1 parent 7a95f0b commit 9aaa3d2

File tree

3 files changed

+36
-0
lines changed

3 files changed

+36
-0
lines changed

Firestore/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# 10.7.0
22
- [feature] Add support for disjunctions in queries (`OR` queries).
3+
- [fixed] Fixed stack overflow caused by deeply nested server timestamps.
34

45
# 10.6.0
56
- [fixed] Fix a potential high memory usage issue.

Firestore/core/src/model/server_timestamp_util.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ const char kServerTimestampSentinel[] = "server_timestamp";
3535
Message<google_firestore_v1_Value> EncodeServerTimestamp(
3636
const Timestamp& local_write_time,
3737
absl::optional<google_firestore_v1_Value> previous_value) {
38+
// We should avoid storing deeply nested server timestamp map values
39+
// because we never use the intermediate "previous values".
40+
// For example:
41+
// previous: 42L, add: t1, result: t1 -> 42L
42+
// previous: t1, add: t2, result: t2 -> 42L (NOT t2 -> t1 -> 42L)
43+
// previous: t2, add: t3, result: t3 -> 42L (NOT t3 -> t2 -> t1 -> 42L)
44+
// `getPreviousValue` recursively traverses server timestamps to find the
45+
// least recent Value.
46+
if (previous_value.has_value() && IsServerTimestamp(*previous_value)) {
47+
previous_value = GetPreviousValue(*previous_value);
48+
}
3849
pb_size_t count = previous_value ? 3 : 2;
3950

4051
Message<google_firestore_v1_Value> result;

Firestore/core/test/unit/local/local_store_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "Firestore/core/test/unit/local/local_store_test.h"
1818

19+
#include <thread> // NOLINT(build/c++11)
1920
#include <unordered_map>
2021
#include <utility>
2122
#include <vector>
@@ -34,10 +35,14 @@
3435
#include "Firestore/core/src/model/document.h"
3536
#include "Firestore/core/src/model/document_key.h"
3637
#include "Firestore/core/src/model/field_index.h"
38+
#include "Firestore/core/src/model/field_mask.h"
39+
#include "Firestore/core/src/model/field_path.h"
40+
#include "Firestore/core/src/model/field_transform.h"
3741
#include "Firestore/core/src/model/mutable_document.h"
3842
#include "Firestore/core/src/model/mutation.h"
3943
#include "Firestore/core/src/model/mutation_batch_result.h"
4044
#include "Firestore/core/src/model/patch_mutation.h"
45+
#include "Firestore/core/src/model/server_timestamp_util.h"
4146
#include "Firestore/core/src/model/set_mutation.h"
4247
#include "Firestore/core/src/model/transform_operation.h"
4348
#include "Firestore/core/src/remote/existence_filter.h"
@@ -92,6 +97,7 @@ using testutil::Key;
9297
using testutil::Map;
9398
using testutil::OverlayTypeMap;
9499
using testutil::Query;
100+
using testutil::ServerTimestamp;
95101
using testutil::UnknownDoc;
96102
using testutil::UpdateRemoteEvent;
97103
using testutil::UpdateRemoteEventWithLimboTargets;
@@ -1684,6 +1690,24 @@ TEST_P(LocalStoreTest, PatchMutationLeadsToPatchOverlay) {
16841690
OverlayTypeMap({{Key("foo/baz"), model::Mutation::Type::Patch}}));
16851691
}
16861692

1693+
TEST_P(LocalStoreTest, DeeplyNestedTimestampDoesNotCauseStackOverflow) {
1694+
Timestamp timestamp = Timestamp::Now();
1695+
Message<_google_firestore_v1_Value> initialServerTimestamp =
1696+
model::EncodeServerTimestamp(timestamp, absl::nullopt);
1697+
model::FieldPath path = model::FieldPath::FromDotSeparatedString("timestamp");
1698+
auto makeDeeplyNestedTimestamp = [&]() {
1699+
for (int i = 0; i < 1000; ++i) {
1700+
WriteMutation(testutil::MergeMutation(
1701+
"foo/bar",
1702+
Map("timestamp",
1703+
model::EncodeServerTimestamp(timestamp, *initialServerTimestamp)),
1704+
{path}, {ServerTimestamp("timestamp")}));
1705+
}
1706+
};
1707+
std::thread t(makeDeeplyNestedTimestamp);
1708+
EXPECT_NO_FATAL_FAILURE(t.join());
1709+
}
1710+
16871711
} // namespace local
16881712
} // namespace firestore
16891713
} // namespace firebase

0 commit comments

Comments
 (0)