123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- // Copyright 2018 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 <tuple>
- #include "build/build_config.h"
- #include "ui/accessibility/ax_node.h"
- #include "ui/accessibility/ax_tree.h"
- // The purpose of this script is to fuzz code that parses
- // table-like structures. As a result, we want to generate
- // accessibility trees that contain lots of table-related
- // roles.
- //
- // We also bias towards cells and rows so that we end up
- // with more of those overall.
- ax::mojom::Role GetInterestingTableRole(unsigned char byte) {
- switch (byte % 16) {
- default:
- case 0:
- case 1:
- case 2:
- case 3:
- return ax::mojom::Role::kCell;
- case 4:
- case 5:
- return ax::mojom::Role::kRow;
- case 6:
- return ax::mojom::Role::kTable;
- case 7:
- return ax::mojom::Role::kGrid;
- case 8:
- return ax::mojom::Role::kColumnHeader;
- case 9:
- return ax::mojom::Role::kRowHeader;
- case 10:
- return ax::mojom::Role::kGenericContainer;
- case 11:
- return ax::mojom::Role::kNone;
- case 12:
- return ax::mojom::Role::kLayoutTable;
- case 13:
- return ax::mojom::Role::kLayoutTableCell;
- case 14:
- return ax::mojom::Role::kLayoutTableRow;
- case 15:
- return ax::mojom::Role::kMain;
- }
- }
- // We want some of the nodes in the accessibility tree to have
- // table-related attributes.
- ax::mojom::IntAttribute GetInterestingTableAttribute(unsigned char byte) {
- switch (byte % 10) {
- case 0:
- default:
- return ax::mojom::IntAttribute::kTableCellRowIndex;
- case 1:
- return ax::mojom::IntAttribute::kTableCellColumnIndex;
- case 2:
- return ax::mojom::IntAttribute::kTableRowCount;
- case 3:
- return ax::mojom::IntAttribute::kTableColumnCount;
- case 4:
- return ax::mojom::IntAttribute::kAriaRowCount;
- case 5:
- return ax::mojom::IntAttribute::kAriaColumnCount;
- case 6:
- return ax::mojom::IntAttribute::kTableCellRowSpan;
- case 7:
- return ax::mojom::IntAttribute::kTableCellColumnSpan;
- case 8:
- return ax::mojom::IntAttribute::kAriaCellRowIndex;
- case 9:
- return ax::mojom::IntAttribute::kAriaCellColumnIndex;
- }
- }
- // Call all of the table-related APIs on an accessibility node.
- // These will be no-ops if the node is not part of a complete
- // table. We don't care about any of the results, we just want
- // to make sure none of these crash or hang.
- void TestTableAPIs(const ui::AXNode* node) {
- std::ignore = node->IsTable();
- std::ignore = node->GetTableColCount();
- std::ignore = node->GetTableRowCount();
- std::ignore = node->GetTableAriaColCount();
- std::ignore = node->GetTableAriaRowCount();
- std::ignore = node->GetTableCellCount();
- std::ignore = node->GetTableCaption();
- for (int i = 0; i < 8; i++)
- std::ignore = node->GetTableCellFromIndex(i);
- for (int i = 0; i < 3; i++)
- for (int j = 0; j < 3; j++)
- std::ignore = node->GetTableCellFromCoords(i, j);
- // Note: some of the APIs return IDs - we don't care what's
- // returned, we just want to make sure these APIs don't
- // crash. Normally |ids| is an out argument only, but
- // there's no reason we shouldn't be able to pass a vector
- // that was previously used by another call.
- std::vector<ui::AXNodeID> ids;
- for (int i = 0; i < 3; i++) {
- std::vector<ui::AXNodeID> col_header_node_ids =
- node->GetTableColHeaderNodeIds(i);
- ids.insert(ids.end(), col_header_node_ids.begin(),
- col_header_node_ids.end());
- std::vector<ui::AXNodeID> row_header_node_ids =
- node->GetTableRowHeaderNodeIds(i);
- ids.insert(ids.end(), row_header_node_ids.begin(),
- row_header_node_ids.end());
- }
- std::vector<ui::AXNodeID> unique_cell_ids = node->GetTableUniqueCellIds();
- ids.insert(ids.end(), unique_cell_ids.begin(), unique_cell_ids.end());
- std::ignore = node->IsTableRow();
- std::ignore = node->GetTableRowRowIndex();
- #if BUILDFLAG(IS_APPLE)
- std::ignore = node->IsTableColumn();
- std::ignore = node->GetTableColColIndex();
- #endif
- std::ignore = node->IsTableCellOrHeader();
- std::ignore = node->GetTableCellIndex();
- std::ignore = node->GetTableCellColIndex();
- std::ignore = node->GetTableCellRowIndex();
- std::ignore = node->GetTableCellColSpan();
- std::ignore = node->GetTableCellRowSpan();
- std::ignore = node->GetTableCellAriaColIndex();
- std::ignore = node->GetTableCellAriaRowIndex();
- std::vector<ui::AXNodeID> cell_col_header_node_ids =
- node->GetTableCellColHeaderNodeIds();
- ids.insert(ids.end(), cell_col_header_node_ids.begin(),
- cell_col_header_node_ids.end());
- std::vector<ui::AXNodeID> cell_row_header_node_ids =
- node->GetTableCellRowHeaderNodeIds();
- ids.insert(ids.end(), cell_row_header_node_ids.begin(),
- cell_row_header_node_ids.end());
- std::vector<ui::AXNode*> headers;
- node->GetTableCellColHeaders(&headers);
- node->GetTableCellRowHeaders(&headers);
- for (const auto* child : node->children())
- TestTableAPIs(child);
- }
- // Entry point for LibFuzzer.
- extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
- ui::AXTreeUpdate initial_state;
- initial_state.root_id = 1;
- size_t i = 0;
- // The root of the accessibility tree.
- ui::AXNodeData root;
- root.id = 1;
- if (i < size)
- root.role = GetInterestingTableRole(data[i++]);
- root.child_ids.push_back(2);
- initial_state.nodes.push_back(root);
- // Force the next node of the accessibility tree to be a table,
- // and give it no attributes but a few children.
- ui::AXNodeData table;
- table.id = 2;
- table.role = ax::mojom::Role::kTable;
- if (i < size) {
- size_t child_count = data[i++] % 8;
- for (size_t j = 0; j < child_count && i < size; j++)
- table.child_ids.push_back(3 + data[i++] % 32);
- }
- initial_state.nodes.push_back(table);
- // Create more accessibility nodes that might result in a table.
- int next_id = 3;
- while (i < size) {
- ui::AXNodeData node;
- node.id = next_id++;
- if (i < size)
- node.role = GetInterestingTableRole(data[i++]);
- if (i < size) {
- int attr_count = data[i++] % 6;
- for (int j = 0; j < attr_count && i + 1 < size; j++) {
- unsigned char attr = data[i++];
- int32_t value = static_cast<int32_t>(data[i++]) - 2;
- node.AddIntAttribute(GetInterestingTableAttribute(attr), value);
- }
- }
- if (i < size) {
- size_t child_count = data[i++] % 8;
- for (size_t j = 0; j < child_count && i < size; j++)
- node.child_ids.push_back(4 + data[i++] % 32);
- }
- initial_state.nodes.push_back(node);
- }
- // Run with --v=1 to aid in debugging a specific crash.
- VLOG(1) << "Input accessibility tree:\n" << initial_state.ToString();
- ui::AXTree tree;
- if (tree.Unserialize(initial_state))
- TestTableAPIs(tree.root());
- return 0;
- }
|