Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 101 additions & 1 deletion src/record_syscall.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <linux/videodev2.h>
#include <linux/wireless.h>
#include <poll.h>
#include <rdma/ib_user_ioctl_cmds.h>
#include <rdma/rdma_user_ioctl_cmds.h>
#include <sched.h>
#include <scsi/sg.h>
Expand Down Expand Up @@ -1587,6 +1588,105 @@ template <typename Arch> void prepare_ethtool_ioctl(RecordTask* t, TaskSyscallSt
syscall_state.after_syscall_action(record_page_below_stack_ptr);
}

template<typename T> struct TD;

template <typename Arch>
static void prepare_rdma_verbs_ioctl(__attribute__((unused)) RecordTask* t,
__attribute__((unused)) TaskSyscallState& syscall_state)
{
auto &regs = syscall_state.syscall_entry_registers;
remote_ptr<void> argp = regs.arg(3);
auto hdrp = argp.cast<typename Arch::ib_uverbs_ioctl_hdr>();
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<decltype(hdrp)> td;
auto attrs_field_p = hdrp.template cast<uint8_t>() + sizeof(typename Arch::ib_uverbs_ioctl_hdr);
auto attrs_p = attrs_field_p.template cast<typename Arch::ib_uverbs_attr>();
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<void> 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<void> 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 <typename Arch>
static Switchable prepare_ioctl(RecordTask* t,
TaskSyscallState& syscall_state) {
Expand Down Expand Up @@ -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<typename Arch::ib_uverbs_ioctl_hdr>(3);
prepare_rdma_verbs_ioctl<Arch>(t, syscall_state);
return PREVENT_SWITCH;

case SG_IO:
Expand Down