blob: 3b4983a5d1b1cb9a65e8d6fbc909c99c1595394b [file] [log] [blame]
// Copyright 2018 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.
// The C++ style guide recommends using <re2> instead of <regex>. However, the
// former isn't available in V8.
#include <regex> // NOLINT(build/c++11)
#include "src/api/api-inl.h"
#include "src/diagnostics/disassembler.h"
#include "src/objects-inl.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
std::string DisassembleFunction(const char* function) {
v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext();
Handle<JSFunction> f = Handle<JSFunction>::cast(
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
CcTest::global()->Get(context, v8_str(function)).ToLocalChecked())));
Address begin = f->code().raw_instruction_start();
Address end = f->code().raw_instruction_end();
Isolate* isolate = CcTest::i_isolate();
std::ostringstream os;
Disassembler::Decode(isolate, &os, reinterpret_cast<byte*>(begin),
reinterpret_cast<byte*>(end),
CodeReference(handle(f->code(), isolate)));
return os.str();
}
struct Matchers {
std::string start = "0x[0-9a-f]+ +[0-9a-f]+ +[0-9a-f]+ +";
std::regex map_load_re =
std::regex(start + "ldr r([0-9]+), \\[r([0-9]+), #-1\\]");
std::regex load_const_re = std::regex(start + "ldr r([0-9]+), \\[pc, .*");
std::regex cmp_re = std::regex(start + "cmp r([0-9]+), r([0-9]+)");
std::regex bne_re = std::regex(start + "bne (.*)");
std::regex beq_re = std::regex(start + "beq (.*)");
std::regex b_re = std::regex(start + "b (.*)");
std::regex eorne_re =
std::regex(start + "eorne r([0-9]+), r([0-9]+), r([0-9]+)");
std::regex eoreq_re =
std::regex(start + "eoreq r([0-9]+), r([0-9]+), r([0-9]+)");
std::regex csdb_re = std::regex(start + "csdb");
std::regex load_field_re =
std::regex(start + "ldr r([0-9]+), \\[r([0-9]+), #\\+[0-9]+\\]");
std::regex mask_re =
std::regex(start + "and r([0-9]+), r([0-9]+), r([0-9]+)");
std::regex untag_re = std::regex(start + "mov r([0-9]+), r([0-9]+), asr #1");
std::string poison_reg = "9";
};
TEST(DisasmPoisonMonomorphicLoad) {
#ifdef ENABLE_DISASSEMBLER
if (i::FLAG_always_opt || !i::FLAG_opt) return;
i::FLAG_allow_natives_syntax = true;
i::FLAG_untrusted_code_mitigations = true;
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
CompileRun(
"function mono(o) { return o.x; };"
"%PrepareFunctionForOptimization(mono);"
"mono({ x : 1 });"
"mono({ x : 1 });"
"%OptimizeFunctionOnNextCall(mono);"
"mono({ x : 1 });");
Matchers m;
std::smatch match;
std::string line;
std::istringstream reader(DisassembleFunction("mono"));
bool poisoning_sequence_found = false;
while (std::getline(reader, line)) {
if (std::regex_match(line, match, m.map_load_re)) {
std::string map_reg = match[1];
std::string object_reg = match[2];
// Matches that the property access sequence is instrumented with
// poisoning. We match the following sequence:
//
// ldr r1, [r0, #-1] ; load map
// ldr r2, [pc, #+104] ; load expected map constant
// cmp r1, r2 ; compare maps
// bne ... ; deopt if different
// eorne r9, r9, r9 ; update the poison
// csdb ; speculation barrier
// ldr r0, [r0, #+11] ; load the field
// and r0, r0, r9 ; apply the poison
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.load_const_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.cmp_re));
CHECK_EQ(match[1], map_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.bne_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.eorne_re));
CHECK_EQ(match[1], m.poison_reg);
CHECK_EQ(match[2], m.poison_reg);
CHECK_EQ(match[3], m.poison_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.csdb_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.load_field_re));
CHECK_EQ(match[2], object_reg);
std::string field_reg = match[1];
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.mask_re));
CHECK_EQ(match[1], field_reg);
CHECK_EQ(match[2], field_reg);
CHECK_EQ(match[3], m.poison_reg);
poisoning_sequence_found = true;
break;
}
}
CHECK(poisoning_sequence_found);
#endif // ENABLE_DISASSEMBLER
}
TEST(DisasmPoisonPolymorphicLoad) {
#ifdef ENABLE_DISASSEMBLER
if (i::FLAG_always_opt || !i::FLAG_opt) return;
i::FLAG_allow_natives_syntax = true;
i::FLAG_untrusted_code_mitigations = true;
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
CompileRun(
"function poly(o) { return o.x + 1; };"
"let o1 = { x : 1 };"
"let o2 = { y : 1 };"
"o2.x = 2;"
"%PrepareFunctionForOptimization(poly);"
"poly(o1);"
"poly(o2);"
"poly(o1);"
"poly(o2);"
"%OptimizeFunctionOnNextCall(poly);"
"poly(o1);");
Matchers m;
std::smatch match;
std::string line;
std::istringstream reader(DisassembleFunction("poly"));
bool poisoning_sequence_found = false;
while (std::getline(reader, line)) {
if (std::regex_match(line, match, m.map_load_re)) {
std::string map_reg = match[1];
std::string object_reg = match[2];
// Matches that the property access sequence is instrumented with
// poisoning. We match the following sequence:
//
// ldr r1, [r0, #-1] ; load map
// ldr r2, [pc, #+104] ; load map constant #1
// cmp r1, r2 ; compare maps
// beq +Lcase1 ; if match, got to the load
// eoreq r9, r9, r9 ; update the poison
// csdb ; speculation barrier
// ldr r1, [r0, #-1] ; load map
// ldr r2, [pc, #+304] ; load map constant #2
// cmp r1, r2 ; compare maps
// bne +Ldeopt ; deopt if different
// eorne r9, r9, r9 ; update the poison
// csdb ; speculation barrier
// ldr r0, [r0, #+11] ; load the field
// and r0, r0, r9 ; apply the poison
// mov r0, r0, asr #1 ; untag
// b +Ldone ; goto merge point
// Lcase1:
// eorne r9, r9, r9 ; update the poison
// csdb ; speculation barrier
// ldr r0, [r0, #+3] ; load property backing store
// and r0, r0, r9 ; apply the poison
// ldr r0, [r0, #+3] ; load the property
// and r0, r0, r9 ; apply the poison
// Ldone:
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.load_const_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.cmp_re));
CHECK_EQ(match[1], map_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.beq_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.eoreq_re));
CHECK_EQ(match[1], m.poison_reg);
CHECK_EQ(match[2], m.poison_reg);
CHECK_EQ(match[3], m.poison_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.csdb_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.map_load_re));
map_reg = match[1];
CHECK_EQ(match[2], object_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.load_const_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.cmp_re));
CHECK_EQ(match[1], map_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.bne_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.eorne_re));
CHECK_EQ(match[1], m.poison_reg);
CHECK_EQ(match[2], m.poison_reg);
CHECK_EQ(match[3], m.poison_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.csdb_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.load_field_re));
CHECK_EQ(match[2], object_reg);
std::string field_reg = match[1];
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.mask_re));
CHECK_EQ(match[1], field_reg);
CHECK_EQ(match[2], field_reg);
CHECK_EQ(match[3], m.poison_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.untag_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.b_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.eorne_re));
CHECK_EQ(match[1], m.poison_reg);
CHECK_EQ(match[2], m.poison_reg);
CHECK_EQ(match[3], m.poison_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.csdb_re));
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.load_field_re));
CHECK_EQ(match[2], object_reg);
std::string storage_reg = match[1];
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.mask_re));
CHECK_EQ(match[1], storage_reg);
CHECK_EQ(match[2], storage_reg);
CHECK_EQ(match[3], m.poison_reg);
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.load_field_re));
CHECK_EQ(match[2], storage_reg);
field_reg = match[1];
CHECK(std::getline(reader, line));
CHECK(std::regex_match(line, match, m.mask_re));
CHECK_EQ(match[1], field_reg);
CHECK_EQ(match[2], field_reg);
CHECK_EQ(match[3], m.poison_reg);
poisoning_sequence_found = true;
break;
}
}
CHECK(poisoning_sequence_found);
#endif // ENABLE_DISASSEMBLER
}
} // namespace internal
} // namespace v8