node/deps/v8/test/unittests/regexp/regexp-fuzzer.cc
Michaël Zasso 9d7cd9b864
deps: update V8 to 12.8.374.13
PR-URL: https://github.com/nodejs/node/pull/54077
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Richard Lau <rlau@redhat.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
2024-08-16 16:03:01 +02:00

180 lines
6.1 KiB
C++

// Copyright 2024 the V8 project 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 "src/regexp/regexp.h"
#include "test/unittests/fuzztest.h"
#include "test/unittests/regexp/regexp-grammar.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace {
using RegExpFlag = internal::RegExpFlag;
template <class T>
class RegExpTest : public fuzztest::PerFuzzTestFixtureAdapter<TestWithContext> {
public:
RegExpTest()
: context_(context()),
isolate_(isolate()),
i_isolate_(reinterpret_cast<i::Isolate*>(isolate_)),
factory_(i_isolate_->factory()) {
internal::v8_flags.expose_gc = true;
}
~RegExpTest() override = default;
void RunRegExp(const std::string&, const i::RegExpFlags&,
const std::vector<T>&);
protected:
virtual i::Handle<i::String> CreateString(v8::base::Vector<const T>) = 0;
void Test(i::Handle<i::JSRegExp>, i::Handle<i::String>);
Local<Context> context_;
Isolate* isolate_;
i::Isolate* i_isolate_;
i::Factory* factory_;
};
// Domain over all combinations of regexp flags.
static fuzztest::Domain<i::RegExpFlags> ArbitraryFlags() {
// The unicode and unicode_sets bits are incompatible.
auto bits_supporting_unicode = fuzztest::BitFlagCombinationOf(
{RegExpFlag::kHasIndices, RegExpFlag::kGlobal, RegExpFlag::kIgnoreCase,
RegExpFlag::kMultiline, RegExpFlag::kSticky, RegExpFlag::kUnicode,
RegExpFlag::kDotAll});
auto bits_supporting_unicode_sets = fuzztest::BitFlagCombinationOf(
{RegExpFlag::kHasIndices, RegExpFlag::kGlobal, RegExpFlag::kIgnoreCase,
RegExpFlag::kMultiline, RegExpFlag::kSticky, RegExpFlag::kUnicodeSets,
RegExpFlag::kDotAll});
auto bits =
fuzztest::OneOf(bits_supporting_unicode, bits_supporting_unicode_sets);
auto flags = fuzztest::Map(
[](auto bits) { return static_cast<i::RegExpFlags>(bits); }, bits);
// Filter out any other incompatibilities.
return fuzztest::Filter(
[](i::RegExpFlags f) { return i::RegExp::VerifyFlags(f); }, flags);
}
// Domain over bytes for a test string to test regular expressions on.
// The resulting strings will consist of a fixed example, simple strings
// of just a, b and space, strings with printable ascii characters and
// strings with arbitrary characters.
template <typename T>
static fuzztest::Domain<std::vector<T>> ArbitraryBytes(
const std::vector<T>& example) {
auto signed_to_unsigned = [](const char& cr) { return static_cast<T>(cr); };
auto just_example = fuzztest::Just(example);
auto simple_char = fuzztest::Map(
signed_to_unsigned,
fuzztest::OneOf(fuzztest::InRange('a', 'b'), fuzztest::Just(' ')));
auto simple_chars =
fuzztest::ContainerOf<std::vector<T>>(simple_char).WithMaxSize(10);
auto printable_char =
fuzztest::Map(signed_to_unsigned, fuzztest::PrintableAsciiChar());
auto printable_chars =
fuzztest::ContainerOf<std::vector<T>>(printable_char).WithMaxSize(10);
auto arbitrary_chars =
fuzztest::ContainerOf<std::vector<T>>(fuzztest::Arbitrary<T>())
.WithMaxSize(10);
return fuzztest::OneOf(just_example, simple_chars, printable_chars,
arbitrary_chars);
}
static fuzztest::Domain<std::vector<uint8_t>> ArbitraryOneBytes() {
return ArbitraryBytes<uint8_t>(
std::vector<uint8_t>{'f', 'o', 'o', 'b', 'a', 'r'});
}
static fuzztest::Domain<std::vector<v8::base::uc16>> ArbitraryTwoBytes() {
return ArbitraryBytes<v8::base::uc16>(
std::vector<v8::base::uc16>{'f', 0xD83D, 0xDCA9, 'b', 'a', 0x2603});
}
template <class T>
void RegExpTest<T>::Test(i::Handle<i::JSRegExp> regexp,
i::Handle<i::String> subject) {
v8::TryCatch try_catch(isolate_);
// Exceptions will be swallowed by the try/catch above.
USE(i::RegExp::Exec(i_isolate_, regexp, subject, 0,
i::RegExpMatchInfo::New(i_isolate_, 2)));
}
template <class T>
void RegExpTest<T>::RunRegExp(const std::string& regexp_input,
const i::RegExpFlags& flags,
const std::vector<T>& test_input) {
CHECK(!i_isolate_->has_exception());
if (regexp_input.size() > INT_MAX) return;
// Convert input string.
i::MaybeHandle<i::String> maybe_source =
factory_->NewStringFromUtf8(v8::base::CStrVector(regexp_input.c_str()));
i::Handle<i::String> source;
if (!maybe_source.ToHandle(&source)) {
i_isolate_->clear_exception();
return;
}
// Create regexp.
i::Handle<i::JSRegExp> regexp;
{
CHECK(!i_isolate_->has_exception());
v8::TryCatch try_catch_inner(isolate_);
i::MaybeHandle<i::JSRegExp> maybe_regexp = i::JSRegExp::New(
i_isolate_, source, i::JSRegExp::AsJSRegExpFlags(flags),
/*backtrack_limit*/ 1000000);
if (!maybe_regexp.ToHandle(&regexp)) {
i_isolate_->clear_exception();
return;
}
}
// Convert input bytes for the subject string.
auto subject = CreateString(
v8::base::Vector<const T>(test_input.data(), test_input.size()));
// Test the regexp on the subject, itself and an empty string.
Test(regexp, subject);
Test(regexp, source);
Test(regexp, factory_->empty_string());
isolate_->RequestGarbageCollectionForTesting(
v8::Isolate::kFullGarbageCollection);
CHECK(!i_isolate_->has_exception());
}
class RegExpOneByteTest : public RegExpTest<uint8_t> {
protected:
i::Handle<i::String> CreateString(
v8::base::Vector<const uint8_t> test_input) {
return factory_->NewStringFromOneByte(test_input).ToHandleChecked();
}
};
V8_FUZZ_TEST_F(RegExpOneByteTest, RunRegExp)
.WithDomains(fuzztest::internal_no_adl::InPatternGrammar(),
ArbitraryFlags(), ArbitraryOneBytes());
class RegExpTwoByteTest : public RegExpTest<v8::base::uc16> {
protected:
i::Handle<i::String> CreateString(
v8::base::Vector<const v8::base::uc16> test_input) {
return factory_->NewStringFromTwoByte(test_input).ToHandleChecked();
}
};
V8_FUZZ_TEST_F(RegExpTwoByteTest, RunRegExp)
.WithDomains(fuzztest::internal_no_adl::InPatternGrammar(),
ArbitraryFlags(), ArbitraryTwoBytes());
} // namespace
} // namespace v8