ax_table_fuzzer.cc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <tuple>
  5. #include "build/build_config.h"
  6. #include "ui/accessibility/ax_node.h"
  7. #include "ui/accessibility/ax_tree.h"
  8. // The purpose of this script is to fuzz code that parses
  9. // table-like structures. As a result, we want to generate
  10. // accessibility trees that contain lots of table-related
  11. // roles.
  12. //
  13. // We also bias towards cells and rows so that we end up
  14. // with more of those overall.
  15. ax::mojom::Role GetInterestingTableRole(unsigned char byte) {
  16. switch (byte % 16) {
  17. default:
  18. case 0:
  19. case 1:
  20. case 2:
  21. case 3:
  22. return ax::mojom::Role::kCell;
  23. case 4:
  24. case 5:
  25. return ax::mojom::Role::kRow;
  26. case 6:
  27. return ax::mojom::Role::kTable;
  28. case 7:
  29. return ax::mojom::Role::kGrid;
  30. case 8:
  31. return ax::mojom::Role::kColumnHeader;
  32. case 9:
  33. return ax::mojom::Role::kRowHeader;
  34. case 10:
  35. return ax::mojom::Role::kGenericContainer;
  36. case 11:
  37. return ax::mojom::Role::kNone;
  38. case 12:
  39. return ax::mojom::Role::kLayoutTable;
  40. case 13:
  41. return ax::mojom::Role::kLayoutTableCell;
  42. case 14:
  43. return ax::mojom::Role::kLayoutTableRow;
  44. case 15:
  45. return ax::mojom::Role::kMain;
  46. }
  47. }
  48. // We want some of the nodes in the accessibility tree to have
  49. // table-related attributes.
  50. ax::mojom::IntAttribute GetInterestingTableAttribute(unsigned char byte) {
  51. switch (byte % 10) {
  52. case 0:
  53. default:
  54. return ax::mojom::IntAttribute::kTableCellRowIndex;
  55. case 1:
  56. return ax::mojom::IntAttribute::kTableCellColumnIndex;
  57. case 2:
  58. return ax::mojom::IntAttribute::kTableRowCount;
  59. case 3:
  60. return ax::mojom::IntAttribute::kTableColumnCount;
  61. case 4:
  62. return ax::mojom::IntAttribute::kAriaRowCount;
  63. case 5:
  64. return ax::mojom::IntAttribute::kAriaColumnCount;
  65. case 6:
  66. return ax::mojom::IntAttribute::kTableCellRowSpan;
  67. case 7:
  68. return ax::mojom::IntAttribute::kTableCellColumnSpan;
  69. case 8:
  70. return ax::mojom::IntAttribute::kAriaCellRowIndex;
  71. case 9:
  72. return ax::mojom::IntAttribute::kAriaCellColumnIndex;
  73. }
  74. }
  75. // Call all of the table-related APIs on an accessibility node.
  76. // These will be no-ops if the node is not part of a complete
  77. // table. We don't care about any of the results, we just want
  78. // to make sure none of these crash or hang.
  79. void TestTableAPIs(const ui::AXNode* node) {
  80. std::ignore = node->IsTable();
  81. std::ignore = node->GetTableColCount();
  82. std::ignore = node->GetTableRowCount();
  83. std::ignore = node->GetTableAriaColCount();
  84. std::ignore = node->GetTableAriaRowCount();
  85. std::ignore = node->GetTableCellCount();
  86. std::ignore = node->GetTableCaption();
  87. for (int i = 0; i < 8; i++)
  88. std::ignore = node->GetTableCellFromIndex(i);
  89. for (int i = 0; i < 3; i++)
  90. for (int j = 0; j < 3; j++)
  91. std::ignore = node->GetTableCellFromCoords(i, j);
  92. // Note: some of the APIs return IDs - we don't care what's
  93. // returned, we just want to make sure these APIs don't
  94. // crash. Normally |ids| is an out argument only, but
  95. // there's no reason we shouldn't be able to pass a vector
  96. // that was previously used by another call.
  97. std::vector<ui::AXNodeID> ids;
  98. for (int i = 0; i < 3; i++) {
  99. std::vector<ui::AXNodeID> col_header_node_ids =
  100. node->GetTableColHeaderNodeIds(i);
  101. ids.insert(ids.end(), col_header_node_ids.begin(),
  102. col_header_node_ids.end());
  103. std::vector<ui::AXNodeID> row_header_node_ids =
  104. node->GetTableRowHeaderNodeIds(i);
  105. ids.insert(ids.end(), row_header_node_ids.begin(),
  106. row_header_node_ids.end());
  107. }
  108. std::vector<ui::AXNodeID> unique_cell_ids = node->GetTableUniqueCellIds();
  109. ids.insert(ids.end(), unique_cell_ids.begin(), unique_cell_ids.end());
  110. std::ignore = node->IsTableRow();
  111. std::ignore = node->GetTableRowRowIndex();
  112. #if BUILDFLAG(IS_APPLE)
  113. std::ignore = node->IsTableColumn();
  114. std::ignore = node->GetTableColColIndex();
  115. #endif
  116. std::ignore = node->IsTableCellOrHeader();
  117. std::ignore = node->GetTableCellIndex();
  118. std::ignore = node->GetTableCellColIndex();
  119. std::ignore = node->GetTableCellRowIndex();
  120. std::ignore = node->GetTableCellColSpan();
  121. std::ignore = node->GetTableCellRowSpan();
  122. std::ignore = node->GetTableCellAriaColIndex();
  123. std::ignore = node->GetTableCellAriaRowIndex();
  124. std::vector<ui::AXNodeID> cell_col_header_node_ids =
  125. node->GetTableCellColHeaderNodeIds();
  126. ids.insert(ids.end(), cell_col_header_node_ids.begin(),
  127. cell_col_header_node_ids.end());
  128. std::vector<ui::AXNodeID> cell_row_header_node_ids =
  129. node->GetTableCellRowHeaderNodeIds();
  130. ids.insert(ids.end(), cell_row_header_node_ids.begin(),
  131. cell_row_header_node_ids.end());
  132. std::vector<ui::AXNode*> headers;
  133. node->GetTableCellColHeaders(&headers);
  134. node->GetTableCellRowHeaders(&headers);
  135. for (const auto* child : node->children())
  136. TestTableAPIs(child);
  137. }
  138. // Entry point for LibFuzzer.
  139. extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
  140. ui::AXTreeUpdate initial_state;
  141. initial_state.root_id = 1;
  142. size_t i = 0;
  143. // The root of the accessibility tree.
  144. ui::AXNodeData root;
  145. root.id = 1;
  146. if (i < size)
  147. root.role = GetInterestingTableRole(data[i++]);
  148. root.child_ids.push_back(2);
  149. initial_state.nodes.push_back(root);
  150. // Force the next node of the accessibility tree to be a table,
  151. // and give it no attributes but a few children.
  152. ui::AXNodeData table;
  153. table.id = 2;
  154. table.role = ax::mojom::Role::kTable;
  155. if (i < size) {
  156. size_t child_count = data[i++] % 8;
  157. for (size_t j = 0; j < child_count && i < size; j++)
  158. table.child_ids.push_back(3 + data[i++] % 32);
  159. }
  160. initial_state.nodes.push_back(table);
  161. // Create more accessibility nodes that might result in a table.
  162. int next_id = 3;
  163. while (i < size) {
  164. ui::AXNodeData node;
  165. node.id = next_id++;
  166. if (i < size)
  167. node.role = GetInterestingTableRole(data[i++]);
  168. if (i < size) {
  169. int attr_count = data[i++] % 6;
  170. for (int j = 0; j < attr_count && i + 1 < size; j++) {
  171. unsigned char attr = data[i++];
  172. int32_t value = static_cast<int32_t>(data[i++]) - 2;
  173. node.AddIntAttribute(GetInterestingTableAttribute(attr), value);
  174. }
  175. }
  176. if (i < size) {
  177. size_t child_count = data[i++] % 8;
  178. for (size_t j = 0; j < child_count && i < size; j++)
  179. node.child_ids.push_back(4 + data[i++] % 32);
  180. }
  181. initial_state.nodes.push_back(node);
  182. }
  183. // Run with --v=1 to aid in debugging a specific crash.
  184. VLOG(1) << "Input accessibility tree:\n" << initial_state.ToString();
  185. ui::AXTree tree;
  186. if (tree.Unserialize(initial_state))
  187. TestTableAPIs(tree.root());
  188. return 0;
  189. }