Commit 41baf87e authored by Rakina Zata Amni's avatar Rakina Zata Amni Committed by Commit Bot

Fix crashes due to matching max codepoint by skipping invalid matches

We should check each ICU match to make sure they are valid (aren't
empty or start/end in non-offset-in-anchor positions).

Bug: 1028152
Change-Id: I1e99075a65e474cb7bf7fc14b08730f801df6541
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1939159
Commit-Queue: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#720425}
parent bd94f94a
...@@ -34,20 +34,27 @@ FindBuffer::Results::Results() { ...@@ -34,20 +34,27 @@ FindBuffer::Results::Results() {
empty_result_ = true; empty_result_ = true;
} }
FindBuffer::Results::Results(const Vector<UChar>& buffer, FindBuffer::Results::Results(const FindBuffer& find_buffer,
String search_text, TextSearcherICU* text_searcher,
const Vector<UChar>& buffer,
const String& search_text,
const blink::FindOptions options) { const blink::FindOptions options) {
// We need to own the |search_text| because |text_searcher_| only has a // We need to own the |search_text| because |text_searcher_| only has a
// StringView (doesn't own the search text). // StringView (doesn't own the search text).
search_text_ = search_text; search_text_ = search_text;
text_searcher_.SetPattern(search_text_, options); find_buffer_ = &find_buffer;
text_searcher_.SetText(buffer.data(), buffer.size()); text_searcher_ = text_searcher;
text_searcher_.SetOffset(0); text_searcher_->SetPattern(search_text_, options);
text_searcher_->SetText(buffer.data(), buffer.size());
text_searcher_->SetOffset(0);
} }
FindBuffer::Results::Iterator::Iterator(TextSearcherICU* text_searcher, FindBuffer::Results::Iterator::Iterator(const FindBuffer& find_buffer,
String search_text) TextSearcherICU* text_searcher,
: text_searcher_(text_searcher), has_match_(true) { const String& search_text)
: find_buffer_(&find_buffer),
text_searcher_(text_searcher),
has_match_(true) {
operator++(); operator++();
} }
...@@ -60,28 +67,50 @@ const FindBuffer::BufferMatchResult FindBuffer::Results::Iterator::operator*() ...@@ -60,28 +67,50 @@ const FindBuffer::BufferMatchResult FindBuffer::Results::Iterator::operator*()
void FindBuffer::Results::Iterator::operator++() { void FindBuffer::Results::Iterator::operator++() {
DCHECK(has_match_); DCHECK(has_match_);
has_match_ = text_searcher_->NextMatchResult(match_); has_match_ = text_searcher_->NextMatchResult(match_);
if (has_match_ && find_buffer_ && find_buffer_->IsInvalidMatch(match_))
operator++();
} }
FindBuffer::Results::Iterator FindBuffer::Results::begin() { bool FindBuffer::IsInvalidMatch(MatchResultICU match) const {
// Invalid matches are a result of accidentally matching elements that are
// replaced with the max code point, and may lead to crashes. To avoid
// crashing, we should skip the matches that are invalid - they would have
// either an empty position or a non-offset-in-anchor position.
const unsigned start_index = match.start;
PositionInFlatTree start_position =
PositionAtStartOfCharacterAtIndex(start_index);
if (start_position.IsNull() || !start_position.IsOffsetInAnchor())
return true;
const unsigned end_index = match.start + match.length;
DCHECK_LE(start_index, end_index);
PositionInFlatTree end_position =
PositionAtEndOfCharacterAtIndex(end_index - 1);
if (end_position.IsNull() || !end_position.IsOffsetInAnchor())
return true;
return false;
}
FindBuffer::Results::Iterator FindBuffer::Results::begin() const {
if (empty_result_) if (empty_result_)
return end(); return end();
text_searcher_.SetOffset(0); text_searcher_->SetOffset(0);
return Iterator(&text_searcher_, search_text_); return Iterator(*find_buffer_, text_searcher_, search_text_);
} }
FindBuffer::Results::Iterator FindBuffer::Results::end() const { FindBuffer::Results::Iterator FindBuffer::Results::end() const {
return Iterator(); return Iterator();
} }
bool FindBuffer::Results::IsEmpty() { bool FindBuffer::Results::IsEmpty() const {
return begin() == end(); return begin() == end();
} }
FindBuffer::BufferMatchResult FindBuffer::Results::front() { FindBuffer::BufferMatchResult FindBuffer::Results::front() const {
return *begin(); return *begin();
} }
FindBuffer::BufferMatchResult FindBuffer::Results::back() { FindBuffer::BufferMatchResult FindBuffer::Results::back() const {
Iterator last_result; Iterator last_result;
for (Iterator it = begin(); it != end(); ++it) { for (Iterator it = begin(); it != end(); ++it) {
last_result = it; last_result = it;
...@@ -89,7 +118,7 @@ FindBuffer::BufferMatchResult FindBuffer::Results::back() { ...@@ -89,7 +118,7 @@ FindBuffer::BufferMatchResult FindBuffer::Results::back() {
return *last_result; return *last_result;
} }
unsigned FindBuffer::Results::CountForTesting() { unsigned FindBuffer::Results::CountForTesting() const {
unsigned result = 0; unsigned result = 0;
for (Iterator it = begin(); it != end(); ++it) { for (Iterator it = begin(); it != end(); ++it) {
++result; ++result;
...@@ -205,15 +234,14 @@ EphemeralRangeInFlatTree FindBuffer::FindMatchInRange( ...@@ -205,15 +234,14 @@ EphemeralRangeInFlatTree FindBuffer::FindMatchInRange(
FindBuffer buffer( FindBuffer buffer(
EphemeralRangeInFlatTree(start_position, range.EndPosition())); EphemeralRangeInFlatTree(start_position, range.EndPosition()));
std::unique_ptr<Results> match_results = Results match_results = buffer.FindMatches(search_text, options);
buffer.FindMatches(search_text, options); if (!match_results.IsEmpty()) {
if (!match_results->IsEmpty()) {
if (!(options & kBackwards)) { if (!(options & kBackwards)) {
BufferMatchResult match = match_results->front(); BufferMatchResult match = match_results.front();
return buffer.RangeFromBufferIndex(match.start, return buffer.RangeFromBufferIndex(match.start,
match.start + match.length); match.start + match.length);
} }
BufferMatchResult match = match_results->back(); BufferMatchResult match = match_results.back();
last_match_range = last_match_range =
buffer.RangeFromBufferIndex(match.start, match.start + match.length); buffer.RangeFromBufferIndex(match.start, match.start + match.length);
} }
...@@ -222,19 +250,18 @@ EphemeralRangeInFlatTree FindBuffer::FindMatchInRange( ...@@ -222,19 +250,18 @@ EphemeralRangeInFlatTree FindBuffer::FindMatchInRange(
return last_match_range; return last_match_range;
} }
std::unique_ptr<FindBuffer::Results> FindBuffer::FindMatches( FindBuffer::Results FindBuffer::FindMatches(const WebString& search_text,
const WebString& search_text, const blink::FindOptions options) {
const blink::FindOptions options) const {
// We should return empty result if it's impossible to get a match (buffer is // We should return empty result if it's impossible to get a match (buffer is
// empty or too short), or when something went wrong in layout, in which case // empty or too short), or when something went wrong in layout, in which case
// |offset_mapping_| is null. // |offset_mapping_| is null.
if (buffer_.IsEmpty() || search_text.length() > buffer_.size() || if (buffer_.IsEmpty() || search_text.length() > buffer_.size() ||
!offset_mapping_) !offset_mapping_)
return std::make_unique<Results>(); return Results();
String search_text_16_bit = search_text; String search_text_16_bit = search_text;
search_text_16_bit.Ensure16Bit(); search_text_16_bit.Ensure16Bit();
FoldQuoteMarksAndSoftHyphens(search_text_16_bit); FoldQuoteMarksAndSoftHyphens(search_text_16_bit);
return std::make_unique<Results>(buffer_, search_text_16_bit, options); return Results(*this, &text_searcher_, buffer_, search_text_16_bit, options);
} }
bool FindBuffer::PushScopedForcedUpdateIfNeeded(const Element& element) { bool FindBuffer::PushScopedForcedUpdateIfNeeded(const Element& element) {
...@@ -388,7 +415,7 @@ EphemeralRangeInFlatTree FindBuffer::RangeFromBufferIndex( ...@@ -388,7 +415,7 @@ EphemeralRangeInFlatTree FindBuffer::RangeFromBufferIndex(
return EphemeralRangeInFlatTree(start_position, end_position); return EphemeralRangeInFlatTree(start_position, end_position);
} }
FindBuffer::BufferNodeMapping FindBuffer::MappingForIndex( const FindBuffer::BufferNodeMapping* FindBuffer::MappingForIndex(
unsigned index) const { unsigned index) const {
// Get the first entry that starts at a position higher than offset, and // Get the first entry that starts at a position higher than offset, and
// move back one entry. // move back one entry.
...@@ -397,27 +424,32 @@ FindBuffer::BufferNodeMapping FindBuffer::MappingForIndex( ...@@ -397,27 +424,32 @@ FindBuffer::BufferNodeMapping FindBuffer::MappingForIndex(
[](const unsigned offset, const BufferNodeMapping& entry) { [](const unsigned offset, const BufferNodeMapping& entry) {
return offset < entry.offset_in_buffer; return offset < entry.offset_in_buffer;
}); });
DCHECK_NE(it, buffer_node_mappings_.begin()); if (it == buffer_node_mappings_.begin())
auto* const entry = std::prev(it); return nullptr;
return *entry; auto* entry = std::prev(it);
return entry;
} }
PositionInFlatTree FindBuffer::PositionAtStartOfCharacterAtIndex( PositionInFlatTree FindBuffer::PositionAtStartOfCharacterAtIndex(
unsigned index) const { unsigned index) const {
DCHECK_LT(index, buffer_.size()); DCHECK_LT(index, buffer_.size());
DCHECK(offset_mapping_); DCHECK(offset_mapping_);
BufferNodeMapping entry = MappingForIndex(index); const BufferNodeMapping* entry = MappingForIndex(index);
if (!entry)
return PositionInFlatTree();
return ToPositionInFlatTree(offset_mapping_->GetLastPosition( return ToPositionInFlatTree(offset_mapping_->GetLastPosition(
index - entry.offset_in_buffer + entry.offset_in_mapping)); index - entry->offset_in_buffer + entry->offset_in_mapping));
} }
PositionInFlatTree FindBuffer::PositionAtEndOfCharacterAtIndex( PositionInFlatTree FindBuffer::PositionAtEndOfCharacterAtIndex(
unsigned index) const { unsigned index) const {
DCHECK_LT(index, buffer_.size()); DCHECK_LT(index, buffer_.size());
DCHECK(offset_mapping_); DCHECK(offset_mapping_);
BufferNodeMapping entry = MappingForIndex(index); const BufferNodeMapping* entry = MappingForIndex(index);
if (!entry)
return PositionInFlatTree();
return ToPositionInFlatTree(offset_mapping_->GetFirstPosition( return ToPositionInFlatTree(offset_mapping_->GetFirstPosition(
index - entry.offset_in_buffer + entry.offset_in_mapping + 1)); index - entry->offset_in_buffer + entry->offset_in_mapping + 1));
} }
void FindBuffer::AddTextToBuffer(const Text& text_node, void FindBuffer::AddTextToBuffer(const Text& text_node,
......
...@@ -49,18 +49,26 @@ class CORE_EXPORT FindBuffer { ...@@ -49,18 +49,26 @@ class CORE_EXPORT FindBuffer {
// All match results for this buffer. We can iterate through the // All match results for this buffer. We can iterate through the
// BufferMatchResults one by one using the Iterator. // BufferMatchResults one by one using the Iterator.
class CORE_EXPORT Results { class CORE_EXPORT Results {
STACK_ALLOCATED();
public: public:
Results(); Results();
Results(const Vector<UChar>& buffer, Results(const FindBuffer& find_buffer,
String search_text, TextSearcherICU* text_searcher,
const Vector<UChar>& buffer,
const String& search_text,
const blink::FindOptions options); const blink::FindOptions options);
class CORE_EXPORT Iterator class CORE_EXPORT Iterator
: public std::iterator<std::forward_iterator_tag, BufferMatchResult> { : public std::iterator<std::forward_iterator_tag, BufferMatchResult> {
STACK_ALLOCATED();
public: public:
Iterator() = default; Iterator() = default;
Iterator(TextSearcherICU* text_searcher, String search_text_); Iterator(const FindBuffer& find_buffer,
TextSearcherICU* text_searcher,
const String& search_text);
bool operator==(const Iterator& other) { bool operator==(const Iterator& other) {
return has_match_ == other.has_match_; return has_match_ == other.has_match_;
...@@ -75,32 +83,34 @@ class CORE_EXPORT FindBuffer { ...@@ -75,32 +83,34 @@ class CORE_EXPORT FindBuffer {
void operator++(); void operator++();
private: private:
const FindBuffer* find_buffer_;
TextSearcherICU* text_searcher_; TextSearcherICU* text_searcher_;
MatchResultICU match_; MatchResultICU match_;
bool has_match_ = false; bool has_match_ = false;
}; };
Iterator begin(); Iterator begin() const;
Iterator end() const; Iterator end() const;
bool IsEmpty(); bool IsEmpty() const;
BufferMatchResult front(); BufferMatchResult front() const;
BufferMatchResult back(); BufferMatchResult back() const;
unsigned CountForTesting(); unsigned CountForTesting() const;
private: private:
String search_text_; String search_text_;
TextSearcherICU text_searcher_; const FindBuffer* find_buffer_;
TextSearcherICU* text_searcher_;
bool empty_result_ = false; bool empty_result_ = false;
}; };
// Finds all the match for |search_text| in |buffer_|. // Finds all the match for |search_text| in |buffer_|.
std::unique_ptr<Results> FindMatches(const WebString& search_text, Results FindMatches(const WebString& search_text,
const blink::FindOptions options) const; const blink::FindOptions options);
// Gets a flat tree range corresponding to text in the [start_index, // Gets a flat tree range corresponding to text in the [start_index,
// end_index) of |buffer|. // end_index) of |buffer|.
...@@ -113,6 +123,8 @@ class CORE_EXPORT FindBuffer { ...@@ -113,6 +123,8 @@ class CORE_EXPORT FindBuffer {
return PositionInFlatTree::FirstPositionInNode(*node_after_block_); return PositionInFlatTree::FirstPositionInNode(*node_after_block_);
} }
bool IsInvalidMatch(MatchResultICU match) const;
private: private:
// Collects text for one LayoutBlockFlow located within |range| to |buffer_|, // Collects text for one LayoutBlockFlow located within |range| to |buffer_|,
// might be stopped without finishing one full LayoutBlockFlow if we // might be stopped without finishing one full LayoutBlockFlow if we
...@@ -164,7 +176,7 @@ class CORE_EXPORT FindBuffer { ...@@ -164,7 +176,7 @@ class CORE_EXPORT FindBuffer {
const unsigned offset_in_mapping; const unsigned offset_in_mapping;
}; };
BufferNodeMapping MappingForIndex(unsigned index) const; const BufferNodeMapping* MappingForIndex(unsigned index) const;
PositionInFlatTree PositionAtStartOfCharacterAtIndex(unsigned index) const; PositionInFlatTree PositionAtStartOfCharacterAtIndex(unsigned index) const;
...@@ -179,6 +191,7 @@ class CORE_EXPORT FindBuffer { ...@@ -179,6 +191,7 @@ class CORE_EXPORT FindBuffer {
Vector<UChar> buffer_; Vector<UChar> buffer_;
Vector<BufferNodeMapping> buffer_node_mappings_; Vector<BufferNodeMapping> buffer_node_mappings_;
Vector<DisplayLockContext::ScopedForcedUpdate> scoped_forced_update_list_; Vector<DisplayLockContext::ScopedForcedUpdate> scoped_forced_update_list_;
TextSearcherICU text_searcher_;
const NGOffsetMapping* offset_mapping_ = nullptr; const NGOffsetMapping* offset_mapping_ = nullptr;
}; };
......
...@@ -112,9 +112,9 @@ class FindTaskController::IdleFindTask ...@@ -112,9 +112,9 @@ class FindTaskController::IdleFindTask
while (search_start != search_end) { while (search_start != search_end) {
// Find in the whole block. // Find in the whole block.
FindBuffer buffer(EphemeralRangeInFlatTree(search_start, search_end)); FindBuffer buffer(EphemeralRangeInFlatTree(search_start, search_end));
std::unique_ptr<FindBuffer::Results> match_results = FindBuffer::Results match_results =
buffer.FindMatches(search_text_, find_options); buffer.FindMatches(search_text_, find_options);
for (FindBuffer::BufferMatchResult match : *match_results) { for (FindBuffer::BufferMatchResult match : match_results) {
const EphemeralRangeInFlatTree ephemeral_match_range = const EphemeralRangeInFlatTree ephemeral_match_range =
buffer.RangeFromBufferIndex(match.start, buffer.RangeFromBufferIndex(match.start,
match.start + match.length); match.start + match.length);
......
...@@ -104,11 +104,11 @@ EphemeralRangeInFlatTree FindImmediateMatch(String search_text, ...@@ -104,11 +104,11 @@ EphemeralRangeInFlatTree FindImmediateMatch(String search_text,
// TODO(nburris): FindBuffer will search the rest of the document for a match, // TODO(nburris): FindBuffer will search the rest of the document for a match,
// but we only need to check for an immediate match, so we should stop // but we only need to check for an immediate match, so we should stop
// searching if there's no immediate match. // searching if there's no immediate match.
std::unique_ptr<FindBuffer::Results> match_results = FindBuffer::Results match_results =
buffer.FindMatches(search_text, kCaseInsensitive); buffer.FindMatches(search_text, kCaseInsensitive);
if (!match_results->IsEmpty() && match_results->front().start == 0u) { if (!match_results.IsEmpty() && match_results.front().start == 0u) {
FindBuffer::BufferMatchResult buffer_match = match_results->front(); FindBuffer::BufferMatchResult buffer_match = match_results.front();
EphemeralRangeInFlatTree match = buffer.RangeFromBufferIndex( EphemeralRangeInFlatTree match = buffer.RangeFromBufferIndex(
buffer_match.start, buffer_match.start + buffer_match.length); buffer_match.start, buffer_match.start + buffer_match.length);
if (IsWholeWordMatch(match)) if (IsWholeWordMatch(match))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment