diff --git a/src/record_syscall.cc b/src/record_syscall.cc index 4e40a1d5f3e..4c928dcc144 100644 --- a/src/record_syscall.cc +++ b/src/record_syscall.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1587,6 +1588,105 @@ template void prepare_ethtool_ioctl(RecordTask* t, TaskSyscallSt syscall_state.after_syscall_action(record_page_below_stack_ptr); } +template struct TD; + +template +static void prepare_rdma_verbs_ioctl(__attribute__((unused)) RecordTask* t, + __attribute__((unused)) TaskSyscallState& syscall_state) +{ + auto ®s = syscall_state.syscall_entry_registers; + remote_ptr argp = regs.arg(3); + auto hdrp = argp.cast(); + if (hdrp.is_null()) { + LOG(fatal) << "arg3 is NULL."; + // TODO(sodar): How to handle it correctly? + syscall_state.expect_errno = EFAULT; + return; + } + + bool ok = true; + auto hdr = t->read_mem(hdrp, &ok); + if (!ok) { + syscall_state.expect_errno = EFAULT; + return; + } + + LOG(debug) + << "hdr->length=" << hdr.length + << " hdr->object_id=" << hdr.object_id + << " hdr->method_id=" << hdr.method_id + << " hdr->num_attrs=" << hdr.num_attrs; + + auto size = hdr.length; + auto hdrv = syscall_state.reg_parameter(3, size, IN_OUT); + if (hdrv.is_null()) { + LOG(fatal) << "hdrv is NULL."; + // TODO(sodar): How to handle it correctly? + syscall_state.expect_errno = EFAULT; + return; + } + + // TODO(sodar): Do using real structs... + // TD td; + auto attrs_field_p = hdrp.template cast() + sizeof(typename Arch::ib_uverbs_ioctl_hdr); + auto attrs_p = attrs_field_p.template cast(); + for (unsigned int i = 0; i < hdr.num_attrs; ++i) { + auto attr_p = attrs_p + i; + auto attr = t->read_mem(attr_p, &ok); + ASSERT(t, ok) << "failed to read attrs[" << i << "]"; + + if (hdr.object_id == UVERBS_OBJECT_DEVICE && hdr.method_id == UVERBS_METHOD_INVOKE_WRITE) { + switch (attr.attr_id) { + case UVERBS_ATTR_CORE_IN: + case UVERBS_ATTR_UHW_IN: { + if (attr.len > sizeof(uint64_t)) { + auto data_p = REMOTE_PTR_FIELD(attr_p, data); + syscall_state.mem_ptr_parameter(data_p, attr.len, IN_OUT); + } + break; + } + case UVERBS_ATTR_CORE_OUT: + case UVERBS_ATTR_UHW_OUT: { + auto data_p = REMOTE_PTR_FIELD(attr_p, data); + syscall_state.mem_ptr_parameter(data_p, attr.len, IN_OUT); + break; + } + case UVERBS_ATTR_WRITE_CMD: { + // Should be inside attr struct. + break; + } + default: + ASSERT(t, false) << "unknown attr_id for INVOKE_WRITE verb"; + } + continue; + } + + ssize_t data_len = attr.len; + uintptr_t data_addr = attr.data; + if (data_len > 8) { + // Heuristic - assume that any data longer than 8 bytes, is pointer pointing to a buffer and + // attr is PTR_OUT. + ASSERT(t, data_addr != 0) << "p is NULL, but data_len = " << data_len << "for arrts[" << i << "]"; + // remote_ptr data_p = data_addr; + auto data_p = REMOTE_PTR_FIELD(attr_p, data); + auto p = syscall_state.mem_ptr_parameter(data_p, data_len, IN_OUT); + ASSERT(t, !p.is_null()) << "p to arrts[" << i << "] is null"; + } else if (data_len == 8) { + // Heuristic - if data can fit a pointer check if pointer is valid; if not, it is probably ENUM_IN. + // Otherwise PTR_IN and/or PTR_OUT. + // ASSERT(t, data_addr != 0) << "p is NULL, but data_len = " << data_len << "for arrts[" << i << "]"; + // remote_ptr data_p = data_addr; + auto data_p = REMOTE_PTR_FIELD(attr_p, data); + if (t->vm()->has_mapping(data_addr)) { + auto p = syscall_state.mem_ptr_parameter(data_p, data_len, IN_OUT); + ASSERT(t, !p.is_null()) << "p to arrts[" << i << "] is null"; + } + } else { + // Already stored in VLA. + } + } +} + template static Switchable prepare_ioctl(RecordTask* t, TaskSyscallState& syscall_state) { @@ -1761,7 +1861,7 @@ static Switchable prepare_ioctl(RecordTask* t, */ case RDMA_VERBS_IOCTL: // TODO: ioctl argument size is dynamically calculated. How to do this in rr? - syscall_state.reg_parameter(3); + prepare_rdma_verbs_ioctl(t, syscall_state); return PREVENT_SWITCH; case SG_IO: