> What about PTRACE_SYSEMU_SINGLESTEP ? I will read the code
> tomorrow, but it is easy to miss some detail and we don't
> have any test-cases.

These exist purely for UML.  So the real test cases are to use UML.
To start with, make sure that check_sysemu() gets the same results
as on the vanilla kernel.

> Looks like, PTRACE_SYSEMU_SINGLESTEP is PTRACE_SINGLESTEP
> plus: if we enter syscall we should
> 
>       - return UTRACE_SYSCALL_ABORT
> 
>       - avoid SYSCALL_EXIT report like PTRACE_SYSEMU

Right.  I believe that the intent is PTRACE_SYSEMU_SINGLESTEP into a
syscall insn behaves exactly like PTRACE_SYSEMU.  If it's any other insn,
it behaves exactly like PTRACE_SINGLESTEP.

>       - avoid the unnecessary send_sigtrap() in syscall_trace_leave()
>         somehow, but the exact semantics is not clear to me...

If there was a syscall-entry report under SYSEMU, then you do not also
report that as a single-step stop.

> In particular. The tracee stops in SYSCALL_ENTRY, the tracer
> does PTRACE_SYSEMU_SINGLESTEP. We should just do enable_step()
> and clear UTRACE_EVENT(SYSCALL_EXIT), correct?

That is a possible implementation detail, sure.  The semantics are that
the syscall doesn't happen, and there is only one stop.

> Afaics, PTRACE_SYSEMU means:
> 
>       - cancel this syscall, and do _not_ report SYSCALL_ENTRY
>         to the tracer
> 
>       - SYSCALL_EXIT should be reported, but send_sigtrap()
>         should be avoied (in case it was SYSEMU_SINGLESTEP).
> 
> Correct?

There is no distinction in ptrace between the entry and exit reports.
So this is implementation detail, not semantics.  The semantics say that
the tracer sees only one stop per syscall insn, not two.  

The other thing that is possibly relevant to the semantics is the
interaction with syscall audit.  In the syscall entry path, we decide
that the syscall will be skipped, then stop for the ptrace report.
After resuming, we'll get to audit_syscall_entry and it will examine the
register state as changed by ptrace before the resumption.  So that will
claim something or other (chosen by the tracer) for audit, even though
the syscall doesn't really happen.  Then we'll get to the syscall exit
path, do audit_syscall_exit with the very same register state, and do
nothing else.

Those audit calls seem pretty useless since they don't even really tell
a lie chosen by the tracer--the audit log just sees the "not really
made" syscall entry state followed by the "aborted" (-ENOSYS) exit
state.  But it might be considered worthwhile upstream to keep this
exactly as it was.

> So. Looks like the only complication is that we should avoid
> the unnecessary send_sigtrap() in syscall_trace_leave(), but
> we shouldn't use TIF_SYSCALL_EMU - it must die.

Right.  There is another possible wrinkle, though this probably doesn't
matter.  If you used PTRACE_SYSEMU{,_SINGLESTEP} and got a syscall entry
stop, then you can resume it with PTRACE_SYSCALL or PTRACE_SINGLESTEP
instead.  In those cases, you will get a syscall-exit report or will get
a synthetic TRAP_BRKPT, looking at the "syscall returned -ENOSYS" state.

I'm almost positive that nobody ever does this and that nobody cares
about this behaving that way (i.e. UML doesn't care, and nobody else
uses this stuff).  But them's the manifest semantics of today.

So if we were to care about preserving that, it could impinge on whether
we implement by stopping in syscall-entry and then skipping syscall-exit
or vice versa.


Thanks,
Roland

Reply via email to