Skip to content

Commit

Permalink
i#6662 public traces, part 7: regdeps invariants (DynamoRIO#6889)
Browse files Browse the repository at this point in the history
OFFLINE_FILE_TYPE_ARCH_REGDEPS traces have specific properties.

Instructions always have OP_UNDECODED as opcode, ISA mode is
DR_ISA_REGDEPS, operands are only registers, there is no distinction
between what specific arithmetic flags are read or written, and their
size
must be a multiple of 4, which is their required byte alignment.

Some markers are not allowed and are removed in the filtering process.
Specifically: TRACE_MARKER_TYPE_SYSCALL_IDX,
TRACE_MARKER_TYPE_SYSCALL,
TRACE_MARKER_TYPE_SYSCALL_TRACE_START,
TRACE_MARKER_TYPE_SYSCALL_TRACE_END,
TRACE_MARKER_TYPE_SYSCALL_FAILED are not allowed in
OFFLINE_FILE_TYPE_ARCH_REGDEPS traces.
Function related markers (i.e.,
TRACE_MARKER_TYPE_FUNC_[ID | ARG | RETVAL | RETADDR]) are allowed
only if the associated value of TRACE_MARKER_TYPE_FUNC_ID is the ID of
the SYS_futex system call. All other markers are allowed.

We add checks (in a new separate function: `check_regdeps_invariants()`)
in the invariant_checker tool to validate these properties.

We add unit tests to check the firing of regdeps invariant errors
related to
markers. The `invariant_checker_on_regdeps_trace_kernel_xfer_app` and
the newly added `invariant_checker_on_regdeps_trace_ci_shared_app`also
test these regdeps invariants (for both markers and instructions) by
running
invariant_checker on an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace.

Issue DynamoRIO#6662
  • Loading branch information
edeiana authored Jul 20, 2024
1 parent e099287 commit 0ab1ec0
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Hello, world!

Trace invariant checks passed

Output .* entries from .* entries.

Trace invariant checks passed

WARNING: invariant_checker is being run on an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace.
Some invariant checks have been disabled.
113 changes: 112 additions & 1 deletion clients/drcachesim/tests/invariant_checker_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
#include "../tools/invariant_checker.h"
#include "../common/memref.h"
#include "memref_gen.h"
#ifdef LINUX
# include "../../core/unix/include/syscall_target.h"
#endif

