Commit f14109fe authored by Vaclav Brozek's avatar Vaclav Brozek Committed by Commit Bot

Introduce FormData parsing fuzzer

This CL adds a function to generate FormData based on a given
DataAccessor. The CL further uses this function to add a fuzzer for the
code parsing FormData into PasswordForms (on iOS, for now).

Bug: 827945
Change-Id: I910a19b8e54b6383f91476c5c0fd698521d11eb2
Reviewed-on: https://chromium-review.googlesource.com/992496
Commit-Queue: Vaclav Brozek <vabr@chromium.org>
Reviewed-by: default avatarVadym Doroshenko <dvadym@chromium.org>
Reviewed-by: default avatarMax Moroz <mmoroz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548783}
parent 8de0daef
......@@ -2,7 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
if (is_ios) {
import("//build/config/sanitizers/sanitizers.gni")
# Determine whetner fuzzer_test targets are built.
does_fuzzer_test_compile =
!disable_libfuzzer && (use_fuzzing_engine || use_drfuzz || is_linux)
if (does_fuzzer_test_compile || is_ios) {
# Compile for production under iOS and for fuzzing whenever fuzzer_tests are built.
static_library("form_parsing") {
sources = [
"ios_form_parser.cc",
......@@ -30,4 +37,4 @@ if (is_ios) {
"//url",
]
}
} # if (is_ios)
} # if (does_fuzzer_test_compile || is_ios)
......@@ -2,14 +2,20 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//testing/libfuzzer/fuzzer_test.gni")
static_library("fuzzer_support") {
sources = [
"data_accessor.cc",
"data_accessor.h",
"form_data_producer.cc",
"form_data_producer.h",
]
deps = [
"//base",
"//components/autofill/core/common",
"//url",
]
}
......@@ -25,3 +31,19 @@ source_set("unit_tests") {
"//testing/gtest",
]
}
fuzzer_test("password_manager_form_parser_fuzzer") {
sources = [
"form_parser_fuzzer.cc",
]
deps = [
":fuzzer_support",
"//base",
"//base:i18n",
"//components/autofill/core/common",
"//components/password_manager/core/browser/form_parsing",
]
dict = "form_parser_fuzzer.dict"
}
......@@ -16,7 +16,7 @@ namespace password_manager {
namespace {
// The maximum byte length of a string to be returned by |ConsumeString*|.
constexpr size_t kMaxStringBytes = 254;
constexpr size_t kMaxStringBytes = 256;
} // namespace
......
......@@ -39,10 +39,10 @@ class DataAccessor {
// Advance the "reading head" to the next whole-byte boundary, if needed, then
// return the string stored in the next |length| characters, advancing the
// "reading head" to point past the read data. A "character" means byte for
// std::string and two bytes for base::string16. At most 254 bytes can be
// std::string and two bytes for base::string16. At most 256 bytes can be
// consumed at once, hence |length| is restricted as noted below.
std::string ConsumeString(size_t length); // |length| <= 254
base::string16 ConsumeString16(size_t length); // |length| <= 127
std::string ConsumeString(size_t length); // |length| <= 256
base::string16 ConsumeString16(size_t length); // |length| <= 128
private:
// Helper for |ConsumeString*|. It combines the |data_| and padding, if
......
// 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 "components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/password_manager/core/browser/form_parsing/fuzzer/data_accessor.h"
#include "url/gurl.h"
#include "url/origin.h"
using autofill::FormData;
using autofill::FormFieldData;
namespace password_manager {
namespace {
struct FormFieldDataParams {
size_t form_control_type_length;
size_t autocomplete_attribute_length;
size_t label_length;
size_t name_length;
size_t id_length;
size_t value_length;
// In an array of FormFieldData, all instances with |same_value_field| true
// get the same value as the first such instance.
bool same_value_field;
};
} // namespace
FormData GenerateWithDataAccessor(DataAccessor* accessor) {
FormData result;
// First determine the main non-string attributes not specific to particular
// fields.
result.is_form_tag = accessor->ConsumeBit();
result.is_formless_checkout = accessor->ConsumeBit();
// To minimize wasting bits, string-based data itself gets extracted after all
// numbers and flags are. Their length can be determined now, however. A
// reasonable range is 0-127 characters, i.e., 7 bits.
const size_t name_length = accessor->ConsumeNumber(7);
const size_t action_length = accessor->ConsumeNumber(7);
const size_t origin_length = accessor->ConsumeNumber(7);
const size_t main_frame_origin_length = accessor->ConsumeNumber(7);
// Determine how many fields this form will have. 0-15, i.e., 4 bits.
const size_t number_of_fields = accessor->ConsumeNumber(4);
result.fields.resize(number_of_fields);
FormFieldDataParams field_params[15];
int first_field_with_same_value = -1;
for (size_t i = 0; i < number_of_fields; ++i) {
// Determine the non-string value for each field.
result.fields[i].is_focusable = accessor->ConsumeBit();
// And the lengths of the string values.
field_params[i].form_control_type_length = accessor->ConsumeNumber(7);
field_params[i].autocomplete_attribute_length = accessor->ConsumeNumber(7);
field_params[i].label_length = accessor->ConsumeNumber(7);
field_params[i].name_length = accessor->ConsumeNumber(7);
field_params[i].id_length = accessor->ConsumeNumber(7);
field_params[i].same_value_field = accessor->ConsumeBit();
bool has_value_copy_from_earlier = field_params[i].same_value_field;
if (field_params[i].same_value_field && first_field_with_same_value == -1) {
first_field_with_same_value = static_cast<int>(i);
has_value_copy_from_earlier = false;
}
// Emtpy values are interesting from the parsing perspective. Ensure that a
// big chunk of the cases ends up with an empty value by letting an input
// bit decide.
field_params[i].value_length = 0;
if (!has_value_copy_from_earlier && accessor->ConsumeBit()) {
field_params[i].value_length = accessor->ConsumeNumber(7) + 1;
}
}
// Now go back and determine the string-based values of the form itself.
result.name = accessor->ConsumeString16(name_length);
result.action = GURL(accessor->ConsumeString(action_length));
result.origin = GURL(accessor->ConsumeString(origin_length));
result.main_frame_origin = url::Origin::Create(
GURL(accessor->ConsumeString(main_frame_origin_length)));
// And finally do the same for all the fields.
for (size_t i = 0; i < number_of_fields; ++i) {
result.fields[i].form_control_type =
accessor->ConsumeString(field_params[i].form_control_type_length);
result.fields[i].autocomplete_attribute =
accessor->ConsumeString(field_params[i].autocomplete_attribute_length);
result.fields[i].label =
accessor->ConsumeString16(field_params[i].label_length);
result.fields[i].name =
accessor->ConsumeString16(field_params[i].name_length);
result.fields[i].id = accessor->ConsumeString16(field_params[i].id_length);
if (field_params[i].same_value_field &&
first_field_with_same_value != static_cast<int>(i)) {
result.fields[i].value = result.fields[first_field_with_same_value].value;
} else {
result.fields[i].value =
accessor->ConsumeString16(field_params[i].value_length);
}
}
return result;
}
} // namespace password_manager
// 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_FUZZER_FORM_DATA_PRODUCER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_FUZZER_FORM_DATA_PRODUCER_H_
#include "components/autofill/core/common/form_data.h"
namespace password_manager {
class DataAccessor;
// Generates a |FormData| object based on values obtained via |accessor|. See
// https://goo.gl/29t6VH for a detailed design.
autofill::FormData GenerateWithDataAccessor(DataAccessor* accessor);
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FORM_PARSING_FUZZER_FORM_DATA_PRODUCER_H_
// 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 <stddef.h>
#include <stdint.h>
#include <memory>
#include "base/at_exit.h"
#include "base/i18n/icu_util.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/form_parsing/fuzzer/data_accessor.h"
#include "components/password_manager/core/browser/form_parsing/fuzzer/form_data_producer.h"
#include "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
namespace password_manager {
// ICU is used inside GURL parser, which is used by GenerateWithDataAccessor.
struct IcuEnvironment {
IcuEnvironment() { CHECK(base::i18n::InitializeICU()); }
// used by ICU integration.
base::AtExitManager at_exit_manager;
};
IcuEnvironment* env = new IcuEnvironment();
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
DataAccessor accessor(data, size);
FormParsingMode mode = accessor.ConsumeBit() ? FormParsingMode::FILLING
: FormParsingMode::SAVING;
autofill::FormData form_data = GenerateWithDataAccessor(&accessor);
std::unique_ptr<autofill::PasswordForm> result =
ParseFormData(form_data, mode);
if (result) {
// Create a copy of the result -- running the copy-constructor might
// discover some invalid data in |result|.
autofill::PasswordForm copy(*result);
}
return 0;
}
} // namespace password_manager
"http://"
"password"
"username"
"current-password"
"new-password"
"cc-name"
"cc-number"
"cc-csc"
"cc-exp"
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