# futex ______________________________________________________
# long sys_futex(u32 __user *uaddr,
#           int op,
#           int val,
#           struct timespec __user *utime,
#           u32 __user *uaddr2,
#           int val3)
# long compat_sys_futex(u32 __user *uaddr, int op, u32 val,
#		struct compat_timespec __user *utime, u32 __user *uaddr2,
#		u32 val3)
#

@define _SYSCALL_FUTEX_NAME
%(
	name = "futex"
%)

@define _SYSCALL_FUTEX_REGARGS
%(
	futex_uaddr = pointer_arg(1)
	op = int_arg(2)
	val = int_arg(3)
	utime_uaddr = pointer_arg(4)
	uaddr2_uaddr = pointer_arg(5)
	val3 = int_arg(6)
%)

@define _SYSCALL_FUTEX_REGARGS_STORE
%(
        if (@probewrite(futex_uaddr))
          set_pointer_arg(1, futex_uaddr)

	if (@probewrite(op))
	  set_int_arg(2, op)

	if (@probewrite(val))
	  set_int_arg(3, val)

	if (@probewrite(utime_uaddr))
	  set_pointer_arg(4, utime_uaddr)

	if (@probewrite(uaddr2_uaddr))
	  set_pointer_arg(5, uaddr2_uaddr)

	if (@probewrite(val3))
	  set_int_arg(6, val3)
%)

probe syscall.futex = dw_syscall.futex !, nd_syscall.futex ? {}
probe syscall.futex.return = dw_syscall.futex.return !,
                             nd_syscall.futex.return ? {}

probe syscall.compat_futex = dw_syscall.compat_futex !,
                             nd_syscall.compat_futex ? {}
probe syscall.compat_futex.return = dw_syscall.compat_futex.return !,
                                    nd_syscall.compat_futex.return ? {}

# dw_futex _____________________________________________________

probe dw_syscall.futex = kernel.function("sys_futex").call ?
{
	@_SYSCALL_FUTEX_NAME
	futex_uaddr = $uaddr
	op = __int32($op)
	val = __int32($val)
	utime_uaddr = $utime
	uaddr2_uaddr = $uaddr2
	val3 = __int32($val3)
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
}
probe dw_syscall.futex.return = kernel.function("sys_futex").return ?
{
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR($return)
}
probe dw_syscall.compat_futex = kernel.function("compat_sys_futex").call ?
{
	@_SYSCALL_FUTEX_NAME
	futex_uaddr = @__pointer($uaddr)
	op = __int32($op)
	val = __int32($val)
	utime_uaddr = @__pointer($utime)
	uaddr2_uaddr = $uaddr2
	val3 = __int32($val3)
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
}
probe dw_syscall.compat_futex.return =
	kernel.function("compat_sys_futex").return ?
{
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR($return)
}

# nd_futex _____________________________________________________

probe nd_syscall.futex = nd1_syscall.futex!, nd2_syscall.futex!, tp_syscall.futex
  { }

probe nd1_syscall.futex = kprobe.function("sys_futex") ?
{
	@_SYSCALL_FUTEX_NAME
	asmlinkage()
	@_SYSCALL_FUTEX_REGARGS
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
}

/* kernel 4.17+ */
probe nd2_syscall.futex = kprobe.function(@arch_syscall_prefix "sys_futex") ?
{
	__set_syscall_pt_regs(pointer_arg(1))
	@_SYSCALL_FUTEX_NAME
	@_SYSCALL_FUTEX_REGARGS
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
},
{
        @_SYSCALL_FUTEX_REGARGS_STORE
}

/* kernel 3.5+, but undesirable because it affects all syscalls */
probe tp_syscall.futex = kernel.trace("sys_enter")
{
	__set_syscall_pt_regs($regs)
	@__syscall_gate(@const("__NR_futex"))
	@_SYSCALL_FUTEX_NAME
	@_SYSCALL_FUTEX_REGARGS
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
},
{
        @_SYSCALL_FUTEX_REGARGS_STORE
}

probe nd_syscall.futex.return = nd1_syscall.futex.return!, nd2_syscall.futex.return!, tp_syscall.futex.return
  { }
  
probe nd1_syscall.futex.return = kprobe.function("sys_futex").return ?
{
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR(returnval())
}

/* kernel 4.17+ */
probe nd2_syscall.futex.return = kprobe.function(@arch_syscall_prefix "sys_futex").return ?
{
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR(returnval())
}
 
/* kernel 3.5+, but undesirable because it affects all syscalls */
probe tp_syscall.futex.return = kernel.trace("sys_exit")
{
	__set_syscall_pt_regs($regs)
	@__syscall_gate(@const("__NR_futex"))
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR($ret)
}

probe nd_syscall.compat_futex = nd1_syscall.compat_futex!, nd2_syscall.compat_futex!, tp_syscall.compat_futex
  { }

probe nd1_syscall.compat_futex = kprobe.function("compat_sys_futex") ?
{
	@_SYSCALL_FUTEX_NAME
	asmlinkage()
	@_SYSCALL_FUTEX_REGARGS
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
}

/* kernel 4.17+ */
probe nd2_syscall.compat_futex = kprobe.function(@arch_syscall_prefix "compat_sys_futex") ?
{
	__set_syscall_pt_regs(pointer_arg(1))
	@_SYSCALL_FUTEX_NAME
	@_SYSCALL_FUTEX_REGARGS
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
},
{
        @_SYSCALL_FUTEX_REGARGS_STORE
}

/* kernel 3.5+, but undesirable because it affects all syscalls */
probe tp_syscall.compat_futex = kernel.trace("sys_enter")
{
	__set_syscall_pt_regs($regs)
	@__compat_syscall_gate(@const("__NR_compat_futex"))
	@_SYSCALL_FUTEX_NAME
	@_SYSCALL_FUTEX_REGARGS
	@__futex_argstr(futex_uaddr, op, val, utime_uaddr, uaddr2_uaddr, val3)
},
{
        @_SYSCALL_FUTEX_REGARGS_STORE
}

probe nd_syscall.compat_futex.return = nd1_syscall.compat_futex.return!, nd2_syscall.compat_futex.return!, tp_syscall.compat_futex.return
  { }
  
probe nd1_syscall.compat_futex.return =
	kprobe.function("compat_sys_futex").return ?
{
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR(returnval())
}

/* kernel 4.17+ */
probe nd2_syscall.compat_futex.return =
	kprobe.function(@arch_syscall_prefix "compat_sys_futex").return ?
{
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR(returnval())
}
 
/* kernel 3.5+, but undesirable because it affects all syscalls */
probe tp_syscall.compat_futex.return = kernel.trace("sys_exit")
{
	__set_syscall_pt_regs($regs)
	@__compat_syscall_gate(@const("__NR_compat_futex"))
	@_SYSCALL_FUTEX_NAME
	@SYSC_RETVALSTR($ret)
}