namespace dynamorio {
namespace drmemtrace {
Expand Down Expand Up @@ -3532,6 +3535,114 @@ check_has_instructions(void)
return true;
}

bool
check_regdeps(void)
{
std::cerr << "Testing regdeps traces\n";
// Incorrect: TRACE_MARKER_TYPE_SYSCALL_IDX not allowed.
{
std::vector<memref_t> memrefs = {
gen_marker(TID_A, TRACE_MARKER_TYPE_FILETYPE, OFFLINE_FILE_TYPE_ARCH_REGDEPS),
gen_marker(TID_A, TRACE_MARKER_TYPE_CACHE_LINE_SIZE, 64),
gen_marker(TID_A, TRACE_MARKER_TYPE_PAGE_SIZE, 4096),
gen_marker(TID_A, TRACE_MARKER_TYPE_SYSCALL_IDX, 102),
gen_instr(TID_A),
gen_exit(TID_A),
};
if (!run_checker(
memrefs, true,
{ "OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_SYSCALL_IDX markers",
/*tid=*/TID_A,
/*ref_ordinal=*/4, /*last_timestamp=*/0,
/*instrs_since_last_timestamp=*/0 },
"Failed to catch non-allowed TRACE_MARKER_TYPE_SYSCALL_IDX marker"))
return false;
}

// Incorrect: TRACE_MARKER_TYPE_SYSCALL not allowed.
{
std::vector<memref_t> memrefs = {
gen_marker(TID_A, TRACE_MARKER_TYPE_FILETYPE,
OFFLINE_FILE_TYPE_SYSCALL_NUMBERS |
OFFLINE_FILE_TYPE_ARCH_REGDEPS),
gen_marker(TID_A, TRACE_MARKER_TYPE_CACHE_LINE_SIZE, 64),
gen_marker(TID_A, TRACE_MARKER_TYPE_PAGE_SIZE, 4096),
gen_marker(TID_A, TRACE_MARKER_TYPE_SYSCALL, 102),
gen_instr(TID_A),
gen_exit(TID_A),
};
if (!run_checker(memrefs, true,
{ "OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_SYSCALL markers",
/*tid=*/TID_A,
/*ref_ordinal=*/4, /*last_timestamp=*/0,
/*instrs_since_last_timestamp=*/0 },
"Failed to catch non-allowed TRACE_MARKER_TYPE_SYSCALL marker"))
return false;
}

#ifdef LINUX
// Incorrect: non SYS_futex TRACE_MARKER_TYPE_FUNC_ID not allowed.
{
std::vector<memref_t> memrefs = {
gen_marker(TID_A, TRACE_MARKER_TYPE_FILETYPE, OFFLINE_FILE_TYPE_ARCH_REGDEPS),
gen_marker(TID_A, TRACE_MARKER_TYPE_CACHE_LINE_SIZE, 64),
gen_marker(TID_A, TRACE_MARKER_TYPE_PAGE_SIZE, 4096),
gen_marker(TID_A, TRACE_MARKER_TYPE_FUNC_ID, 102),
gen_instr(TID_A),
gen_exit(TID_A),
};
if (!run_checker(memrefs, true,
{ "OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_FUNC_ID markers related to functions that "
"are not SYS_futex",
/*tid=*/TID_A,
/*ref_ordinal=*/4, /*last_timestamp=*/0,
/*instrs_since_last_timestamp=*/0 },
"Failed to catch non-allowed TRACE_MARKER_TYPE_FUNC_ID marker"))
return false;
}

// Correct: SYS_futex TRACE_MARKER_TYPE_FUNC_ID allowed.
{
std::vector<memref_t> memrefs = {
gen_marker(TID_A, TRACE_MARKER_TYPE_FILETYPE, OFFLINE_FILE_TYPE_ARCH_REGDEPS),
gen_marker(TID_A, TRACE_MARKER_TYPE_CACHE_LINE_SIZE, 64),
gen_marker(TID_A, TRACE_MARKER_TYPE_PAGE_SIZE, 4096),
gen_marker(TID_A, TRACE_MARKER_TYPE_FUNC_ID,
static_cast<uintptr_t>(func_trace_t::TRACE_FUNC_ID_SYSCALL_BASE) +
SYS_futex),
gen_instr(TID_A),
gen_exit(TID_A),
};
if (!run_checker(memrefs, false))
return false;
}
#else
// Correct: a non-LINUX DR build should always succeed when checking
// TRACE_MARKER_TYPE_FUNC_ID markers, as we cannot determine if the function ID of
// the TRACE_MARKER_TYPE_FUNC_ID marker is allowed in the
// OFFLINE_FILE_TYPE_ARCH_REGDEPS trace because we cannot determine if function ID is
// SYS_futex or not. For this reason the TRACE_MARKER_TYPE_FUNC_ID invariant check is
// disbled, we print a warning instead.
{
std::vector<memref_t> memrefs = {
gen_marker(TID_A, TRACE_MARKER_TYPE_FILETYPE, OFFLINE_FILE_TYPE_ARCH_REGDEPS),
gen_marker(TID_A, TRACE_MARKER_TYPE_CACHE_LINE_SIZE, 64),
gen_marker(TID_A, TRACE_MARKER_TYPE_PAGE_SIZE, 4096),
gen_marker(TID_A, TRACE_MARKER_TYPE_FUNC_ID, 102),
gen_instr(TID_A),
gen_exit(TID_A),
};
if (!run_checker(memrefs, false))
return false;
}
#endif

return true;
}

int
test_main(int argc, const char *argv[])
{
Expand All @@ -3543,7 +3654,7 @@ test_main(int argc, const char *argv[])
check_timestamps_increase_monotonically() &&
check_read_write_records_match_operands() && check_exit_found() &&
check_kernel_syscall_trace() && check_has_instructions() &&
check_kernel_context_switch_trace()) {
check_kernel_context_switch_trace() && check_regdeps()) {
std::cerr << "invariant_checker_test passed\n";
return 0;
}
Expand Down
133 changes: 133 additions & 0 deletions clients/drcachesim/tools/invariant_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,17 @@
#include "invariant_checker_create.h"
#include "trace_entry.h"
#include "utils.h"
#ifdef LINUX
# include "../../core/unix/include/syscall_target.h"
#endif

namespace dynamorio {
namespace drmemtrace {

// We don't expose the alignment requirement for DR_ISA_REGDEPS instructions (4 bytes),
// so we duplicate it here.
#define REGDEPS_ALIGN_BYTES 4

analysis_tool_t *
invariant_checker_create(bool offline, unsigned int verbose)
{
Expand Down Expand Up @@ -1233,6 +1240,10 @@ invariant_checker_t::parallel_shard_memref(void *shard_data, const memref_t &mem
}
}
}
// Run additional checks for OFFLINE_FILE_TYPE_ARCH_REGDEPS traces.
if (TESTANY(OFFLINE_FILE_TYPE_ARCH_REGDEPS, shard->file_type_))
check_regdeps_invariants(shard, memref);

return true;
}

Expand Down Expand Up @@ -1573,5 +1584,127 @@ invariant_checker_t::check_for_pc_discontinuity(
return error_msg;
}

void
invariant_checker_t::check_regdeps_invariants(per_shard_t *shard, const memref_t &memref)
{
// Check instructions.
if (type_is_instr(memref.instr.type)) {
const bool expect_encoding =
TESTANY(OFFLINE_FILE_TYPE_ENCODINGS, shard->file_type_);
if (expect_encoding) {
// Decode memref_t to get an instr_t we can analyze.
const app_pc trace_pc = reinterpret_cast<app_pc>(memref.instr.addr);
instr_noalloc_t noalloc;
instr_noalloc_init(drcontext_, &noalloc);
instr_t *noalloc_instr = instr_from_noalloc(&noalloc);
const app_pc encoding_addr = const_cast<app_pc>(memref.instr.encoding);
app_pc next_pc =
decode_from_copy(drcontext_, encoding_addr, trace_pc, noalloc_instr);
bool instr_is_decoded = next_pc != nullptr;
report_if_false(
shard, instr_is_decoded,
"DR_ISA_REGDEPS instructions should always succeed during decoding");
// We still check this condition in case we don't abort on invariant errors.
if (instr_is_decoded) {
// ISA mode should be DR_ISA_REGDEPS
report_if_false(shard,
instr_get_isa_mode(noalloc_instr) == DR_ISA_REGDEPS,
"DR_ISA_REGDEPS instruction has incorrect ISA mode");
// Only OP_UNDECODED as opcode are allowed.
report_if_false(shard, instr_get_opcode(noalloc_instr) == OP_UNDECODED,
"DR_ISA_REGDEPS instruction opcode is not OP_UNDECODED");
// Only register operands are allowed.
int num_dsts = instr_num_dsts(noalloc_instr);
for (int dst_index = 0; dst_index < num_dsts; ++dst_index) {
opnd_t dst_opnd = instr_get_dst(noalloc_instr, dst_index);
report_if_false(shard, opnd_is_reg(dst_opnd),
"DR_ISA_REGDEPS instruction destination operand is "
"not a register");
}
int num_srcs = instr_num_srcs(noalloc_instr);
for (int src_index = 0; src_index < num_srcs; ++src_index) {
opnd_t src_opnd = instr_get_src(noalloc_instr, src_index);
report_if_false(
shard, opnd_is_reg(src_opnd),
"DR_ISA_REGDEPS instruction source operand is not a register");
}
// Arithmetic flags should either be 0 or all read or all written or both,
// but nothing in between, as we don't expose individual flags.
uint eflags = instr_get_arith_flags(noalloc_instr, DR_QUERY_DEFAULT);
report_if_false(
shard,
eflags == 0 || eflags == EFLAGS_WRITE_ARITH ||
eflags == EFLAGS_READ_ARITH ||
eflags == (EFLAGS_WRITE_ARITH | EFLAGS_READ_ARITH),
"DR_ISA_REGDEPS instruction has incorrect arithmetic flags");
// Instruction length should be a multiple of 4 bytes
// (i.e., REGDEPS_ALIGN_BYTES).
report_if_false(
shard, ((next_pc - encoding_addr) % REGDEPS_ALIGN_BYTES) == 0,
"DR_ISA_REGDEPS instruction has incorrect length, it's not a "
"multiple of REGDEPS_ALIGN_BYTES = 4");
}
}
}

// Check markers.
if (memref.marker.type == TRACE_TYPE_MARKER) {
switch (memref.marker.marker_type) {
case TRACE_MARKER_TYPE_SYSCALL_IDX:
report_if_false(shard, false,
"OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_SYSCALL_IDX markers");
break;
case TRACE_MARKER_TYPE_SYSCALL:
report_if_false(shard, false,
"OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_SYSCALL markers");
break;
case TRACE_MARKER_TYPE_SYSCALL_TRACE_START:
report_if_false(shard, false,
"OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_SYSCALL_TRACE_START markers");
break;
case TRACE_MARKER_TYPE_SYSCALL_TRACE_END:
report_if_false(shard, false,
"OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_SYSCALL_TRACE_END markers");
break;
case TRACE_MARKER_TYPE_SYSCALL_FAILED:
report_if_false(shard, false,
"OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_SYSCALL_FAILED markers");
break;
// This case also covers TRACE_MARKER_TYPE_FUNC_RETADDR,
// TRACE_MARKER_TYPE_FUNC_RETVAL, and TRACE_MARKER_TYPE_FUNC_ARG, since these
// markers are always preceed by TRACE_MARKER_TYPE_FUNC_ID.
case TRACE_MARKER_TYPE_FUNC_ID: {
// In OFFLINE_FILE_TYPE_ARCH_REGDEPS traces the only TRACE_MARKER_TYPE_FUNC_
// markers allowed are those related to SYS_futex. We can only check that
// for LINUX builds of DR, otherwise we disable this check and print a
// warning instead.
#ifdef LINUX
report_if_false(
shard,
memref.marker.marker_value ==
static_cast<uintptr_t>(func_trace_t::TRACE_FUNC_ID_SYSCALL_BASE) +
SYS_futex,
"OFFLINE_FILE_TYPE_ARCH_REGDEPS traces cannot have "
"TRACE_MARKER_TYPE_FUNC_ID markers related to functions that "
"are not SYS_futex");
#else
std::cerr << "WARNING: we cannot determine whether the function ID of "
"TRACE_MARKER_TYPE_FUNC_ID is allowed in an "
"OFFLINE_FILE_TYPE_ARCH_REGDEPS trace because DynamoRIO "
"was not built on a Linux platform.\n";
#endif
} break;
default:
// All other markers are allowed.
break;
}
}
}

} // namespace drmemtrace
} // namespace dynamorio
5 changes: 5 additions & 0 deletions clients/drcachesim/tools/invariant_checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ class invariant_checker_t : public analysis_tool_t {
const per_shard_t::instr_info_t &cur_memref_info,
bool expect_encoding, bool at_kernel_event);

