123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- // Copyright 2022 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ui/accessibility/ax_enums.mojom.h"
- #include "ui/accessibility/ax_node.h"
- #include "ui/accessibility/ax_node_data.h"
- #include "ui/accessibility/ax_node_position.h"
- #include "ui/accessibility/ax_position.h"
- #include "ui/accessibility/ax_range.h"
- #include "ui/accessibility/ax_role_properties.h"
- #include "ui/accessibility/ax_tree.h"
- #include "ui/accessibility/ax_tree_data.h"
- #include "ui/accessibility/ax_tree_fuzzer_util.h"
- #include "ui/accessibility/ax_tree_id.h"
- #include "ui/accessibility/ax_tree_update.h"
- #include "ui/accessibility/test_ax_tree_manager.h"
- // Max amount of fuzz data needed to create the next position
- const size_t kNextNodePositionMaxDataSize = 4;
- // Min/Max node size for generated tree.
- const size_t kMinNodeCount = 10;
- const size_t kMaxNodeCount = kMinNodeCount + 50;
- // Min fuzz data needed for fuzzer to function.
- // Tree of minimum size with text for each node + 2 positions.
- const size_t kMinFuzzDataSize =
- kMinNodeCount * AXTreeFuzzerGenerator::kMinimumNewNodeFuzzDataSize +
- kMinNodeCount * AXTreeFuzzerGenerator::kMinTextFuzzDataSize +
- 2 * kNextNodePositionMaxDataSize;
- // Cap fuzz data to avoid slowness.
- const size_t kMaxFuzzDataSize = 20000;
- using TestPositionType =
- std::unique_ptr<ui::AXPosition<ui::AXNodePosition, ui::AXNode>>;
- using TestPositionRange =
- ui::AXRange<ui::AXPosition<ui::AXNodePosition, ui::AXNode>>;
- // Helper to create positions in the given tree.
- class AXNodePositionFuzzerGenerator {
- public:
- AXNodePositionFuzzerGenerator(ui::AXTree* tree,
- ui::AXNodeID max_id,
- FuzzerData& fuzzer_data);
- TestPositionType CreateNewPosition();
- TestPositionType GenerateNextPosition(TestPositionType& current_position,
- TestPositionType& previous_position);
- static void CallPositionAPIs(TestPositionType& position,
- TestPositionType& other_position);
- private:
- static ax::mojom::MoveDirection GenerateMoveDirection(unsigned char byte);
- static ax::mojom::TextAffinity GenerateTextAffinity(unsigned char byte);
- static ui::AXPositionKind GeneratePositionKind(unsigned char byte);
- static ui::AXPositionAdjustmentBehavior GenerateAdjustmentBehavior(
- unsigned char byte);
- static ui::AXMovementOptions GenerateMovementOptions(
- unsigned char behavior_byte,
- unsigned char detection_byte);
- TestPositionType CreateNewPosition(ui::AXNodeID anchor_id,
- int child_index_or_text_offset,
- ui::AXPositionKind position_kind,
- ax::mojom::TextAffinity affinity);
- ui::AXTree* tree_;
- const ui::AXNodeID max_id_;
- FuzzerData& fuzzer_data_;
- };
- AXNodePositionFuzzerGenerator::AXNodePositionFuzzerGenerator(
- ui::AXTree* tree,
- ui::AXNodeID max_id,
- FuzzerData& fuzzer_data)
- : tree_(tree), max_id_(max_id), fuzzer_data_(fuzzer_data) {}
- TestPositionType AXNodePositionFuzzerGenerator::CreateNewPosition() {
- return CreateNewPosition(fuzzer_data_.NextByte(), fuzzer_data_.NextByte(),
- GeneratePositionKind(fuzzer_data_.NextByte()),
- GenerateTextAffinity(fuzzer_data_.NextByte()));
- }
- TestPositionType AXNodePositionFuzzerGenerator::CreateNewPosition(
- ui::AXNodeID anchor_id,
- int child_index_or_text_offset,
- ui::AXPositionKind position_kind,
- ax::mojom::TextAffinity affinity) {
- // To ensure that anchor_id is between |ui::kInvalidAXNodeID| and the max ID
- // of the tree (non-inclusive), get a number [0, max_id - 1) and then shift by
- // 1 to get [1, max_id)
- anchor_id = (anchor_id % (max_id_ - 1)) + 1;
- ui::AXNode* anchor = tree_->GetFromId(anchor_id);
- DCHECK(anchor);
- switch (position_kind) {
- case ui::AXPositionKind::TREE_POSITION:
- // Avoid division by zero in the case where the node has no children.
- child_index_or_text_offset =
- anchor->GetChildCount()
- ? child_index_or_text_offset % anchor->GetChildCount()
- : 0;
- return ui::AXNodePosition::CreateTreePosition(
- tree_->GetAXTreeID(), anchor_id, child_index_or_text_offset);
- case ui::AXPositionKind::TEXT_POSITION: {
- // Avoid division by zero in the case where the node has no text.
- child_index_or_text_offset =
- anchor->GetTextContentLengthUTF16()
- ? child_index_or_text_offset % anchor->GetTextContentLengthUTF16()
- : 0;
- return ui::AXNodePosition::CreateTextPosition(
- tree_->GetAXTreeID(), anchor_id, child_index_or_text_offset,
- affinity);
- case ui::AXPositionKind::NULL_POSITION:
- NOTREACHED();
- return ui::AXNodePosition::CreateNullPosition();
- }
- }
- }
- ax::mojom::MoveDirection AXNodePositionFuzzerGenerator::GenerateMoveDirection(
- unsigned char byte) {
- constexpr unsigned char max_value =
- static_cast<unsigned char>(ax::mojom::MoveDirection::kMaxValue);
- return static_cast<ax::mojom::MoveDirection>(byte % max_value);
- }
- ax::mojom::TextAffinity AXNodePositionFuzzerGenerator::GenerateTextAffinity(
- unsigned char byte) {
- constexpr unsigned char max_value =
- static_cast<unsigned char>(ax::mojom::TextAffinity::kMaxValue);
- return static_cast<ax::mojom::TextAffinity>(byte % max_value);
- }
- ui::AXPositionKind AXNodePositionFuzzerGenerator::GeneratePositionKind(
- unsigned char byte) {
- return byte % 2 ? ui::AXPositionKind::TREE_POSITION
- : ui::AXPositionKind::TEXT_POSITION;
- }
- ui::AXPositionAdjustmentBehavior
- AXNodePositionFuzzerGenerator::GenerateAdjustmentBehavior(unsigned char byte) {
- return byte % 2 ? ui::AXPositionAdjustmentBehavior::kMoveBackward
- : ui::AXPositionAdjustmentBehavior::kMoveForward;
- }
- ui::AXMovementOptions AXNodePositionFuzzerGenerator::GenerateMovementOptions(
- unsigned char behavior_byte,
- unsigned char detection_byte) {
- return ui::AXMovementOptions(
- static_cast<ui::AXBoundaryBehavior>(behavior_byte % 3),
- static_cast<ui::AXBoundaryDetection>(detection_byte % 3));
- }
- TestPositionType AXNodePositionFuzzerGenerator::GenerateNextPosition(
- TestPositionType& current_position,
- TestPositionType& previous_position) {
- switch (fuzzer_data_.NextByte() % 55) {
- case 0:
- default:
- return CreateNewPosition();
- case 1:
- return current_position->AsValidPosition();
- case 2:
- return current_position->AsTreePosition();
- case 3:
- return current_position->AsLeafTreePosition();
- case 4:
- return current_position->AsTextPosition();
- case 5:
- return current_position->AsLeafTextPosition();
- case 6:
- return current_position->AsDomSelectionPosition();
- case 7:
- return current_position->AsUnignoredPosition(
- GenerateAdjustmentBehavior(fuzzer_data_.NextByte()));
- case 8:
- return current_position->CreateAncestorPosition(
- previous_position->GetAnchor(),
- GenerateMoveDirection(fuzzer_data_.NextByte()));
- case 9:
- return current_position->CreatePositionAtStartOfAnchor();
- case 10:
- return current_position->CreatePositionAtEndOfAnchor();
- case 11:
- return current_position->CreatePositionAtStartOfAXTree();
- case 12:
- return current_position->CreatePositionAtEndOfAXTree();
- case 13:
- return current_position->CreatePositionAtStartOfContent();
- case 14:
- return current_position->CreatePositionAtEndOfContent();
- case 15:
- return current_position->CreateChildPositionAt(fuzzer_data_.NextByte() %
- 10);
- case 16:
- return current_position->CreateParentPosition(
- GenerateMoveDirection(fuzzer_data_.NextByte()));
- case 17:
- return current_position->CreateNextLeafTreePosition();
- case 18:
- return current_position->CreatePreviousLeafTreePosition();
- case 19:
- return current_position->CreateNextLeafTextPosition();
- case 20:
- return current_position->CreatePreviousLeafTextPosition();
- case 21:
- return current_position->AsLeafTextPositionBeforeCharacter();
- case 22:
- return current_position->AsLeafTextPositionAfterCharacter();
- case 23:
- return current_position->CreatePreviousCharacterPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 24:
- return current_position->CreateNextWordStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 25:
- return current_position->CreatePreviousWordStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 26:
- return current_position->CreateNextWordEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 27:
- return current_position->CreatePreviousWordEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 28:
- return current_position->CreateNextLineStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 29:
- return current_position->CreatePreviousLineStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 30:
- return current_position->CreateNextLineEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 31:
- return current_position->CreatePreviousLineEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 32:
- return current_position->CreateNextFormatStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 33:
- return current_position->CreatePreviousFormatStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 34:
- return current_position->CreateNextFormatEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 35:
- return current_position->CreatePreviousFormatEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 36:
- return current_position->CreateNextSentenceStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 37:
- return current_position->CreatePreviousSentenceStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 38:
- return current_position->CreateNextSentenceEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 39:
- return current_position->CreatePreviousSentenceEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 40:
- return current_position->CreateNextParagraphStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 41:
- return current_position
- ->CreateNextParagraphStartPositionSkippingEmptyParagraphs(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 42:
- return current_position->CreatePreviousParagraphStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 43:
- return current_position
- ->CreatePreviousParagraphStartPositionSkippingEmptyParagraphs(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 44:
- return current_position->CreateNextParagraphEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 45:
- return current_position->CreatePreviousParagraphEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 46:
- return current_position->CreateNextPageStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 47:
- return current_position->CreatePreviousPageStartPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 48:
- return current_position->CreateNextPageEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 49:
- return current_position->CreatePreviousPageEndPosition(
- GenerateMovementOptions(fuzzer_data_.NextByte(),
- fuzzer_data_.NextByte()));
- case 52:
- return current_position->CreateNextAnchorPosition();
- case 53:
- return current_position->CreatePreviousAnchorPosition();
- case 54:
- return current_position->LowestCommonAncestorPosition(
- *previous_position, GenerateMoveDirection(fuzzer_data_.NextByte()));
- }
- }
- void AXNodePositionFuzzerGenerator::CallPositionAPIs(
- TestPositionType& position,
- TestPositionType& other_position) {
- // Call APIs on the created position. We don't care about any of the results,
- // we just want to make sure none of these crash or hang.
- std::ignore = position->GetAnchor();
- std::ignore = position->GetAnchorSiblingCount();
- std::ignore = position->IsIgnored();
- std::ignore = position->IsLeaf();
- std::ignore = position->IsValid();
- std::ignore = position->AtStartOfWord();
- std::ignore = position->AtEndOfWord();
- std::ignore = position->AtStartOfLine();
- std::ignore = position->AtEndOfLine();
- std::ignore = position->GetFormatStartBoundaryType();
- std::ignore = position->GetFormatEndBoundaryType();
- std::ignore = position->AtStartOfSentence();
- std::ignore = position->AtEndOfSentence();
- std::ignore = position->AtStartOfParagraph();
- std::ignore = position->AtEndOfParagraph();
- std::ignore = position->AtStartOfInlineBlock();
- std::ignore = position->AtStartOfPage();
- std::ignore = position->AtEndOfPage();
- std::ignore = position->AtStartOfAXTree();
- std::ignore = position->AtEndOfAXTree();
- std::ignore = position->AtStartOfContent();
- std::ignore = position->AtEndOfContent();
- std::ignore = position->LowestCommonAnchor(*other_position);
- std::ignore = position->CompareTo(*other_position);
- std::ignore = position->GetText();
- std::ignore = position->IsPointingToLineBreak();
- std::ignore = position->IsInTextObject();
- std::ignore = position->IsInWhiteSpace();
- std::ignore = position->MaxTextOffset();
- std::ignore = position->GetRole();
- }
- // Entry point for LibFuzzer.
- extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
- if (size < kMinFuzzDataSize || size > kMaxFuzzDataSize)
- return 0;
- AXTreeFuzzerGenerator generator;
- FuzzerData fuzz_data(data, size);
- const size_t node_count =
- kMinNodeCount + fuzz_data.NextByte() % kMaxNodeCount;
- generator.GenerateInitialUpdate(fuzz_data, node_count);
- ui::AXNodeID max_id = generator.GetMaxAssignedID();
- ui::AXTree* tree = generator.GetTree();
- // Run with --v=1 to aid in debugging a specific crash.
- VLOG(1) << tree->ToString();
- // Check to ensure there is enough fuzz data to create two positions.
- if (fuzz_data.RemainingBytes() < kNextNodePositionMaxDataSize * 2)
- return 0;
- AXNodePositionFuzzerGenerator position_fuzzer(tree, max_id, fuzz_data);
- // Having two positions allows us to test "more interesting" APIs that do work
- // on multiple positions.
- TestPositionType previous_position = position_fuzzer.CreateNewPosition();
- TestPositionType position = position_fuzzer.CreateNewPosition();
- while (fuzz_data.RemainingBytes() > kNextNodePositionMaxDataSize) {
- // Run with --v=1 to aid in debugging a specific crash.
- VLOG(1) << position->ToString() << fuzz_data.RemainingBytes();
- position_fuzzer.CallPositionAPIs(position, previous_position);
- // Determine next position to test:
- TestPositionType next_position =
- position_fuzzer.GenerateNextPosition(position, previous_position);
- previous_position = std::move(position);
- position = std::move(next_position);
- }
- return 0;
- }
|