Commit 5cc41399 authored by dmazzoni's avatar dmazzoni Committed by Commit bot

Re-land: Add fuzzer for AXTree and fix a couple of bugs it found.

Original change: https://codereview.chromium.org/2323103002/

Fix: On Android, the delegate callback called in DestroySubtree was
accessing AXTree::root() after we had deleted it but before we set it
to null. We need to clear it first and then destroy it.

BUG=646777
TBR=aboxhall,mmoroz

Review-Url: https://codereview.chromium.org/2340893002
Cr-Commit-Position: refs/heads/master@{#418698}
parent 2c245143
...@@ -6,6 +6,7 @@ import("//build/config/linux/pkg_config.gni") ...@@ -6,6 +6,7 @@ import("//build/config/linux/pkg_config.gni")
import("//build/config/features.gni") import("//build/config/features.gni")
import("//build/config/ui.gni") import("//build/config/ui.gni")
import("//build/json_schema_api.gni") import("//build/json_schema_api.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni") import("//testing/test.gni")
if (is_android) { if (is_android) {
import("//build/config/android/rules.gni") import("//build/config/android/rules.gni")
...@@ -153,3 +154,13 @@ json_schema_api("ax_gen") { ...@@ -153,3 +154,13 @@ json_schema_api("ax_gen") {
root_namespace = "ui" root_namespace = "ui"
schemas = true schemas = true
} }
fuzzer_test("ax_tree_fuzzer") {
sources = [
"ax_tree_fuzzer.cc",
]
deps = [
":accessibility",
]
}
...@@ -243,8 +243,16 @@ bool AXTree::UpdateNode(const AXNodeData& src, ...@@ -243,8 +243,16 @@ bool AXTree::UpdateNode(const AXNodeData& src,
// First, delete nodes that used to be children of this node but aren't // First, delete nodes that used to be children of this node but aren't
// anymore. // anymore.
if (!DeleteOldChildren(node, src.child_ids, update_state)) { if (!DeleteOldChildren(node, src.child_ids, update_state)) {
if (update_state->new_root) if (update_state->new_root) {
DestroySubtree(update_state->new_root, update_state); AXNode* old_root = root_;
root_ = nullptr;
DestroySubtree(old_root, update_state);
if (node != old_root &&
update_state->new_nodes.find(node) != update_state->new_nodes.end()) {
DestroySubtree(node, update_state);
}
}
return false; return false;
} }
...@@ -261,7 +269,7 @@ bool AXTree::UpdateNode(const AXNodeData& src, ...@@ -261,7 +269,7 @@ bool AXTree::UpdateNode(const AXNodeData& src,
// DestroySubtree. // DestroySubtree.
AXNode* old_root = root_; AXNode* old_root = root_;
root_ = node; root_ = node;
if (old_root) if (old_root && old_root != node)
DestroySubtree(old_root, update_state); DestroySubtree(old_root, update_state);
} }
......
// Copyright 2016 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_tree.h"
class EmptyAXTreeDelegate : public ui::AXTreeDelegate {
public:
EmptyAXTreeDelegate() {}
void OnNodeDataWillChange(ui::AXTree* tree,
const ui::AXNodeData& old_node_data,
const ui::AXNodeData& new_node_data) override {}
void OnTreeDataChanged(ui::AXTree* tree) override {}
void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override {}
void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override {}
void OnNodeWillBeReparented(ui::AXTree* tree, ui::AXNode* node) override {}
void OnSubtreeWillBeReparented(ui::AXTree* tree, ui::AXNode* node) override {}
void OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) override {}
void OnNodeReparented(ui::AXTree* tree, ui::AXNode* node) override {}
void OnNodeChanged(ui::AXTree* tree, ui::AXNode* node) override {}
void OnAtomicUpdateFinished(ui::AXTree* tree,
bool root_changed,
const std::vector<Change>& changes) override {}
};
// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) {
ui::AXTreeUpdate initial_state;
size_t i = 0;
while (i < size) {
ui::AXNodeData node;
node.id = data[i++];
node.state = 0;
if (i < size) {
size_t child_count = data[i++];
for (size_t j = 0; j < child_count && i < size; j++)
node.child_ids.push_back(data[i++]);
}
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;
EmptyAXTreeDelegate delegate;
tree.SetDelegate(&delegate);
tree.Unserialize(initial_state);
return 0;
}
...@@ -479,4 +479,33 @@ TEST(AXTreeTest, TreeDelegateIsNotCalledForReparenting) { ...@@ -479,4 +479,33 @@ TEST(AXTreeTest, TreeDelegateIsNotCalledForReparenting) {
tree.SetDelegate(NULL); tree.SetDelegate(NULL);
} }
// UAF caught by ax_tree_fuzzer
TEST(AXTreeTest, BogusAXTree) {
AXTreeUpdate initial_state;
AXNodeData node;
node.id = 0;
node.state = 0;
initial_state.nodes.push_back(node);
initial_state.nodes.push_back(node);
ui::AXTree tree;
tree.Unserialize(initial_state);
}
// UAF caught by ax_tree_fuzzer
TEST(AXTreeTest, BogusAXTree2) {
AXTreeUpdate initial_state;
AXNodeData node;
node.id = 0;
node.state = 0;
initial_state.nodes.push_back(node);
AXNodeData node2;
node2.id = 0;
node2.state = 0;
node2.child_ids.push_back(0);
node2.child_ids.push_back(0);
initial_state.nodes.push_back(node2);
ui::AXTree tree;
tree.Unserialize(initial_state);
}
} // namespace ui } // namespace ui
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