// Check for invariant violations related to OFFLINE_FILE_TYPE_ARCH_REGDEPS traces.
// Checks both instructions and markers.
void
check_regdeps_invariants(per_shard_t *shard, const memref_t &memref);

#ifdef X86
// Whether the expected write entry count check should be relaxed for the kernel
// part of the trace.
Expand Down
25 changes: 22 additions & 3 deletions suite/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4750,10 +4750,29 @@ if (BUILD_CLIENTS)
endif ()

if (X86 AND X64 AND UNIX AND NOT APPLE)
# Run the invariant_checker on an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace.
set(testname "tool.invariant_checker_on_regdeps_trace")
# Run the invariant_checker on an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace of
# ci_shared_app.
set(testname "tool.invariant_checker_on_regdeps_trace_ci_shared_app")
torun_record_filter("${testname}" ${ci_shared_app}
"invariant_checker_on_regdeps_trace_ci_shared_app"
# Generate an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace by running record_filter
# with -filter_encodings2regdeps to change instruction encodings,
# -filter_keep_func_ids 4294967498 (which is SYS_futex, associated to the only
# TRACE_MARKER_TYPE_FUNC_ markers we want to keep), and
# -filter_marker_types 19,25,27,28,30 (which correspond to
# TRACE_MARKER_TYPE_SYSCALL_IDX, TRACE_MARKER_TYPE_SYSCALL,
# TRACE_MARKER_TYPE_SYSCALL_TRACE_START, TRACE_MARKER_TYPE_SYSCALL_TRACE_END,
# TRACE_MARKER_TYPE_SYSCALL_FAILED).
"${drcachesim_path}@-simulator_type@record_filter@-filter_encodings2regdeps@-indir@${testname}.${ci_shared_app}.*.dir/trace@-outdir@${testname}.filtered.dir@-filter_marker_types@19,25,27,28,30@-filter_keep_func_ids@4294967498"
# We run the invariant_checker analyzer on the REGDEPS filtered trace.
# We expect no invariant errors.
"invariant_checker")

# Run the invariant_checker on an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace of
# kernel_xfer_app.
set(testname "tool.invariant_checker_on_regdeps_trace_kernel_xfer_app")
torun_record_filter("${testname}" ${kernel_xfer_app}
"invariant_checker_on_regdeps_trace"
"invariant_checker_on_regdeps_trace_kernel_xfer_app"
# Generate an OFFLINE_FILE_TYPE_ARCH_REGDEPS trace by running record_filter
# with -filter_encodings2regdeps to change instruction encodings,
# -filter_keep_func_ids 4294967498 (which is SYS_futex, associated to the only
Expand Down

0 comments on commit 0ab1ec0

Please sign in to comment.