[value-serializer] Verify deserialized JSRegExp flags

One of the serializer fuzzers passes in random data to the deserializer,
which can then be used to deserialize a JSRegExp instance with random flag
contents. This can cause issues since the JSRegExp::Flag enum statically
contains kDotAll - but it is only valid to set kDotAll iff
FLAG_harmony_regexp_dotall is set.

This CL verifies deserialized flags before constructing the JSRegExp
and bails out if they are invalid.

R=jbroman@chromium.org,yangguo@chromium.org
BUG=chromium:719280

Review-Url: https://codereview.chromium.org/2870743004
Cr-Commit-Position: refs/heads/master@{#45222}
This commit is contained in:
jgruber 2017-05-10 01:52:21 -07:00 committed by Commit bot
parent 0679765daf
commit 540419b660
2 changed files with 50 additions and 1 deletions

View File

@ -1463,11 +1463,22 @@ MaybeHandle<JSRegExp> ValueDeserializer::ReadJSRegExp() {
uint32_t raw_flags;
Handle<JSRegExp> regexp;
if (!ReadString().ToHandle(&pattern) ||
!ReadVarint<uint32_t>().To(&raw_flags) ||
!ReadVarint<uint32_t>().To(&raw_flags)) {
return MaybeHandle<JSRegExp>();
}
// Ensure the deserialized flags are valid. The context behind this is that
// the JSRegExp::Flags enum statically includes kDotAll, but it is only valid
// to set kDotAll if FLAG_harmony_regexp_dotall is enabled. Fuzzers don't
// know about this and happily set kDotAll anyways, leading to CHECK failures
// later on.
uint32_t flags_mask = static_cast<uint32_t>(-1) << JSRegExp::FlagCount();
if ((raw_flags & flags_mask) ||
!JSRegExp::New(pattern, static_cast<JSRegExp::Flags>(raw_flags))
.ToHandle(&regexp)) {
return MaybeHandle<JSRegExp>();
}
AddObjectWithID(id, regexp);
return regexp;
}

View File

@ -1603,6 +1603,44 @@ TEST_F(ValueSerializerTest, DecodeRegExp) {
});
}
// Tests that invalid flags are not accepted by the deserializer. In particular,
// the dotAll flag ('s') is only valid when the corresponding flag is enabled.
TEST_F(ValueSerializerTest, DecodeRegExpDotAll) {
i::FLAG_harmony_regexp_dotall = false;
DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x1f},
[this](Local<Value> value) {
ASSERT_TRUE(value->IsRegExp());
EXPECT_TRUE(EvaluateScriptForResultBool(
"Object.getPrototypeOf(result) === RegExp.prototype"));
EXPECT_TRUE(EvaluateScriptForResultBool(
"result.toString() === '/foo/gimuy'"));
});
InvalidDecodeTest(
{0xff, 0x09, 0x3f, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x3f});
InvalidDecodeTest(
{0xff, 0x09, 0x3f, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x7f});
i::FLAG_harmony_regexp_dotall = true;
DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x1f},
[this](Local<Value> value) {
ASSERT_TRUE(value->IsRegExp());
EXPECT_TRUE(EvaluateScriptForResultBool(
"Object.getPrototypeOf(result) === RegExp.prototype"));
EXPECT_TRUE(EvaluateScriptForResultBool(
"result.toString() === '/foo/gimuy'"));
});
DecodeTest({0xff, 0x09, 0x3f, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x3f},
[this](Local<Value> value) {
ASSERT_TRUE(value->IsRegExp());
EXPECT_TRUE(EvaluateScriptForResultBool(
"Object.getPrototypeOf(result) === RegExp.prototype"));
EXPECT_TRUE(EvaluateScriptForResultBool(
"result.toString() === '/foo/gimsuy'"));
});
InvalidDecodeTest(
{0xff, 0x09, 0x3f, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, 0x7f});
}
TEST_F(ValueSerializerTest, RoundTripMap) {
RoundTripTest(
"(() => { var m = new Map(); m.set(42, 'foo'); return m; })()",