Loading...
Searching...
No Matches
parse.cc
Go to the documentation of this file.
#include <fuzzer/FuzzedDataProvider.h>
#include <cassert>
#include <cstdio>
#include <memory>
#include <string>
#include "ada.cpp"
#include "ada.h"
bool is_valid_utf8_string(const char *buf, size_t len) {
const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
uint64_t pos = 0;
uint32_t code_point = 0;
while (pos < len) {
uint64_t next_pos = pos + 16;
if (next_pos <= len) { // if it is safe to read 16 more bytes, check that
// they are ascii
uint64_t v1;
std::memcpy(&v1, data + pos, sizeof(uint64_t));
uint64_t v2;
std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
uint64_t v{v1 | v2};
if ((v & 0x8080808080808080) == 0) {
pos = next_pos;
continue;
}
}
unsigned char byte = data[pos];
while (byte < 0b10000000) {
if (++pos == len) {
return true;
}
byte = data[pos];
}
if ((byte & 0b11100000) == 0b11000000) {
next_pos = pos + 2;
if (next_pos > len) {
return false;
}
if ((data[pos + 1] & 0b11000000) != 0b10000000) {
return false;
}
code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
if ((code_point < 0x80) || (0x7ff < code_point)) {
return false;
}
} else if ((byte & 0b11110000) == 0b11100000) {
next_pos = pos + 3;
if (next_pos > len) {
return false;
}
if ((data[pos + 1] & 0b11000000) != 0b10000000) {
return false;
}
if ((data[pos + 2] & 0b11000000) != 0b10000000) {
return false;
}
code_point = (byte & 0b00001111) << 12 |
(data[pos + 1] & 0b00111111) << 6 |
(data[pos + 2] & 0b00111111);
if ((code_point < 0x800) || (0xffff < code_point) ||
(0xd7ff < code_point && code_point < 0xe000)) {
return false;
}
} else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
next_pos = pos + 4;
if (next_pos > len) {
return false;
}
if ((data[pos + 1] & 0b11000000) != 0b10000000) {
return false;
}
if ((data[pos + 2] & 0b11000000) != 0b10000000) {
return false;
}
if ((data[pos + 3] & 0b11000000) != 0b10000000) {
return false;
}
code_point =
(byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
(data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
if (code_point <= 0xffff || 0x10ffff < code_point) {
return false;
}
} else {
return false;
}
pos = next_pos;
}
return true;
}
// Exercise all getters and boolean predicates on ada::url
static void exercise_url_predicates(const ada::url &u) {
volatile size_t length = 0;
length += u.get_href().size();
length += u.get_origin().size();
length += u.get_protocol().size();
length += u.get_username().size();
length += u.get_password().size();
length += u.get_host().size();
length += u.get_hostname().size();
length += u.get_pathname().size();
length += u.get_search().size();
length += u.get_hash().size();
length += u.get_port().size();
length += u.to_string().size();
length += u.get_pathname_length();
(void)u.has_valid_domain();
(void)u.has_credentials();
(void)u.has_empty_hostname();
(void)u.has_hostname();
(void)u.has_port();
(void)u.has_hash();
(void)u.has_search();
(void)u.get_components();
}
// Exercise all getters and boolean predicates on ada::url_aggregator
static void exercise_aggregator_predicates(const ada::url_aggregator &u) {
volatile size_t length = 0;
length += u.get_href().size();
length += u.get_origin().size();
length += u.get_protocol().size();
length += u.get_username().size();
length += u.get_password().size();
length += u.get_host().size();
length += u.get_hostname().size();
length += u.get_pathname().size();
length += u.get_search().size();
length += u.get_hash().size();
length += u.get_port().size();
length += u.to_string().size();
length += u.get_pathname_length();
(void)u.has_valid_domain();
(void)u.has_credentials();
(void)u.has_empty_hostname();
(void)u.has_hostname();
(void)u.has_non_empty_username();
(void)u.has_non_empty_password();
(void)u.has_password();
(void)u.has_port();
(void)u.has_hash();
(void)u.has_search();
(void)u.get_components();
volatile bool is_valid = u.validate();
(void)is_valid;
(void)u.to_diagram();
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fdp(data, size);
std::string source = fdp.ConsumeRandomLengthString(256);
std::string base = fdp.ConsumeRandomLengthString(256);
// volatile forces the compiler to store the results without undue
// optimizations
volatile size_t length = 0;
auto parse_url = ada::parse<ada::url>(source);
auto parse_url_aggregator = ada::parse<ada::url_aggregator>(source);
if (is_valid_utf8_string(source.data(), source.length())) {
if (parse_url.has_value() ^ parse_url_aggregator.has_value()) {
printf("Source used to parse: %s", source.c_str());
abort();
}
}
if (parse_url) {
length += parse_url->get_href().size();
length += parse_url->get_origin().size();
}
if (parse_url_aggregator) {
length += parse_url_aggregator->get_href().size();
length += parse_url_aggregator->get_origin().size();
volatile bool is_parse_url_aggregator_output_valid = false;
is_parse_url_aggregator_output_valid = parse_url_aggregator->validate();
assert(parse_url->get_protocol() == parse_url_aggregator->get_protocol());
assert(parse_url->get_href() == parse_url_aggregator->get_href());
assert(std::string(parse_url->get_hostname()) ==
std::string(parse_url_aggregator->get_hostname()));
assert(std::string(parse_url->get_pathname()) ==
std::string(parse_url_aggregator->get_pathname()));
assert(std::string(parse_url->get_search()) ==
std::string(parse_url_aggregator->get_search()));
assert(std::string(parse_url->get_hash()) ==
std::string(parse_url_aggregator->get_hash()));
assert(std::string(parse_url->get_port()) ==
std::string(parse_url_aggregator->get_port()));
assert(parse_url->get_username() ==
std::string(parse_url_aggregator->get_username()));
assert(parse_url->get_password() ==
std::string(parse_url_aggregator->get_password()));
assert(std::string(parse_url->get_host()) ==
std::string(parse_url_aggregator->get_host()));
// Exercise all predicates on both types
exercise_url_predicates(*parse_url);
exercise_aggregator_predicates(*parse_url_aggregator);
// Test set_href consistency
parse_url->set_href(source);
parse_url_aggregator->set_href(source);
assert(parse_url->get_href() == parse_url_aggregator->get_href());
}
if (parse_url) {
// Copy constructor
ada::url copied_url = *parse_url;
assert(copied_url.get_href() == parse_url->get_href());
// Copy assignment
ada::url assigned_url;
assigned_url = *parse_url;
assert(assigned_url.get_href() == parse_url->get_href());
// Move constructor
ada::url moved_url = std::move(copied_url);
assert(moved_url.get_href() == parse_url->get_href());
}
if (parse_url_aggregator) {
// Copy constructor
ada::url_aggregator copied_agg = *parse_url_aggregator;
assert(std::string(copied_agg.get_href()) ==
std::string(parse_url_aggregator->get_href()));
// Copy assignment
ada::url_aggregator assigned_agg;
assigned_agg = *parse_url_aggregator;
assert(std::string(assigned_agg.get_href()) ==
std::string(parse_url_aggregator->get_href()));
// Move constructor
ada::url_aggregator moved_agg = std::move(copied_agg);
assert(std::string(moved_agg.get_href()) ==
std::string(parse_url_aggregator->get_href()));
// Move assignment
ada::url_aggregator move_assigned_agg;
move_assigned_agg = std::move(assigned_agg);
assert(std::string(move_assigned_agg.get_href()) ==
std::string(parse_url_aggregator->get_href()));
}
auto out_url = ada::parse<ada::url>("https://www.ada-url.com");
if (out_url) {
out_url->set_protocol(source);
out_url->set_username(source);
out_url->set_password(source);
out_url->set_hostname(source);
out_url->set_host(source);
out_url->set_pathname(source);
out_url->set_search(source);
out_url->set_hash(source);
out_url->set_port(source);
// getters
length += out_url->get_protocol().size();
length += out_url->get_username().size();
length += out_url->get_password().size();
length += out_url->get_hostname().size();
length += out_url->get_host().size();
length += out_url->get_pathname().size();
length += out_url->get_search().size();
length += out_url->get_hash().size();
length += out_url->get_origin().size();
length += out_url->get_port().size();
length += out_url->get_pathname_length();
length += out_url->to_string().size();
// boolean predicates after setters
(void)out_url->has_valid_domain();
(void)out_url->has_credentials();
(void)out_url->has_empty_hostname();
(void)out_url->has_hostname();
(void)out_url->has_port();
(void)out_url->has_hash();
(void)out_url->has_search();
(void)out_url->get_components();
}
auto out_aggregator =
ada::parse<ada::url_aggregator>("https://www.ada-url.com");
if (out_aggregator) {
out_aggregator->set_protocol(source);
out_aggregator->set_username(source);
out_aggregator->set_password(source);
out_aggregator->set_hostname(source);
out_aggregator->set_host(source);
out_aggregator->set_pathname(source);
out_aggregator->set_search(source);
out_aggregator->set_hash(source);
out_aggregator->set_port(source);
// getters
length += out_aggregator->get_protocol().size();
length += out_aggregator->get_username().size();
length += out_aggregator->get_password().size();
length += out_aggregator->get_hostname().size();
length += out_aggregator->get_host().size();
length += out_aggregator->get_pathname().size();
length += out_aggregator->get_search().size();
length += out_aggregator->get_hash().size();
length += out_aggregator->get_origin().size();
length += out_aggregator->get_port().size();
length += out_aggregator->get_pathname_length();
length += out_aggregator->to_string().size();
volatile bool is_output_valid = false;
is_output_valid = out_aggregator->validate();
(void)out_aggregator->to_diagram();
// boolean predicates after setters
(void)out_aggregator->has_valid_domain();
(void)out_aggregator->has_credentials();
(void)out_aggregator->has_empty_hostname();
(void)out_aggregator->has_hostname();
(void)out_aggregator->has_non_empty_username();
(void)out_aggregator->has_non_empty_password();
(void)out_aggregator->has_password();
(void)out_aggregator->has_port();
(void)out_aggregator->has_hash();
(void)out_aggregator->has_search();
(void)out_aggregator->get_components();
// clear methods
out_aggregator->clear_port();
out_aggregator->clear_search();
out_aggregator->clear_hash();
}
auto base_url = ada::parse<ada::url>(base);
auto base_agg = ada::parse<ada::url_aggregator>(base);
if (base_url) {
auto result = ada::parse<ada::url>(source, &*base_url);
if (result) {
length += result->get_href().size();
length += result->get_origin().size();
exercise_url_predicates(*result);
}
}
if (base_agg) {
auto result = ada::parse<ada::url_aggregator>(source, &*base_agg);
if (result) {
length += result->get_href().size();
length += result->get_origin().size();
exercise_aggregator_predicates(*result);
}
}
// Cross-type consistency: relative URL parsing with a base should agree
// between url and url_aggregator representations for valid UTF-8 inputs.
if (is_valid_utf8_string(source.data(), source.length()) &&
is_valid_utf8_string(base.data(), base.length()) && base_url &&
base_agg) {
auto res_url = ada::parse<ada::url>(source, &*base_url);
auto res_agg = ada::parse<ada::url_aggregator>(source, &*base_agg);
if (res_url.has_value() ^ res_agg.has_value()) {
printf("Relative parse inconsistency for source=%s base=%s\n",
source.c_str(), base.c_str());
abort();
}
if (res_url && res_agg) {
if (res_url->get_href() != std::string(res_agg->get_href())) {
printf("Relative parse href mismatch for source=%s base=%s\n",
source.c_str(), base.c_str());
abort();
}
}
}
if (base_agg) {
auto level1 = ada::parse<ada::url_aggregator>(source, &*base_agg);
if (level1) {
std::string input2 = fdp.ConsumeRandomLengthString(128);
auto level2 = ada::parse<ada::url_aggregator>(input2, &*level1);
if (level2) {
length += level2->get_href().size();
volatile bool v = level2->validate();
(void)v;
}
}
}
{
auto known_base =
ada::parse<ada::url_aggregator>("https://example.com/a/b/c?query#hash");
if (known_base) {
auto result = ada::parse<ada::url_aggregator>(source, &*known_base);
if (result) {
length += result->get_href().size();
exercise_aggregator_predicates(*result);
}
}
}
length += ada::href_from_file(source).size();
bool is_valid = ada::checkers::verify_dns_length(source);
(void)is_valid;
return 0;
} // extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
ada.cpp
ada.h
Main header for the Ada URL parser library.
ada::href_from_file
std::string href_from_file(std::string_view path)
Definition implementation.cpp:28
ada::parse
ada_warn_unused ada::result< result_type > parse(std::string_view input, const result_type *base_url=nullptr)
Definition implementation.cpp:13
is_valid_utf8_string
bool is_valid_utf8_string(const char *buf, size_t len)
Definition parse.cc:11
exercise_url_predicates
static void exercise_url_predicates(const ada::url &u)
Definition parse.cc:96
exercise_aggregator_predicates
static void exercise_aggregator_predicates(const ada::url_aggregator &u)
Definition parse.cc:122
LLVMFuzzerTestOneInput
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
Definition parse.cc:153
ada::url_aggregator
Memory-efficient URL representation using a single buffer.
Definition url_aggregator.h:46
ada::url_aggregator::get_components
ada_really_inline const url_components & get_components() const noexcept
Definition url_aggregator-inl.h:732
ada::url_aggregator::has_non_empty_password
constexpr bool has_non_empty_password() const noexcept
Definition url_aggregator-inl.h:781
ada::url_aggregator::validate
constexpr bool validate() const noexcept
Definition url_aggregator-inl.h:924
ada::url_aggregator::get_search
std::string_view get_search() const ada_lifetime_bound
Definition url_aggregator.cpp:792
ada::url_aggregator::get_hash
std::string_view get_hash() const ada_lifetime_bound
Definition url_aggregator.cpp:744
ada::url_aggregator::to_string
std::string to_string() const override
Definition url_aggregator.cpp:816
ada::url_aggregator::get_password
std::string_view get_password() const ada_lifetime_bound
Definition url_aggregator.cpp:724
ada::url_aggregator::get_host
std::string_view get_host() const ada_lifetime_bound
Definition url_aggregator.cpp:758
ada::url_aggregator::get_username
std::string_view get_username() const ada_lifetime_bound
Definition url_aggregator.cpp:714
ada::url_aggregator::get_port
std::string_view get_port() const ada_lifetime_bound
Definition url_aggregator.cpp:734
ada::url_aggregator::to_diagram
std::string to_diagram() const
Definition url_aggregator.cpp:1273
ada::url_aggregator::has_hostname
constexpr bool has_hostname() const noexcept
Definition url_aggregator-inl.h:806
ada::url_aggregator::has_search
constexpr bool has_search() const noexcept override
Definition url_aggregator-inl.h:715
ada::url_aggregator::get_origin
std::string get_origin() const override
Definition url_aggregator.cpp:687
ada::url_aggregator::get_href
constexpr std::string_view get_href() const noexcept ada_lifetime_bound
Definition url_aggregator-inl.h:849
ada::url_aggregator::has_empty_hostname
constexpr bool has_empty_hostname() const noexcept
Definition url_aggregator-inl.h:793
ada::url_aggregator::has_password
constexpr bool has_password() const noexcept
Definition url_aggregator-inl.h:786
ada::url_aggregator::get_pathname_length
ada_really_inline uint32_t get_pathname_length() const noexcept
Definition url_aggregator-inl.h:154
ada::url_aggregator::has_valid_domain
bool has_valid_domain() const noexcept override
Definition url_aggregator.cpp:909
ada::url_aggregator::has_hash
constexpr bool has_hash() const noexcept override
Definition url_aggregator-inl.h:710
ada::url_aggregator::get_pathname
constexpr std::string_view get_pathname() const ada_lifetime_bound
Definition url_aggregator-inl.h:1094
ada::url_aggregator::get_hostname
std::string_view get_hostname() const ada_lifetime_bound
Definition url_aggregator.cpp:777
ada::url_aggregator::get_protocol
std::string_view get_protocol() const ada_lifetime_bound
Definition url_aggregator.cpp:810
ada::url_aggregator::has_port
constexpr bool has_port() const noexcept
Definition url_aggregator-inl.h:810
ada::url_aggregator::has_credentials
ada_really_inline constexpr bool has_credentials() const noexcept
Definition url_aggregator-inl.h:720
ada::url_aggregator::has_non_empty_username
constexpr bool has_non_empty_username() const noexcept
Definition url_aggregator-inl.h:776
ada::url
Represents a parsed URL with individual string components.
Definition url.h:62
ada::url::get_components
ada_really_inline ada::url_components get_components() const noexcept
Definition url-inl.h:50
ada::url::has_empty_hostname
bool has_empty_hostname() const noexcept
Definition url-inl.h:29
ada::url::has_port
bool has_port() const noexcept
Definition url-inl.h:22
ada::url::has_credentials
ada_really_inline bool has_credentials() const noexcept
Definition url-inl.h:19
ada::url::get_pathname_length
ada_really_inline size_t get_pathname_length() const noexcept
Definition url-inl.h:42
ada::url::get_search
std::string get_search() const
Definition url.cpp:659
ada::url::get_port
std::string get_port() const
Definition url.cpp:674
ada::url::get_protocol
std::string get_protocol() const
Definition url.cpp:633
ada::url::has_hostname
bool has_hostname() const noexcept
Definition url-inl.h:35
ada::url::get_host
std::string get_host() const
Definition url.cpp:641
ada::url::get_pathname
constexpr std::string_view get_pathname() const noexcept
Definition url-inl.h:46
ada::url::get_hostname
std::string get_hostname() const
Definition url.cpp:655
ada::url::get_href
ada_really_inline std::string get_href() const
Definition url-inl.h:188
ada::url::get_origin
std::string get_origin() const override
Definition url.cpp:607
ada::url::get_password
const std::string & get_password() const noexcept
Definition url.cpp:670
ada::url::get_username
const std::string & get_username() const noexcept
Definition url.cpp:666
ada::url::has_search
constexpr bool has_search() const noexcept override
Definition url-inl.h:164
ada::url::to_string
std::string to_string() const override
Definition url.cpp:550
ada::url::get_hash
std::string get_hash() const
Definition url.cpp:678
ada::url::has_hash
constexpr bool has_hash() const noexcept override
Definition url-inl.h:160
ada::url::has_valid_domain
bool has_valid_domain() const noexcept override
Definition url.cpp:600