From 0ab1ec0cbb113b745353cfd3d61d77a15f623ceb Mon Sep 17 00:00:00 2001 From: Enrico Deiana Date: Sat, 20 Jul 2024 02:09:51 -0700 Subject: [PATCH] i#6662 public traces, part 7: regdeps invariants (#6889) 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 #6662 --- ...r_on_regdeps_trace_ci_shared_app.templatex | 10 ++ ...n_regdeps_trace_kernel_xfer_app.templatex} | 0 .../tests/invariant_checker_test.cpp | 113 ++++++++++++++- .../drcachesim/tools/invariant_checker.cpp | 133 ++++++++++++++++++ clients/drcachesim/tools/invariant_checker.h | 5 + suite/tests/CMakeLists.txt | 25 +++- 6 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 clients/drcachesim/tests/invariant_checker_on_regdeps_trace_ci_shared_app.templatex rename clients/drcachesim/tests/{invariant_checker_on_regdeps_trace.templatex => invariant_checker_on_regdeps_trace_kernel_xfer_app.templatex} (100%) diff --git a/clients/drcachesim/tests/invariant_checker_on_regdeps_trace_ci_shared_app.templatex b/clients/drcachesim/tests/invariant_checker_on_regdeps_trace_ci_shared_app.templatex new file mode 100644 index 00000000000..4455acfb787 --- /dev/null +++ b/clients/drcachesim/tests/invariant_checker_on_regdeps_trace_ci_shared_app.templatex @@ -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. diff --git a/clients/drcachesim/tests/invariant_checker_on_regdeps_trace.templatex b/clients/drcachesim/tests/invariant_checker_on_regdeps_trace_kernel_xfer_app.templatex similarity index 100% rename from clients/drcachesim/tests/invariant_checker_on_regdeps_trace.templatex rename to clients/drcachesim/tests/invariant_checker_on_regdeps_trace_kernel_xfer_app.templatex diff --git a/clients/drcachesim/tests/invariant_checker_test.cpp b/clients/drcachesim/tests/invariant_checker_test.cpp index a3b6bf348c0..b398a2eb80f 100644 --- a/clients/drcachesim/tests/invariant_checker_test.cpp +++ b/clients/drcachesim/tests/invariant_checker_test.cpp @@ -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 { @@ -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 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 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 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 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(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 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[]) { @@ -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; } diff --git a/clients/drcachesim/tools/invariant_checker.cpp b/clients/drcachesim/tools/invariant_checker.cpp index a81c2f8df8f..f8f7aa91233 100644 --- a/clients/drcachesim/tools/invariant_checker.cpp +++ b/clients/drcachesim/tools/invariant_checker.cpp @@ -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) { @@ -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; } @@ -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(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(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(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 diff --git a/clients/drcachesim/tools/invariant_checker.h b/clients/drcachesim/tools/invariant_checker.h index 37b4221195c..6bf476ce563 100644 --- a/clients/drcachesim/tools/invariant_checker.h +++ b/clients/drcachesim/tools/invariant_checker.h @@ -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. diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index 59c8120d91e..05bfdcc42d6 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -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