Page MenuHomeFreeBSD

Pass the syscall number to capsicum permission-denied signals
Needs ReviewPublic

Authored by theraven on Mar 10 2021, 12:58 PM.

Details

Reviewers
manu
Group Reviewers
capsicum
manpages
Summary

The syscall number is stored in the same register as the syscall return on amd64 (and possibly other architectures) and so it is impossible to recover in the signal handler after the call has returned. This small tweak delivers it in the si_value field of the signal, which is sufficient to catch capability violations and emulate them with a call to a more-privileged process in the signal handler.

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint OK
Unit
No Unit Test Coverage
Build Status
Buildable 37863
Build 34752: arc lint + arc unit

Event Timeline

theraven created this revision.

si_value is not the best place for this kind of information. Signal-specific data is supplied in the _reason union of the struct __siginfo. It is cleaner IMO to define a new union member and pass the syscall number through it, preserving the proper use of the interface.

@kib, thank you, that also mirrors what Linux does for seccomp violation signals. I will update the diff to reflect that.

Updated based on @kib's feedback.

I am still somewhat concerned that this doesn't let you differentiate between a syscall made directly and via a syscall / __syscall system call, which will have different argument frame layouts. This information seems to be erased on syscall entry. I'd be happy to make a larger change if you can suggest a good place to stash this information.

As a side comment @theraven if you have to upload a new version next time would you add context via e.g. git show -U99999

@emaste, sorry, I don't think I understand this comment.

@theraven that means uploading a full(er) diff, which is easier to check the other content in the modified files. This can also be done through using arcanist tool (devel/arcanist). More information is available at https://wiki.freebsd.org/Phabricator

I appear to be setting the field in the siginfo structure incorrectly in the new version, but it's now the end of the day for me so I will try to debug this tomorrow.

Updated based on @kib's feedback.

I am still somewhat concerned that this doesn't let you differentiate between a syscall made directly and via a syscall / __syscall system call, which will have different argument frame layouts. This information seems to be erased on syscall entry. I'd be happy to make a larger change if you can suggest a good place to stash this information.

struct syscall_args is the right place, of course. You would need to do a pass over all MD/ABI cpu_fetch_syscall_args() implementations.

When do you need this? Isn't this kind of introspection warrants a use of real debugger which can deduce all the data from debugging information?

When do you need this? Isn't this kind of introspection warrants a use of real debugger which can deduce all the data from debugging information?

We are using this to implement the FreeBSD version of something we have working with seccomp-bpf already: trapping when a disallowed syscall occurs and turning it into an RPC to a more privileged process. We don't have a debugger attached. The FreeBSD decision to use SIGTRAP is somewhat unfortunate because debuggers don't deliver this to the traced process if they see it and so we can't debug code using this on FreeBSD (Linux uses SIGSYS and so we can).

Add amd64 paths to preserve the original system call number.

If this is the right approach then I will make the same changes for other
architectures.

Sorry, this version does work, I had only updated one of the places in the code using it to check si_syscall and so the test was failing yesterday.

This looks good to me.

sys/sys/signal.h
258

Look how other comments are formatted. Add (at least one) tab after ';' and possibly wrap line if longer than 80 chars.

The FreeBSD decision to use SIGTRAP is somewhat unfortunate because debuggers don't deliver this to the traced process if they see it

SIGTRAP on capability errno was intended as a debugging aid, I did not anticipate it would be used in this manner. I'm not sure what signal might be more appropriate for this case though.

  • Add missing architecture support, address reviewer comments.

Are the syscall_args structures part of the stable KBI? This won't quite cleanly apply to older versions of FreeBSD because at least on x86 the system call entry code has been cleaned up to remove some code duplication, but I am running on 12-STABLE with a version of this that doesn't correctly handle the syscall / __syscall syscalls and it would be great to be able to MFC as much as possible, even if we can't get full support without the KBI change.

Are the syscall_args structures part of the stable KBI? This won't quite cleanly apply to older versions of FreeBSD because at least on x86 the system call entry code has been cleaned up to remove some code duplication, but I am running on 12-STABLE with a version of this that doesn't correctly handle the syscall / __syscall syscalls and it would be great to be able to MFC as much as possible, even if we can't get full support without the KBI change.

By themself, syscall_args are not used by anything but syscall entry code (and some ptrace bits). But the structure is embedded into struct thread, and layout of struct thread must be stable.

What do you mean by saying that some stable/12 does not handle the syscall syscall correctly? Against which version of FreeBSD is your patch generated?

sys/amd64/include/proc.h
95

Note that this addition of u_int does not change layout or size of syscall_args because there is a gap after code.

sys/i386/include/proc.h
66

But this addition changes layout and probably triggers compile-time asserts in kern_thread.c.

BTW you are inconsistent: amd64 added original_code after code, i386 is reversed.

In D29185#655877, @kib wrote:

By themself, syscall_args are not used by anything but syscall entry code (and some ptrace bits). But the structure is embedded into struct thread, and layout of struct thread must be stable.

I see. So it would be possible to MFC the changes to the layouts on 64-bit platforms if original_code is after code and therefore inside padding, but not the changes in 32-bit architectures? Should I split them into two separate patches?

What do you mean by saying that some stable/12 does not handle the syscall syscall correctly? Against which version of FreeBSD is your patch generated?

The patch is against main. I also have a version done against stable/12, but to support the syscall syscall you need the original_code bits, which change the structure, and so I wanted to understand what is possible to MFC.

sys/i386/include/proc.h
66

It's before in both, but from what you say about KBIs it should be after on 64-bit architectures to enable MFC and it doesn't matter (but for consistency should probably be after) on 32-bit ones.

For stable/12 and stable/13, you can add a new member to struct thread at the end. Then you need to add some manual code to copy this member on fork.

bcr added a subscriber: bcr.

OK from manpages.