tracing: split out syscall_trace_enter construction
Subsystems (18)
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Linus TorvaldsQuoted 4 lines · Linus Torvalds
And if you filter system calls, it's entirely possible that you can attack suid executables through such a vector. Your "limit system calls for security" security suddenly turned into "avoid the system call that made things secure"!
That should not be possible with Will's event filter based solution (his last submitted patch), due to this code in fs/exec.c (which is in your upstream tree as well):
/*
* Flush performance counters when crossing a
* security domain:
*/
if (!get_dumpable(current->mm))
perf_event_exit_task(current);This will drop all filters if a setuid-root (or whatever setuid) binary is executed from a filtered environment.
Does this cover the case you were thinking of?
Thanks,
Ingo
James Morris
maintainerRe: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Linus TorvaldsQuoted 6 lines · Linus Torvalds
And per-system-call permissions are very dubious. What system calls don't you want to succeed? That ioctl? You just made it impossible to do a modern graphical application. Yet the kind of thing where we would _want_ to help users is in making it easier to sandbox something like the adobe flash player. But without accelerated direct rendering, that's not going to fly, is it?
Going back to the initial idea proposed by Will, where seccomp is simply extended to filter all syscalls, there is potential benefit in being able to limit the attack surface of the syscall API.
This is not security mediation in terms of interaction between things (e.g. "allow A to read B"). It's a _hardening_ feature which prevents a process from being able to invoke potentially hundreds of syscalls is has no need for. It would allow us to usefully restrict some well-established attack modes, e.g. triggering bugs in kernel code via unneeded syscalls.
This is orthogonal to access control schemes (such as SELinux), which are about mediating security-relevant interactions between objects.
One area of possible use is KVM/Qemu, where processes now contain entire operating systems, and the attack surface between them is now much broader e.g. a local unprivileged vulnerability is now effectively a 'remote' full system compromise.
There has been some discussion of this within the KVM project. Using the existing seccomp facility is problematic in that it requires significant reworking of Qemu to a privsep model, which would also then incur a likely unacceptable context switching overhead. The generalized seccomp filter as proposed by Will would provide a significant reduction in exposed syscalls and thus guest->host attack surface.
I've cc'd some KVM folk for more input on how this may or may not meet their requirements -- Avi/Gleb, there's a background writeup here:
http://lwn.net/Articles/442569/ . We may need a proof of concept and/or commitment to use this feature for it to be accepted upstream.
- James
Signature · 2 lines
James Morris <[email protected]>
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 34 lines · James Morris
Quoted 6 lines · Linus Torvalds
And per-system-call permissions are very dubious. What system calls don't you want to succeed? That ioctl? You just made it impossible to do a modern graphical application. Yet the kind of thing where we would _want_ to help users is in making it easier to sandbox something like the adobe flash player. But without accelerated direct rendering, that's not going to fly, is it?
Going back to the initial idea proposed by Will, where seccomp is simply extended to filter all syscalls, there is potential benefit in being able to limit the attack surface of the syscall API.
This is not security mediation in terms of interaction between things (e.g. "allow A to read B"). It's a _hardening_ feature which prevents a process from being able to invoke potentially hundreds of syscalls is has no need for. It would allow us to usefully restrict some well-established attack modes, e.g. triggering bugs in kernel code via unneeded syscalls.
This is orthogonal to access control schemes (such as SELinux), which are about mediating security-relevant interactions between objects.
One area of possible use is KVM/Qemu, where processes now contain entire operating systems, and the attack surface between them is now much broader e.g. a local unprivileged vulnerability is now effectively a 'remote' full system compromise.
There has been some discussion of this within the KVM project. Using the existing seccomp facility is problematic in that it requires significant reworking of Qemu to a privsep model, which would also then incur a likely unacceptable context switching overhead. The generalized seccomp filter as proposed by Will would provide a significant reduction in exposed syscalls and thus
guest->hostattack surface.
Quoted 4 lines
I've cc'd some KVM folk for more input on how this may or may not meet their requirements -- Avi/Gleb, there's a background writeup here:
http://lwn.net/Articles/442569/ . We may need a proof of concept and/or commitment to use this feature for it to be accepted upstream.
Indeed are were looking at sandboxing as a means to mitigate the "guest exploits qemu, proceeds to exploit host syscall interface" scenario, and evolved seccomp looks like the best tradeoff in terms of security gains vs effort needed.
Eric Paris (copied) prototyped this with his own version of enhanced seccomp and achieved pretty good results, so a proof of concept will be quite easy to provide.
Regarding dynamic filtering, the biggest question here is how this will interact with hotplug, which requires new files to be opened in the sandboxed process (or SCM_RIGHTed in). Any fd-based filtering will defeat that, so we'll need some way for a privileged monitor to adjust filters.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to James MorrisQuoted 9 lines · James Morris
Quoted 6 lines · Linus Torvalds
And per-system-call permissions are very dubious. What system calls don't you want to succeed? That ioctl? You just made it impossible to do a modern graphical application. Yet the kind of thing where we would _want_ to help users is in making it easier to sandbox something like the adobe flash player. But without accelerated direct rendering, that's not going to fly, is it?
Quoted 3 lines
Going back to the initial idea proposed by Will, where seccomp is simply extended to filter all syscalls, there is potential benefit in being able to limit the attack surface of the syscall API.
If controlling the system call boundary is found to be useful then the logical next logical step is to realize that limiting it to *only* the syscall boundary is shortsighted.
Also, here's a short reminder of the complexity evolution of this patch-set, which i've followed since it's been first posted in 2009:
bitmask (2009): 6 files changed, 194 insertions(+), 22 deletions(-)
filter engine (2010): 18 files changed, 1100 insertions(+), 21 deletions(-)
event filters (2011): 5 files changed, 82 insertions(+), 16 deletions(-)
Interestingly, the events version is *by far* the most flexible one in both the short and the long run, and it is also the smallest patch
...
It's a perfect fit and that's not really surprising: system call boundary hardening is about filtering various key parameters - while event tracing is about filtering various key parameters as well.
But it goes further than that: SELinux security policies are in essence primitive event filters as well, on an abstract level - see below for more details.
And yes, the primitive, coarse, per syscall allow/disallow bitmask v1 version would not be too painful to the core kernel in terms of code impact and interaction with other code (it does not interact at all)
- but it would still be sadly shortsighted to not explore the event filters angle, now that we have actual working code.
It would not improve the LSM situation one tiny bit either - the bitmask design would guarantee that the seccomp approach can never seriously replace the sucky LSM concepts we have in the kernel today.
Quoted 6 lines
This is not security mediation in terms of interaction between things (e.g. "allow A to read B"). It's a _hardening_ feature which prevents a process from being able to invoke potentially hundreds of syscalls is has no need for. It would allow us to usefully restrict some well-established attack modes, e.g. triggering bugs in kernel code via unneeded syscalls.
If you think about it then you'll see that this artificial distinction between 'mediation' and 'hardening' is nonsense!
If we add the appropriate file label field to VFS tracing events (which would be useful for many instrumentation reasons as well) then the event filtering variant of Will's patch:
_will be able to do object level security mediation too_
What is at the core of every access control concept, be that DAC, MAC, RBAC or ACL? Flexible task specific set of access vectors to file and other labeled objects, which cannot be circumvented by that task.
How can we implement a user-space file object manager via Will's event filters approach? It's actually pretty easy:
- a simple object manager wants to know 'who' does something, 'what' it is trying to access, and then wants to generate an allow/deny action as a function of the (who,what) parameters:
- The 'who' is a given as the event filters are per task, so different tasks with different roles can have different event filters. This is the equivalent of the current tasks's security context. [ Event filters installed by the parent cannot be removed by child tasks (they cannot even read them - it's transparent). ]
- The most finegrained representation of 'what' are inode numbers. Because we do not want to generate rules for every single object we want to group objects and want to define access rules on different groups. This can be done by defining an event that emits file labels.
So a simple object manager would simply use file label event attributes and would define simple rules like:
"(label & tmp_t) || (label & user_home_t)"
to allow access to /tmp and /home files. Filters allow us to define arbitrary access vectors to objects in essence. The above filters get passed to the kernel as an ASCII string by the object manager task, where the filter engine parses it safely and creates atomic predicates out of it, which can then be executed at the source of any event.
[ We could even implement a transparent AVC-cache equivalent for
filters, should the complexity and popularity of them increase:
ASCII filters lend themselves very well to hash based caches. ]
Similarly, support for other types of object tagging, network labels, etc. can be added as well with little pain: they can be added without any change to the basic ABI! Using events filters here makes it a very extensible security concept.
It is capable to implement the functional equivalent of most MAC, DAC, RBAC and other access control concepts, purely in user-space - in addition to 'hardening' (which btw. is really access control too, in disguise).
Obviously it is all layered: it is only allowed to control access to objects all the other security concepts allow for it to access - i.e. this is an unprivileged LSM, a per application security layer if you will, that can further refine security policies.
In terms of security models this event filters approach is unconditional goodness in my opinion.
Quoted 3 lines
This is orthogonal to access control schemes (such as SELinux), which are about mediating security-relevant interactions between objects.
It's only 'orthogonal' because IMO you make two fundamental mistakes:
1) You arbitrarily limit SELinux to object based security measures alone.
Which is not even true btw: SELinux certainly has some hooks it uses for pragmatic non-object hardening: for example all the places where we fall back to capabilities are places where there's a method based restriction not object based restriction.
The KDSKBENT ioctl check for example in security/selinux/hooks.c::selinux_file_ioctl(), or selinux_vm_enough_memory(), or the CAP_MAC_ADMIN exception in selinux_inode_getsecurity() all violate 'pure' DAC concepts but obviously for pragmatic reasons SELinux is doing these ...
mmap_min_addr is a borderline method restriction feature as well:
it does not really control access to the underlying object (RAM), but controls one (of many) access methods to it by controlling virtual memory ...
So SELinux, in a rather hypocritical fashion is already involved in hardening and in filtering, because obviously any practical and pragmatic security system *has to*.
2) You arbitrarily limit Will's patch to *not* be able to implement object based security mechanisms. Why?
Syscall hardening and object based access rules are *deeply* connected, conceptually they are subsets of one and the same thing: a good, organic security model controlling different hierarchies of physical and derived (virtual) resources, which allows flexible control of both objects *and* methods.
The 'methods' (the syscalls and other functionality) are *also* a derived resource so it's entirely legitimate to control access to them. Yes, because they are methods you can also try to use them to restrict access to underlying objects - this is what AppArmour is about mostly, and yes i agree that in the general case it's not a particularly robust method.
And yes, i fully submit that object access control has theoretical advantages and it should often be the primary measure that gives a robust, often provable backbone to a secure system.
But you'd be out of your mind to not recognize:
- The utility of controlling access methods (as resources) as well, both to reduce the attack surface in the implementation of those methods, and to allow the easy summary control of objects where there's only a low number of (and often only a single!) access method.
- The utility of unprivileged security frameworks.
- The utility of stackable security fetures. (defense in depth, anyone?)
Will's astonishingly small patch:
event filters (2011): 5 files changed, 82 insertions(+), 16 deletions(-)
Gives us *all three* of those, while also allowing user-space implemented MAC, DAC, RBAC as well.
Quoted 4 lines
One area of possible use is KVM/Qemu, where processes now contain entire operating systems, and the attack surface between them is now much broader e.g. a local unprivileged vulnerability is now effectively a 'remote' full system compromise.
Note that the main reason why Qemu needs access method hardening is because it has a dominantly state machine based design which does not lend itself very well to an object manager security design.
Note that tools/kvm/ would probably like to implement its own object manager model as well in addition to access method restrictions: by being virtual hardware it deals with many resources and object hierarchies that are simply not known to the host OS's LSM.
Unlike Qemu tools/kvm/ has a design that is very fit for MAC
concepts: it uses separate helper threads for separate resources (this could in many cases even be changed to be separate processes which only share access to the guest RAM image) - while Qemu is in most parts a state machine, so in tools/kvm/ we can realistically have a good object manager and keep an exploit in a networking interface driver from being able to access disk driver state.
(I've Cc:-ed Pekka for tools/kvm/.)
Quoted 7 lines
There has been some discussion of this within the KVM project. Using the existing seccomp facility is problematic in that it requires significant reworking of Qemu to a privsep model, which would also then incur a likely unacceptable context switching overhead. The generalized seccomp filter as proposed by Will would provide a significant reduction in exposed syscalls and thus
guest->hostattack surface.
... and the event filter based method would *also* allow MAC to be defined over physical resources, such as virtual network interfaces, virtual disk devices, etc.
You are seriously limiting the capabilities of this feature for no good reason i can recognize.
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Hi Ingo,
Quoted 7 lines · Ingo Molnar
Unlike Qemu
tools/kvm/has a design that is very fit for MAC
concepts: it uses separate helper threads for separate resources (this could in many cases even be changed to be separate processes which only share access to the guest RAM image) - while Qemu is in most parts a state machine, so intools/kvm/we can realistically have a good object manager and keep an exploit in a networking interface driver from being able to access disk driver state.
I haven't really followed this particular discussion nor do I know if Qemu is good or bad fit but sure, for tools/kvm Chrome-style sandboxing makes tons of sense and would be a pretty good fit for how our device model works now.
Pekka
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo MolnarQuoted 4 lines · Ingo Molnar
So a simple object manager would simply use file label event attributes and would define simple rules like:
"(label& tmp_t) || (label& user_home_t)"
Filtering by label vs. filtering by descriptor would solve qemu's hotplug issue neatly.
Quoted 5 lines
Note that
tools/kvm/would probably like to implement its own object manager model as well in addition to access method restrictions: by being virtual hardware it deals with many resources and object hierarchies that are simply not known to the host OS's LSM.
Quoted 7 lines
Unlike Qemu
tools/kvm/has a design that is very fit for MAC
concepts: it uses separate helper threads for separate resources (this could in many cases even be changed to be separate processes which only share access to the guest RAM image) - while Qemu is in most parts a state machine, so intools/kvm/we can realistically have a good object manager and keep an exploit in a networking interface driver from being able to access disk driver state.
You mean each thread will have a different security context? I don't see the point. All threads share all of memory so it would be trivial for one thread to exploit another and gain all of its privileges.
A multi process model works better but it has significant memory and performance overhead.
(well the memory overhead is much smaller when using transparent huge pages, but these only work for anonymous memory).
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Hi Avi,
Quoted 3 lines · Avi Kivity
You mean each thread will have a different security context? I don't see the point. All threads share all of memory so it would be trivial for one thread to exploit another and gain all of its privileges.
So how would that happen? I'm assuming that once the security context has been set up for a thread, you're not able to change it after that. You'd be able to exploit other threads through shared memory but how would you gain privileges?
Pekka
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 10 lines · Gleb Natapov
Quoted 9 lines · Pekka Enberg
Hi Avi,
Quoted 4 lines · Avi Kivity
You mean each thread will have a different security context? I don't see the point. All threads share all of memory so it would be trivial for one thread to exploit another and gain all of its privileges.
Quoted 7 lines
Quoted 4 lines
So how would that happen? I'm assuming that once the security context has been set up for a thread, you're not able to change it after that. You'd be able to exploit other threads through shared memory but how would you gain privileges?
By tricking other threads to execute code for you. Just replace return address on the other's thread stack.
That kind of exploit is not possible if the worker pool consists of processes - which would be rather easy to achieve with tools/kvm/.
In that model each process has its own stack, not accessible to other worker processes. They'd only share the guest RAM image and some (minimal) global state.
This way the individual devices are (optionally) isolated from each other. In a way this is a microkernel done right ;-)
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 23 lines · Ingo Molnar
Quoted 10 lines · Gleb Natapov
Quoted 9 lines · Pekka Enberg
Hi Avi,
Quoted 4 lines · Avi Kivity
You mean each thread will have a different security context? I don't see the point. All threads share all of memory so it would be trivial for one thread to exploit another and gain all of its privileges.
Quoted 7 lines
Quoted 4 lines
So how would that happen? I'm assuming that once the security context has been set up for a thread, you're not able to change it after that. You'd be able to exploit other threads through shared memory but how would you gain privileges?
By tricking other threads to execute code for you. Just replace return address on the other's thread stack.
That kind of exploit is not possible if the worker pool consists of processes - which would be rather easy to achieve with
tools/kvm/.
Quoted 6 lines
In that model each process has its own stack, not accessible to other worker processes. They'd only share the guest RAM image and some (minimal) global state.
This way the individual devices are (optionally) isolated from each other. In a way this is a microkernel done right ;-)
It's really hard to achieve, since devices have global interactions. For example a PCI device can change the memory layout when a BAR is programmed. So you would have a lot of message passing going on (not at runtime, so no huge impact on performance). The programming model is very different.
Note that message passing is in fact quite a good way to model hardware, since what different devices actually do is pass messages to each other. I expect if done this way, the device model would be better than what we have today. But it's not an easy step away from threads.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo MolnarQuoted 18 lines · Ingo Molnar
Quoted 15 lines · Gleb Natapov
Quoted 13 lines · Pekka Enberg
Hi Avi,
Quoted 4 lines · Avi Kivity
You mean each thread will have a different security context? I don't see the point. All threads share all of memory so it would be trivial for one thread to exploit another and gain all of its privileges.
So how would that happen? I'm assuming that once the security context has been set up for a thread, you're not able to change it after that. You'd be able to exploit other threads through shared memory but how would you gain privileges?
Quoted 6 lines
Quoted 2 lines
By tricking other threads to execute code for you. Just replace return address on the other's thread stack.
That kind of exploit is not possible if the worker pool consists of processes - which would be rather easy to achieve with
tools/kvm/.
Well, of course. There original question was about threads.
Quoted 7 lines
In that model each process has its own stack, not accessible to other worker processes. They'd only share the guest RAM image and some (minimal) global state.
This way the individual devices are (optionally) isolated from each other. In a way this is a microkernel done right ;-)
But doesn't this design suffer the same problem as microkernel? Namely a lot of slow IPCs?
--
Gleb.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 9 lines · Gleb Natapov
Quoted 6 lines
In that model each process has its own stack, not accessible to other worker processes. They'd only share the guest RAM image and some (minimal) global state.
This way the individual devices are (optionally) isolated from each other. In a way this is a microkernel done right ;-)
But doesn't this design suffer the same problem as microkernel? Namely a lot of slow IPCs?
Most of the IPCs we do already, to keep the devices separated from each other. So the most common type of IPC comes 'for free' in that model - and this is specific to virtualization so i'd not extend the claim to the host kernel.
virtio is an IPC mechanism to begin with.
It's certainly not entirely free though so if this is implemented in tools/kvm/ it should be configurable.
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Avi KivityQuoted 15 lines · Avi Kivity
Quoted 6 lines
Note that
tools/kvm/would probably like to implement its own object manager model as well in addition to access method
restrictions: by being virtual hardware it deals with many resources and object hierarchies that are simply not known to the host OS's LSM.Quoted 8 lines
Unlike Qemu
tools/kvm/has a design that is very fit for MAC
concepts: it uses separate helper threads for separate resources (this could in many cases even be changed to be separate processes which only share access to the guest RAM image) - while Qemu is in most parts a state machine, so intools/kvm/we can realistically have a good object manager and keep an exploit in a networking interface driver from being able to access disk driver state.
Quoted 4 lines
You mean each thread will have a different security context? I don't see the point. All threads share all of memory so it would be trivial for one thread to exploit another and gain all of its privileges.
You are missing the geniality of the tools/kvm/ thread pool! :-)
It could be switched to a worker *process* model rather easily. Guest RAM and (a limited amount of) global resources would be shared via mmap(SHARED), but otherwise each worker process would have its own stack, its own subsystem-specific state, etc.
Exploiting other device domains via the shared guest RAM image is not possible, we treat guest RAM as untrusted data already.
Devices, like real hardware devices, are functionally pretty independent from each other, so this security model is rather natural and makes a lot of sense.
Quoted 2 lines
A multi process model works better but it has significant memory and performance overhead.
Not in Linux :-) We context-switch between processes almost as quickly as we do between threads. With modern tagged TLB hardware it's even faster.
Quoted 2 lines
(well the memory overhead is much smaller when using transparent huge pages, but these only work for anonymous memory).
The biggest amount of RAM is the guest RAM image - but if that is mmap(SHARED) and mapped using hugepages then the pte overhead from a process model is largely mitigated.
Once we have a process model then isolation and MAC between devices becomes a very real possibility: exploit via one network interface cannot break into a disk interface.
Maybe even the isolation and per device access control of *same-class* devices from each other is possible: with careful implementation of the subsystem shared data structures. (which isnt much really)
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 6 lines · Ingo Molnar
You are missing the geniality of the
tools/kvm/thread pool! :-)It could be switched to a worker *process* model rather easily. Guest RAM and (a limited amount of) global resources would be shared via mmap(SHARED), but otherwise each worker process would have its own stack, its own subsystem-specific state, etc.
We get VM exit events in the vcpu threads which after minimal processing pass much of the work to the thread pool. Most of the virtio work (which could be a source of vulnerability - ringbuffers are hard) is done in the worker task context.
It would be possible to further increase isolation there by also passing the IO/MMIO decoding to the worker thread - but i'm not sure that's truly needed. Most of the risk is where most of the code is - and the code is in the worker task which interprets on-disk data, protocols, etc.
So we could not only isolate devices from each other, but we could also protect the highly capable vcpu fd from exploits in devices - worker threads generally do not need access to the vcpu fd IIRC.
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 14 lines · Ingo Molnar
Quoted 6 lines · Ingo Molnar
You are missing the geniality of the
tools/kvm/thread pool! :-)It could be switched to a worker *process* model rather easily. Guest RAM and (a limited amount of) global resources would be shared via mmap(SHARED), but otherwise each worker process would have its own stack, its own subsystem-specific state, etc.
We get VM exit events in the vcpu threads which after minimal processing pass much of the work to the thread pool. Most of the virtio work (which could be a source of vulnerability - ringbuffers are hard) is done in the worker task context.
Quoted 5 lines
It would be possible to further increase isolation there by also passing the IO/MMIO decoding to the worker thread - but i'm not sure that's truly needed. Most of the risk is where most of the code is - and the code is in the worker task which interprets on-disk data, protocols, etc.
I've suggested in the past to add an "mmiofd" facility to kvm, similar to ioeventfd. This is how it would work:
- userspace configures kvm with an mmio range and a pipe
- guest writes to that range write a packet to the pipe describing the write
- guest reads from that range write a packet to the pipe describing the read, then wait for a reply packet with the result
The advantages would be
- avoid heavyweight exit; kvm can simply wake up a thread on another core and resume processing
- writes can be pipelined, similar to how PCI writes are posted
- supports process separation
So far no one has posted an implementation but it should be pretty simple.
Quoted 3 lines
So we could not only isolate devices from each other, but we could also protect the highly capable vcpu fd from exploits in devices - worker threads generally do not need access to the vcpu fd IIRC.
Yes.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 14 lines · Avi Kivity
Quoted 5 lines
It would be possible to further increase isolation there by also passing the IO/MMIO decoding to the worker thread - but i'm not sure that's truly needed. Most of the risk is where most of the code is - and the code is in the worker task which interprets on-disk data, protocols, etc.
I've suggested in the past to add an "mmiofd" facility to kvm, similar to ioeventfd. This is how it would work:
- userspace configures kvm with an mmio range and a pipe
- guest writes to that range write a packet to the pipe describing the write
- guest reads from that range write a packet to the pipe describing the read, then wait for a reply packet with the result
Quoted 5 lines
The advantages would be
- avoid heavyweight exit; kvm can simply wake up a thread on another core and resume processing
- writes can be pipelined, similar to how PCI writes are posted
- supports process separation
Yes, that was my exact thought, a per transport channel fd.
Quoted 2 lines
So far no one has posted an implementation but it should be pretty simple.
tools/kvm/ could make quick use of it - and it's a performance optimization mainly IMO, not primarily a security feature.
If you whip up a quick untested prototype for the KVM side we could look into adding tooling support for it and could test it.
As long as it's provided as an opt-in ioctl() which if fails (on older kernels) we fall back to the vcpu-fd, it should be relatively straightforward to support on the tooling side as well AFAICS.
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo MolnarQuoted 17 lines · Ingo Molnar
Quoted 15 lines · Avi Kivity
Quoted 14 lines
Note that
tools/kvm/would probably like to implement its own object manager model as well in addition to access method
restrictions: by being virtual hardware it deals with many resources and object hierarchies that are simply not known to the host OS's LSM.Unlike Qemu
tools/kvm/has a design that is very fit for MAC
concepts: it uses separate helper threads for separate resources (this could in many cases even be changed to be separate processes which only share access to the guest RAM image) - while Qemu is in most parts a state machine, so intools/kvm/we can realistically have a good object manager and keep an exploit in a networking interface driver from being able to access disk driver state.
Quoted 6 lines
Quoted 4 lines
You mean each thread will have a different security context? I don't see the point. All threads share all of memory so it would be trivial for one thread to exploit another and gain all of its privileges.
You are missing the geniality of the
tools/kvm/thread pool! :-)
I'm sure the thread pool is very general, but the hardware we're modelling is not.
Quoted 4 lines
It could be switched to a worker *process* model rather easily. Guest RAM and (a limited amount of) global resources would be shared via mmap(SHARED), but otherwise each worker process would have its own stack, its own subsystem-specific state, etc.
Suppose a guest reconfigures a device's MSI page, and suppose that's handled by the device's process. Now it's not sufficient to update some global state, you have to go and tell the host kernel about it. With good privilege separation the device process would not be permitted to do that; now it has to pass a message to a process that is.
Same thing applies for BARs, reset signals, live migration, etc.
Quoted 2 lines
Exploiting other device domains via the shared guest RAM image is not possible, we treat guest RAM as untrusted data already.
Right.
Quoted 3 lines
Devices, like real hardware devices, are functionally pretty independent from each other, so this security model is rather natural and makes a lot of sense.
When just pushing packets, you are right. However setup/configuration is hardly clean.
Consider a CD-ROM eject, for example. Now it can't be done by a simple callback.
Quoted 6 lines
Quoted 2 lines
A multi process model works better but it has significant memory and performance overhead.
Not in Linux :-) We context-switch between processes almost as quickly as we do between threads. With modern tagged TLB hardware it's even faster.
Once we get PCID in, yes. There's still the message passing overhead, and unnecessary context switches. In a threaded model you can choose whether to switch threads or not, in a process model you cannot.
Quoted 6 lines
Quoted 2 lines
(well the memory overhead is much smaller when using transparent huge pages, but these only work for anonymous memory).
The biggest amount of RAM is the guest RAM image - but if that is mmap(SHARED) and mapped using hugepages then the pte overhead from a process model is largely mitigated.
That doesn't work with memory hotplug.
Quoted 3 lines
Once we have a process model then isolation and MAC between devices becomes a very real possibility: exploit via one network interface cannot break into a disk interface.
Yes, certainly.
Quoted 4 lines
Maybe even the isolation and per device access control of *same-class* devices from each other is possible: with careful implementation of the subsystem shared data structures. (which isnt much really)
Right, hardly at all in fact. The problem comes from the side-band issues like reset, interrupts, hotplug, and whatnot.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 5 lines · Avi Kivity
Quoted 3 lines
The biggest amount of RAM is the guest RAM image - but if that is mmap(SHARED) and mapped using hugepages then the pte overhead from a process model is largely mitigated.
That doesn't work with memory hotplug.
Why not, if we do the sensible thing and restrict the size granularity and alignment of plugged/unplugged memory regions to 2MB?
We can fix guest Linux as well to not be stupid about the sizing of memory hotplug requests. It does hotplug based on the memory map we pass to it anyway.
Am i missing something obvious here?
Quoted 7 lines
Quoted 4 lines
Maybe even the isolation and per device access control of *same-class* devices from each other is possible: with careful implementation of the subsystem shared data structures. (which isnt much really)
Right, hardly at all in fact. The problem comes from the side-band issues like reset, interrupts, hotplug, and whatnot.
Yeah. There are two good aspects here i think:
- The sideband IPC overhead does not matter much, it's a side band.
- Spending the effort to isolate configuration details is worth it:
sideband code is a primary breeding ground for bugs and security holes.
The main worry to me would be the maintainability difference: does it result in much more complex code? As always i'm cautiously optimistic about that: i think once we try it we can find a suitable model ... It might even turn out to be more readable and more flexible in the end.
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 10 lines · Ingo Molnar
Quoted 5 lines · Avi Kivity
Quoted 3 lines
The biggest amount of RAM is the guest RAM image - but if that is mmap(SHARED) and mapped using hugepages then the pte overhead from a process model is largely mitigated.
That doesn't work with memory hotplug.
Why not, if we do the sensible thing and restrict the size granularity and alignment of plugged/unplugged memory regions to 2MB?
Once forked, you cannot have new shared anonymous memory, can you?
Quoted 5 lines
We can fix guest Linux as well to not be stupid about the sizing of memory hotplug requests. It does hotplug based on the memory map we pass to it anyway.
Am i missing something obvious here?
Yes, the new mmap() will be only visible in the calling process.
Quoted 16 lines
Quoted 7 lines
Quoted 4 lines
Maybe even the isolation and per device access control of *same-class* devices from each other is possible: with careful implementation of the subsystem shared data structures. (which isnt much really)
Right, hardly at all in fact. The problem comes from the side-band issues like reset, interrupts, hotplug, and whatnot.
Yeah. There are two good aspects here i think:
- The sideband IPC overhead does not matter much, it's a side band.
- Spending the effort to isolate configuration details is worth it:
sideband code is a primary breeding ground for bugs and security holes.
Quoted 5 lines
The main worry to me would be the maintainability difference: does it result in much more complex code? As always i'm cautiously optimistic about that: i think once we try it we can find a suitable model ... It might even turn out to be more readable and more flexible in the end.
I also believe it will be more maintainable, especially if written in a language that has explicit support for message passing (e.g. Erlang). This is because it is more similar to how hardware actually works. However it needs to be designed in, it's not just a matter of switching a thread to a process.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 7 lines · Avi Kivity
Quoted 6 lines · Ingo Molnar
Quoted 4 lines · Avi Kivity
Quoted 3 lines
The biggest amount of RAM is the guest RAM image - but if that is mmap(SHARED) and mapped using hugepages then the pte overhead from a process model is largely mitigated.
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
That doesn't work with memory hotplug.
Why not, if we do the sensible thing and restrict the size granularity and alignment of plugged/unplugged memory regions to 2MB?
Once forked, you cannot have new shared anonymous memory, can you?
We can have named shared memory.
Incidentally i suggested this to Pekka just yesterday: i think we should consider guest RAM images to be named files on the local filesystem (prefixed with the disk image's name or so, for easy identification), this will help with debugging and with swapping as well. (This way guest RAM wont eat up regular anonymous swap space - it will be swapped to the filesystem.)
As a sidenote, live migration might also become possible this way: in theory we could freeze a guest to its RAM image - which can then be copied (together with the disk image) to another box as files and restarted there, with some some hw configuration state dumped to a header portion of that RAM image as well. (outside of the RAM area)
Thanks,
Ingo
Peter Zijlstra
maintainerRe: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 4 lines · Ingo Molnar
Incidentally i suggested this to Pekka just yesterday: i think we should consider guest RAM images to be named files on the local filesystem (prefixed with the disk image's name or so, for easy identification),
That'll break THP and KSM, both rely and work on anon only.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 8 lines · Peter Zijlstra
Quoted 4 lines · Ingo Molnar
Incidentally i suggested this to Pekka just yesterday: i think we should consider guest RAM images to be named files on the local filesystem (prefixed with the disk image's name or so, for easy identification),
That'll break THP and KSM, both rely and work on anon only.
No reason they should be limited to anon only though.
Also, don't we have some sort of anonfs, from which we could get an fd, which, if mmap()-ed produces regular anonymous shared memory?
That fd could be passed over to other processes, who could then mmap() the new piece of shared-anon memory as well.
Thanks,
Ingo
James Morris
maintainerRe: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Btw, if anyone's going to be at Plumbers this year, we have a day set aside for the security summit:
https://security.wiki.kernel.org/index.php/LinuxSecuritySummit2011
This may make a good discussion topic.
- James
Signature · 2 lines
James Morris <[email protected]>
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo MolnarQuoted 11 lines · Ingo Molnar
Quoted 9 lines · Avi Kivity
Quoted 8 lines · Ingo Molnar
Quoted 5 lines · Avi Kivity
Quoted 3 lines
The biggest amount of RAM is the guest RAM image - but if that is mmap(SHARED) and mapped using hugepages then the pte overhead from a process model is largely mitigated.
That doesn't work with memory hotplug.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
Why not, if we do the sensible thing and restrict the size granularity and alignment of plugged/unplugged memory regions to 2MB?
Once forked, you cannot have new shared anonymous memory, can you?
We can have named shared memory.
But then the benefit of transparent huge pages goes away.
Of course, if some is working on extending transparent hugepages, the problem is solved. I know there is interest in this.
Quoted 6 lines
Incidentally i suggested this to Pekka just yesterday: i think we should consider guest RAM images to be named files on the local filesystem (prefixed with the disk image's name or so, for easy identification), this will help with debugging and with swapping as well. (This way guest RAM wont eat up regular anonymous swap space - it will be swapped to the filesystem.)
Qemu supports this via -mem-path. The motivation was supporting hugetlbfs, before THP. I can't say it was useful for debugging (but then qemu has a built in memory inspector and debugger, and supports attaching gdb to the guest).
Quoted 5 lines
As a sidenote, live migration might also become possible this way: in theory we could freeze a guest to its RAM image - which can then be copied (together with the disk image) to another box as files and restarted there, with some some hw configuration state dumped to a header portion of that RAM image as well. (outside of the RAM area)
Live migration involves the guest running in parallel with its memory being copied over. Even a 1GB guest will take 10s over 1GbE; any reasonably sized guest will take forever.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 3 lines · Avi Kivity
Live migration involves the guest running in parallel with its memory being copied over. Even a 1GB guest will take 10s over 1GbE; any reasonably sized guest will take forever.
I suspect we are really offtopic here, but an initial rsync, then stopping the guest, final rsync + restart the guest at the target would work with minimal interruption.
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 3 lines · Ingo Molnar
I suspect we are really offtopic here, but an initial rsync, then stopping the guest, final rsync + restart the guest at the target would work with minimal interruption.
Actually, after you kick off the migrate, you really want to be tracking in real time what pages get dirtied while you're doing the initial copy, so that the second rsync doesn't have to walk through the file finding the differences.
But that requires some extra instrumentation.
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 11 lines · * [email protected] <[email protected]> wrote:
Quoted 3 lines · Ingo Molnar
I suspect we are really offtopic here, but an initial rsync, then stopping the guest, final rsync + restart the guest at the target would work with minimal interruption.
Actually, after you kick off the migrate, you really want to be tracking in real time what pages get dirtied while you're doing the initial copy, so that the second rsync doesn't have to walk through the file finding the differences. But that requires some extra instrumentation.
Yeah, and that's how socket based live migration works - it's completely seemless.
But note that the rsync re-scan should not be an issue: both the source and the target system will obviously have a *lot* more RAM than the guest RAM image size.
Thanks,
Ingo
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Linus TorvaldsQuoted 25 lines · Linus Torvalds
Quoted 5 lines · Kees Cook
Can we just go back to the original spec? A lot of people were excited about the
prctl()API as done in Will's earlier patchset, we don't lose the extremely useful "enable_on_exec" feature, and we can get away from all this disagreement... and quite frankly, I'm not even convinced about the original simpler spec.
Security is a morass. People come up with cool ideas every day, and nobody actually uses them - or if they use them, they are just a maintenance nightmare.
Quite frankly, limiting pathname access by some prefix is "cool", but it's basically useless.
That's not where security problems are.
Security problems are in the odd corners - ioctl's, /proc files, random small interfaces that aren't just about file access.
And who would *use* this thing in real life? Nobody. In order to sell me on a new security interface, give me a real actual use case that is security-conscious and relevant to real users.
Quoted 9 lines
For things like web servers that actually want to limit filename lookup, we'd be <i>much</i> better off with a few new flags to pathname lookup that say "don't follow symlinks" and "don't follow '..'". Things like that can actually be beneficial to security-conscious programming, with very little overhead. Some of those things currently look up pathnames one component at a time, because they can't afford to not do so. That's a *much* better model for the whole "only limit to this subtree" case that was quoted sometime early in this thread.
The "make sure we don't follow symlinks at all" is a real problem in VirtFS (http://wiki.qemu.org/Documentation/9psetup) that we are fixing by adding a forked chrooted process to Qemu. If we are open to a new open flag O_NOFOLLOW_PATH, which would fail with ELOOP if any of the path component is a symbolic link, that would greatly simplify VirtFS. Will such a new flag to open be acceptable ?
-aneesh
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 7 lines · Aneesh Kumar K.V
The "make sure we don't follow symlinks at all" is a real problem in VirtFS (http://wiki.qemu.org/Documentation/9psetup) that we are fixing by adding a forked chrooted process to Qemu. If we are open to a new open flag
O_NOFOLLOW_PATH, which would fail with ELOOP if any of the path component is a symbolic link, that would greatly simplify VirtFS. Will such a new flag to open be acceptable ?
Such a flag should be something like 3 lines of actual code (and then the header file changes to actually add the mask itself, which is apt to be th ebulk of the patch just because we have to have different values for different architectures).
And yes, it is absolutely acceptable. The only questions in my mind are
- why haven't we done this long ago?
- do we have the flag space?
- should we do a O_NOMNT_PATH flag to do the same for mount-points?
Some people worry about being confused by bind mounts etc.
- do we think ".." is worthy of a flag too?
or is that a "user space can damn well check that itself, even if
it would be absolutely trivial to check in the kernel too"?
Whatever. I think the NOFOLLOW_PATH one is pretty much a no-brainer. It's not like symlink worries are unusual.
Linus
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 10 lines · Linus Torvalds
And yes, it is absolutely acceptable. The only questions in my mind are
- why haven't we done this long ago?
- do we have the flag space?
- should we do a
O_NOMNT_PATHflag to do the same for mount-points?Some people worry about being confused by bind mounts etc.
Quoted 7 lines
- do we think ".." is worthy of a flag too?
or is that a "user space can damn well check that itself, even if
it would be absolutely trivial to check in the kernel too"?Whatever. I think the
NOFOLLOW_PATHone is pretty much a no-brainer. It's not like symlink worries are unusual.
It's not *quite* a no-brainer. Guys, please hold that one off for a while; we have more massage to do in the area and I *really* want to get atomic open work finished (== intents gone, revalidation vs mountpoints sanitized, etc.) before anything else is done to fs/namie.c. OK?
And as for .. - userland can bloody well check that on its own if it cares. Let's keep it simple, please - we already have things far too complicated in there for my taste.
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Thomas GleixnerQuoted 8 lines · Thomas Gleixner
Quoted 5 lines
Quoted 3 lines
We do _NOT_ make any decision based on the trace point so what's the "pre-existing" active role in the syscall entry code?
The seccomp code we are discussing in this thread.
That's proposed code and has absolutely nothing to do with the existing trace point semantics.
So because it's proposed code it does not exist?
If the feature is accepted (and given Linus's opinion it's not clear at all it's accepted in any form) then it's obviously a very legitimate technical concern whether we do:
ret = seccomp_check_syscall_event(p1, p2, p3, p4, p5); if (ret) return -EACCES; ... random code ... trace_syscall_event(p1, p2, p3, p4, p5);
Where seccomp_check_syscall_event() duplicates much of the machinery that is behind trace_syscall_event().
Or we do the more intelligent:
ret = check_syscall_event(p1, p2, p3, p4, p5); if (ret) return -EACCES;
Where we have the happy side effects of:
- less code at the call site
- (a lot of!) shared infrastructure between the proposed seccomp code and event filters.
- we'd also be able to trace at security check boundaries - which has obvious bug analysis advantages.
In fact i do not see *any* advantages in keeping this needlessly bloaty and needlessly inconsistently sampled form of instrumentation:
ret = seccomp_check_syscall_event(p1, p2, p3, p4, p5); if (ret) return -EACCES; ... random code ... trace_syscall_event(p1, p2, p3, p4, p5);
Do you?
Thanks,
Ingo
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Thomas GleixnerQuoted 7 lines · Thomas Gleixner
Quoted 2 lines
If anything then that should tell you something that events and seccomp are not just casually related ...
They happen to have the hook at the same point in the source and for pure coincidence it works because the problem to solve is extremly simplistic. And that's why the diffstat is minimalistic, but that does not prove anything.
Here are the diffstats of the various versions of this proposed security feature:
bitmask (2009): 6 files changed, 194 insertions(+), 22 deletions(-)
filter engine (2010): 18 files changed, 1100 insertions(+), 21 deletions(-)
event filters (2011): 5 files changed, 82 insertions(+), 16 deletions(-)
The third variant, 'event filters', is actually the most sophisticated one of all and it is not simplistic at all.
The main reason why the diffstat is small is because it reuses over ten thousand lines of pre-existing kernel code intelligently. Are you interpreting that as some sort of failure of the patch? I think it's a very good thing.
To demonstrate the non-simplicity of the feature:
- These security rules/filters can be sophisticated like:
sys_close() rule protecting against the closing of stdin/stdout/stderr:
"fd == 0 || fd == 1 || fd == 2"
sys_ioperm() rule allowing port 0x80 access but nothing else:
"from != 128 || num != 1"
sys_listen() rule limiting the max accept() backlog to 16 entries:
"backlog > 16"
sys_mprotect(), sys_mmap[2](), sys_unmap() and sys_mremap() rule protecting the first 1 MB NULL pointer guard range:
"addr < 0x00100000"
sys_setscheduler() rule protecting against the switch to non-SCHED_OTHER scheduler policies:
"policy != 0"
Most of these examples are finegrained access restrictions that AFAIK are not possible with any of the LSM based security measures that Linux offers today.
- These security rules/filters can be safely used and installed by unprivileged userspace, allowing arbitrary end user apps to define their own, flexible security policies.
- These security rules/filters get automatically inherited into child tasks and child tasks cannot mess with them - they cannot even query/observe that these filters *exist*.
- These security rules/filters nest on each other in basically arbitrary depth, giving us a working, implemented, stackable LSM concept.
- These security rules/filters can be extended to arbitrary more object lifetime events in the future, without changing the ABI.
- These security rules/filters, unlike most LSM rules, can execute not just within hardirqs but also within deeply atomic contexts such as NMI contexts, putting far less restrictions on what can be security/access checked.
- Access permission violations can be set up to generate events of the violations into a scalable ring-buffer, providing unprivileged security-auditing functionality to the managing task(s).
I'd call that anything but 'simplistic'.
Thanks,
Ingo
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Will DrewryQuoted 5 lines · Will Drewry
The change avoids defining a new trace call type or a huge number of internal changes and hides seccomp.mode=2 from ABI-exposure in prctl, but the attack surface is non-trivial to verify, and I'm not sure if this ABI change makes sense. It amounts to:
Quoted 8 lines
include/linux/ftrace_event.h | 4 +- include/linux/perf_event.h | 10 +++++--- kernel/perf_event.c | 49 +++++++++++++++++++++++++++++++++++++--- kernel/seccomp.c | 8 ++++++ kernel/trace/trace_syscalls.c | 27 +++++++++++++++++----- 5 files changed, 82 insertions(+), 16 deletions(-)And can be found here: http://static.dataspill.org/perf_secure/v1/
Wow, i'm very impressed how few changes you needed to do to support this!
So, firstly, i don't think we should change perf_tp_event() at all - the 'observer' codepaths should be unaffected.
But there could be a perf_tp_event_ret() or perf_tp_event_check() entry that code like seccomp which wants to use event results can use.
Also, i'm not sure about the seccomp details and assumptions that were moved into the perf core. How about passing in a helper function to perf_tp_event_check(), where seccomp would define its seccomp specific helper function?
That looks sufficiently flexible. That helper function could be an 'extra filter' kind of thing, right?
Also, regarding the ABI and the attr.err_on_discard and attr.require_secure bits, they look a bit too specific as well.
attr.err_on_discard: with the filter helper function passed in this is probably not needed anymore, right?
attr.require_secure: this is basically used to *force* the creation of security-controlling filters, right? It seems to me that this could be done via a seccomp ABI extension as well, without adding this to the perf ABI. That seccomp call could check whether the right events are created and move the task to mode 2 only if that prereq is met - or something like that.
Quoted 2 lines
If there is any interest at all, I can post it properly to this giant CC list. [...]
I'd suggest to trim the Cc: list aggressively - anyone interested in the discussion can pick it up on lkml - and i strongly suspect that most of the Cc:
participants would want to be off the Cc: :-)
Thanks,
Ingo
Steven Rostedt
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 4 lines · Ingo Molnar
* Will Drewry <[email protected]> wrote:
But there could be a
perf_tp_event_ret()orperf_tp_event_check()entry that code like seccomp which wants to use event results can use.
We should name it something else. The "perf_tp.." is a misnomer as it has nothing to do with performance monitoring. "dynamic_event_.." maybe, as it is dynamic to the affect that we can use jump labels to enable or disable it.
-- Steve
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo Molnar[CC trimmed, as recommended]
Hi,
Quoted 16 lines · Ingo Molnar
Quoted 5 lines · Will Drewry
The change avoids defining a new trace call type or a huge number of internal changes and hides seccomp.mode=2 from ABI-exposure in prctl, but the attack surface is non-trivial to verify, and I'm not sure if this ABI change makes sense. It amounts to:
Quoted 8 lines
include/linux/ftrace_event.h | 4 +- include/linux/perf_event.h | 10 +++++--- kernel/perf_event.c | 49 +++++++++++++++++++++++++++++++++++++--- kernel/seccomp.c | 8 ++++++ kernel/trace/trace_syscalls.c | 27 +++++++++++++++++----- 5 files changed, 82 insertions(+), 16 deletions(-)And can be found here: http://static.dataspill.org/perf_secure/v1/
Quoted 7 lines
Wow, i'm very impressed how few changes you needed to do to support this! [...]
attr.require_secure: this is basically used to *force* the creation of security-controlling filters, right? It seems to me that this could be done via a seccomp ABI extension as well, without adding this to the perf ABI. That seccomp call could check whether the right events are created and move the task to mode 2 only if that prereq is met - or something like that.
I understood the prctl() API that was outlined earlier, but it seems this is not going to happen now. What would the programming API actually look like for an application developer using this perf-style method?
-Kees
Signature · 2 lines
Kees Cook Ubuntu Security Team
Re: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 29 lines · Kees Cook
[CC trimmed, as recommended]
Hi,
Quoted 16 lines · Ingo Molnar
Quoted 5 lines · Will Drewry
The change avoids defining a new trace call type or a huge number of internal changes and hides seccomp.mode=2 from ABI-exposure in prctl, but the attack surface is non-trivial to verify, and I'm not sure if this ABI change makes sense. It amounts to:
Quoted 8 lines
include/linux/ftrace_event.h | 4 +- include/linux/perf_event.h | 10 +++++--- kernel/perf_event.c | 49 +++++++++++++++++++++++++++++++++++++--- kernel/seccomp.c | 8 ++++++ kernel/trace/trace_syscalls.c | 27 +++++++++++++++++----- 5 files changed, 82 insertions(+), 16 deletions(-)And can be found here: http://static.dataspill.org/perf_secure/v1/
Quoted 7 lines
Wow, i'm very impressed how few changes you needed to do to support this! [...]
attr.require_secure: this is basically used to *force* the creation of security-controlling filters, right? It seems to me that this could be done via a seccomp ABI extension as well, without adding this to the perf ABI. That seccomp call could check whether the right events are created and move the task to mode 2 only if that prereq is met - or something like that.
Quoted 4 lines
I understood the
prctl()API that was outlined earlier, but it seems this is not going to happen now. What would the programming API actually look like for an application developer using this perf-style method?
Well, this API is probably not going to happen either ;-)
The way is to create a perf event and install a filter by passing the filters ASCII string as a pointer to the kernel, using PERF_EVENT_IOC_SET_FILTER on the event fd. If this ever gets used seriously then it should probably move into its own system call - but that is a detail.
Installing a filter can be safely done by unprivileged user-space (the kernel checks it), and they get inherited across fork(), are properly per task, etc.
Thanks,
Ingo
Will Drewry
reviewerRe: [PATCH 3/5] v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo Molnar[trimmed the cc list to those who've expressed interest or strong opinions]
Quoted 15 lines · Ingo Molnar
Quoted 12 lines · Will Drewry
The change avoids defining a new trace call type or a huge number of internal changes and hides seccomp.mode=2 from ABI-exposure in prctl, but the attack surface is non-trivial to verify, and I'm not sure if this ABI change makes sense. It amounts to:
include/linux/ftrace_event.h | 4 +-
include/linux/perf_event.h| 10 +++++---kernel/perf_event.c| 49 +++++++++++++++++++++++++++++++++++++---kernel/seccomp.c| 8 ++++++kernel/trace/trace_syscalls.c| 27 +++++++++++++++++-----5 files changed, 82 insertions(+), 16 deletions(-)
Quoted 3 lines
Quoted 1 line
And can be found here: http://static.dataspill.org/perf_secure/v1/
Wow, i'm very impressed how few changes you needed to do to support this!
There's definitely a large overlap in the needed infrastructure (event inheritance, event enabling, event registration) which makes changes using it pretty small!
Quoted 2 lines
So, firstly, i don't think we should change
perf_tp_event()at all - the 'observer' codepaths should be unaffected.
Easy enough to do.
Quoted 7 lines
But there could be a
perf_tp_event_ret()orperf_tp_event_check()entry that code like seccomp which wants to use event results can use.Also, i'm not sure about the seccomp details and assumptions that were moved into the perf core. How about passing in a helper function to
perf_tp_event_check(), where seccomp would define its seccomp specific helper function?
I'm curious how we'd register the seccomp callback and/or know when to call it. Would it just be a task-wide callback for task_context events? If so, will that mean only one callback_event user will be able to function at a time (per task)? [That's fine for seccomp purposes, of course, but may incur further internal api pain later unless it uses the list iterator function model that ftrace uses.]
Quoted 6 lines
That looks sufficiently flexible. That helper function could be an 'extra filter' kind of thing, right?
Also, regarding the ABI and the attr.err_on_discard and attr.require_secure bits, they look a bit too specific as well.
Quoted 8 lines
attr.err_on_discard: with the filter helper function passed in this is probably not needed anymore, right?
attr.require_secure: this is basically used to *force* the creation of security-controlling filters, right? It seems to me that this could be done via a seccomp ABI extension as well, without adding this to the perf ABI. That seccomp call could check whether the right events are created and move the task to mode 2 only if that prereq is met - or something like that.
Yeah - this can all be achieved with 0 changes to the perf path entirely. The part that causes me concern is the idea of reusing perf events for a task that may or may not have been installed by the caller of prctl(PR_SET_SECCOMP, 2) without any clear demarcation to the API/ABI consumer. It seems that to use this interface an application will need to:
1. via libperf, drop all task-centric traces
2. via libperf, install some perf_events for the "allowed" events
3. call prctl(PR_SET_SECCOMP, 2).
Even then, it isn't necessarily clear to the user which events will have a security impact and which won't. It creates an implicit ABI (if that makes sense) that will be hard to ensure strong security guarantees for as perf changes. It'd be possible to still do a (PR_SET_SECCOMP_FILTER, eventid) approach to indicate which events are expected, but at that point I'd be tempted to leave libperf out of it again :) Regardless, I'll pull together the patch as I understand the proposal since code can be a bit easier to digest than the discussion.
It seems to me that it's going to be hard to stay away from explicit
ABI changes, but perhaps I'm misunderstanding the ultimate direction still.
If I'm way off base, please let me know :) It'll save some extra patch-churn.
Thanks!
will
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo Molnar[dropping microblaze and roland]
Quoted 9 lines · Ingo Molnar
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Ingo Molnar
I think the sanest semantics is to run all active callbacks as well.
For example if this is used for three stacked security policies - as if 3 LSM modules were stacked at once. We'd call all three, and we'd determine that at least one failed - and we'd return a failure.
Quoted 7 lines
Quoted 3 lines
But that only works for boolean functions where you can return the multi-bit-or of the result. What if you need to return the specific error code.
Do you mean that one filter returns
-EINVALwhile the other-EACCES?Seems like a non-problem to me, we'd return the first nonzero value.
Sounds so easy! Why haven't LSMs stacked already? Because what happens if one of these hooks did something stateful? Lets say on open, hook #1 returns EPERM. hook #2 allocates memory. The open is going to fail and hooks #2 is never going to get the close() which should have freed the allocation. If you can be completely stateless its easier, but there's a reason that stacking security modules is hard. Serge has tried in the past and both dhowells and casey schaufler are working on it right now.
Stacking is never as easy as it sounds :)
-Eric
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system callfiltering
Quoted 2 lines
... If you can be completely stateless its easier, but there's a reason that stacking security modules is hard. Serge has tried in
the
Quoted 1 line
past and both dhowells and casey schaufler are working on it right
now.
Quoted 1 line
Stacking is never as easy as it sounds :)
For a bad example of trying to allow alternate security models look at NetBSD's kauth code :-)
NetBSD also had issues where some 'system call trace' code was being used to (try to) apply security - unfortunately it worked by looking at the user-space buffers on system call entry - and a multithreaded program can easily arrange to update them after the initial check!
For trace/event type activities this wouldn't really matter, for security policy it does.
(I've not looked directly at these event points in linux)
David
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system callfiltering
Quoted 3 lines · David Laight
[...] unfortunately it worked by looking at the user-space buffers on system call entry - and a multithreaded program can easily arrange to update them after the initial check! [...]
Such problems of reliability/persistency of security checks is exactly one of my arguments why this should not be limited to the syscall boundary, if you read the example i have provided in this discussion.
Thanks,
Ingo
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Peter ZijlstraQuoted 9 lines · Peter Zijlstra
Quoted 6 lines
Why should we have two callbacks next to each other:
event_vfs_getname(result); result = check_event_vfs_getname(result);if one could do it all?
Did you actually read the bit where I said that check_event_* (although I still think that name sucks) could imply a matching event_*?
No, did not notice that - and yes that solves this particular problem.
So given that by your own admission it makes sense to share the facilities at the low level, i also argue that it makes sense to share as high up as possible.
Are you perhaps arguing for a ->observe flag that would make 100% sure that the default behavior for events is observe-only? That would make sense indeed.
Otherwise both cases really want to use all the same facilities for event discovery, setup, control and potential extraction of events.
Thanks,
Ingo
Peter Zijlstra
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 4 lines · Ingo Molnar
So given that by your own admission it makes sense to share the facilities at the low level, i also argue that it makes sense to share as high up as possible.
I'm not saying any such thing, I'm saying that it might make sense to observe active objects and auto-create these observation points. That doesn't make them similar or make them share anything.
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 9 lines · Peter Zijlstra
Quoted 4 lines · Ingo Molnar
So given that by your own admission it makes sense to share the facilities at the low level, i also argue that it makes sense to share as high up as possible.
I'm not saying any such thing, I'm saying that it might make sense to observe active objects and auto-create these observation points. That doesn't make them similar or make them share anything.
Well, they would share the lowest level call site:
result = check_event_vfs_getname(result);
You call it 'auto-generated call site', i call it a shared (single line) call site. The same thing as far as the lowest level goes.
Now (the way i understood it) you'd want to stop the sharing right after that. I argue that it should go all the way up.
Note: i fully agree that there should be events where filters can have no effect whatsoever. For example if this was written as:
check_event_vfs_getname(result);
Then it would have no effect. This is decided by the subsystem developers, obviously. So whether an event is 'active' or 'passive' can be enforced at the subsystem level as well.
As far as the event facilities go, 'no effect observation' is a special-case of 'active observation' - just like read-only files are a special case of read-write files.
Thanks,
Ingo
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo Molnar[dropping microblaze and roland]
Quoted 5 lines · Ingo Molnar
* James Morris <[email protected]> wrote:
It is a simple and sensible security feature, agreed? It allows most code to run well and link to countless libraries - but no access to other files is allowed.
It's simple enough and sounds reasonable, but you can read all the discussion about AppArmour why many people don't really think it's the best. Still, I'll agree it's a lot better than nothing.
Quoted 9 lines
But if i had a VFS event at the fs/namei.c::getname() level, i would have access to a central point where the VFS string becomes stable to the kernel and can be checked (and denied if necessary).
A sidenote, and not surprisingly, the audit subsystem already has an event callback there:
audit_getname(result);
Quoted 7 lines
Unfortunately this audit callback cannot be used for my purposes, because the event is single-purpose for auditd and because it allows no feedback (no deny/accept discretion for the security policy).
But if had this simple event there:
err = event_vfs_getname(result);
Wow it sounds so easy. Now lets keep extending your train of thought until we can actually provide the security provided by SELinux. What do we end up with? We end up with an event hook right next to every LSM hook. You know, the LSM hooks were placed where they are for a reason. Because those were the locations inside the kernel where you actually have information about the task doing an operation and the objects (files, sockets, directories, other tasks, etc) they are doing an operation on.
Honestly all you are talking about it remaking the LSM with 2 sets of hooks instead if 1. Why? It seems much easier that if you want the language of the filter engine you would just make a new LSM that uses the filter engine for it's policy language rather than the language created by SELinux or SMACK or name your LSM implementation.
Quoted 14 lines
- unprivileged: application-definable, allowing the embedding of security policy in *apps* as well, not just the system
- flexible: can be added/removed runtime unprivileged, and cheaply so
- transparent: does not impact executing code that meets the policy
- nestable: it is inherited by child tasks and is fundamentally stackable,
multiple policies will have the combined effect and they are transparent to each other. So if a child task within a sandbox adds *more* checks then those add to the already existing set of checks. We only narrow permissions, never extend them.
Quoted 5 lines
- generic: allowing observation and (safe) control of security relevant
parameters not just at the system call boundary but at other relevant places of kernel execution as well: which points/callbacks could also be used for other types of event extraction such as perf. It could even be shared with audit ...
I'm not arguing that any of these things are bad things. What you describe is a new LSM that uses a discretionary access control model but with the granularity and flexibility that has traditionally only existed in the mandatory access control security modules previously implemented in the kernel.
I won't argue that's a bad idea, there's no reason in my mind that a process shouldn't be allowed to control it's own access decisions in a more flexible way than rwx bits. Then again, I certainly don't see a reason that this syscall hardening patch should be held up while a whole new concept in computer security is contemplated...
-Eric
Peter Zijlstra
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 3 lines · Eric Paris
Then again, I certainly don't see a
reason that this syscall hardening patch should be held up while a whole new concept in computer security is contemplated...
Which makes me wonder why this syscall hardening stuff is done outside of LSM? Why isn't is part of the LSM so that say SELinux can have a syscall bitmask per security context?
Making it part of the LSM also avoids having to add this prctl().
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Eric Paris
Then again, I certainly don't see a
reason that this syscall hardening patch should be held up while a whole new concept in computer security is contemplated...Which makes me wonder why this syscall hardening stuff is done outside of LSM? Why isn't is part of the LSM so that say SELinux can have a syscall bitmask per security context?
I could do that, but I like Will's approach better. From the PoV of meeting security goals of information flow, data confidentiality, integrity, least priv, etc limiting on the syscall boundary doesn't make a lot of sense. You just don't know enough there to enforce these things. These are the types of goals that SELinux and other LSMs have previously tried to enforce. From the PoV of making the kernel more resistant to attacks and making a process more resistant to misbehavior I think that the syscall boundary is appropriate. Although I could do it in SELinux it don't really want to do it there.
In case people are interested or confused let me give my definition of two words I've used a bit in these conversations: discretionary and mandatory. Any time I talk about a 'discretionary' security decision it is a security decisions that a process imposed upon itself. Aka the choice to use seccomp is discretionary. The choice to mark our own file u-wx is discretionary. This isn't the best definition but it's one that works well in this discussion. Mandatory security is one enforce by a global policy. It's what selinux is all about. SELinux doesn't give hoot what a process wants to do, it enforces a global policy from the top down. You take over a process, well, too bad, you still have no choice but to follow the mandatory policy.
The LSM does NOT enforce a mandatory access control model, it's just how it's been used in the past. Ingo appears to me (please correct me if I'm wrong) to really be a fan of exposing the flexibility of the LSM to a discretionary access control model. That doesn't seem like a bad idea. And maybe using the filter engine to define the language to do this isn't a bad idea either. But I think that's a 'down the road' project, not something to hold up a better seccomp.
Quoted 1 line
Making it part of the LSM also avoids having to add this
prctl().
Well, it would mean exposing some new language construct to every LSM (instead of a single prctl construct) and it would mean anyone wanting to use the interface would have to rely on the LSM implementing those hooks the way they need it. Honestly chrome can already get all of the benefits of this patch (given a perfectly coded kernel) and a whole lot more using SELinux, but (surprise surprise) not everyone uses SELinux. I think it's a good idea to expose a simple interface which will be widely enough adopted that many userspace applications can rely on it for hardening.
The existence of the LSM and the fact that there exists multiple security modules that may or may not be enabled really leads application developers to be unable to rely on LSM for security. If linux had a single security model which everyone could rely on we wouldn't really have as big of an issue but that's not possible. So I'm advocating for this series which will provide a single useful change which applications can rely upon across distros and platforms to enhance the properties and abilities of the linux kernel.
-Eric
Will Drewry
reviewerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 10 lines · Eric Paris
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Eric Paris
Then again, I certainly don't see a
reason that this syscall hardening patch should be held up while a whole new concept in computer security is contemplated...Which makes me wonder why this syscall hardening stuff is done outside of LSM? Why isn't is part of the LSM so that say SELinux can have a syscall bitmask per security context?
Quoted 9 lines
I could do that, but I like Will's approach better. ?From the PoV of meeting security goals of information flow, data confidentiality, integrity, least priv, etc limiting on the syscall boundary doesn't make a lot of sense. ?You just don't know enough there to enforce these things. ?These are the types of goals that SELinux and other LSMs have previously tried to enforce. ?From the PoV of making the kernel more resistant to attacks and making a process more resistant to misbehavior I think that the syscall boundary is appropriate. ?Although I could do it in SELinux it don't really want to do it there.
There's also the problem that there are no hooks per-system call for LSMs, only logical hooks that sometimes mirror system call names and are called after user data has been parsed. If system call enter hooks, like seccomp's, were added for LSMs, it would allow the lsm bitmask approach, but it still wouldn't satisfy the issues you raise below (and I wholeheartedly agree with).
Quoted 44 lines
In case people are interested or confused let me give my definition of two words I've used a bit in these conversations: discretionary and mandatory. ?Any time I talk about a 'discretionary' security decision it is a security decisions that a process imposed upon itself. ?Aka the choice to use seccomp is discretionary. ?The choice to mark our own file u-wx is discretionary. ?This isn't the best definition but it's one that works well in this discussion. ?Mandatory security is one enforce by a global policy. ?It's what selinux is all about. ?SELinux doesn't give hoot what a process wants to do, it enforces a global policy from the top down. ?You take over a process, well, too bad, you still have no choice but to follow the mandatory policy.
The LSM does NOT enforce a mandatory access control model, it's just how it's been used in the past. ?Ingo appears to me (please correct me if I'm wrong) to really be a fan of exposing the flexibility of the LSM to a discretionary access control model. ?That doesn't seem like a bad idea. ?And maybe using the filter engine to define the language to do this isn't a bad idea either. ?But I think that's a 'down the road' project, not something to hold up a better seccomp.
Quoted 1 line
Making it part of the LSM also avoids having to add this
prctl().Well, it would mean exposing some new language construct to every LSM (instead of a single prctl construct) and it would mean anyone wanting to use the interface would have to rely on the LSM implementing those hooks the way they need it. ?Honestly chrome can already get all of the benefits of this patch (given a perfectly coded kernel) and a whole lot more using SELinux, but (surprise surprise) not everyone uses SELinux. I think it's a good idea to expose a simple interface which will be widely enough adopted that many userspace applications can rely on it for hardening.
The existence of the LSM and the fact that there exists multiple security modules that may or may not be enabled really leads application developers to be unable to rely on LSM for security. ?If linux had a single security model which everyone could rely on we wouldn't really have as big of an issue but that's not possible. ?So I'm advocating for this series which will provide a single useful change which applications can rely upon across distros and platforms to enhance the properties and abilities of the linux kernel.
-Eric
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Eric ParisQuoted 11 lines · Eric Paris
[dropping microblaze and roland]
Quoted 5 lines · Ingo Molnar
* James Morris <[email protected]> wrote:
It is a simple and sensible security feature, agreed? It allows most code to run well and link to countless libraries - but no access to other files is allowed.
It's simple enough and sounds reasonable, but you can read all the discussion about AppArmour why many people don't really think it's the best. [...]
I have to say most of the participants of the AppArmour flamefests were dead wrong, and it wasnt the AppArmour folks who were wrong ...
The straight ASCII VFS namespace *makes sense*, and yes, the raw physical objects space that SELinux uses makes sense as well.
And no, i do not subscribe to the dogma that it is not possible to secure the ASCII VFS namespace: it evidently is possible, if you know and handle the ambiguitites. It is also obviously true that the ASCII VFS namespaces we use every day are a *lot* more intuitive than the labeled physical objects space
...
What all the security flamewars missed is the simple fact that being intuitive matters a *lot* not just to not annoy users, but also to broaden the effective number of security-conscious developers ...
Quoted 17 lines
Quoted 7 lines
Unfortunately this audit callback cannot be used for my purposes, because the event is single-purpose for auditd and because it allows no feedback (no deny/accept discretion for the security policy).
But if had this simple event there:
err = event_vfs_getname(result);Wow it sounds so easy. Now lets keep extending your train of thought until we can actually provide the security provided by SELinux. What do we end up with? We end up with an event hook right next to every LSM hook. You know, the LSM hooks were placed where they are for a reason. Because those were the locations inside the kernel where you actually have information about the task doing an operation and the objects (files, sockets, directories, other tasks, etc) they are doing an operation on.
Quoted 2 lines
Honestly all you are talking about it remaking the LSM with 2 sets of hooks instead if 1. Why? [...]
Not at all. I am taking about using *one* set of events, to keep the intrusion at the lowest possible level.
LSM could make use of them as well.
Obviously for pragmatic reasons that might not be feasible initially.
Quoted 4 lines
[...] It seems much easier that if you want the language of the filter engine you would just make a new LSM that uses the filter engine for it's policy language rather than the language created by SELinux or SMACK or name your LSM implementation.
Correct, that is what i suggested.
Note that performance is a primary concern, so if certain filters are very popular then in practice this would come with support for a couple of 'built in' (pre-optimized) filters that the kernel can accelerate directly, so that we do not incure the cost of executing the filter preds for really common-sense security policies that almost everyone is using.
I.e. in the end we'd *roughly* end up with the same performance and security as we are today (i mean, SELinux and the other LSMs did a nice job of collecting the things that apps should be careful about), but the crutial difference isnt just the advantages i menioned, but the fact that the *development model* of security modules would be a *lot* more extensible.
So security efforts could move to a whole different level: they could move into key apps and they could integrate with the general mind-set of developers.
At least Will as an application framework developer cares, so that hope is justified i think.
Quoted 26 lines
Quoted 14 lines
- unprivileged: application-definable, allowing the embedding of security policy in *apps* as well, not just the system
- flexible: can be added/removed runtime unprivileged, and cheaply so
- transparent: does not impact executing code that meets the policy
- nestable: it is inherited by child tasks and is fundamentally stackable,
multiple policies will have the combined effect and they are transparent to each other. So if a child task within a sandbox adds *more* checks then those add to the already existing set of checks. We only narrow permissions, never extend them.Quoted 5 lines
- generic: allowing observation and (safe) control of security relevant
parameters not just at the system call boundary but at other relevant places of kernel execution as well: which points/callbacks could also be used for other types of event extraction such as perf. It could even be shared with audit ...I'm not arguing that any of these things are bad things. What you describe is a new LSM that uses a discretionary access control model but with the granularity and flexibility that has traditionally only existed in the mandatory access control security modules previously implemented in the kernel.
Quoted 5 lines
I won't argue that's a bad idea, there's no reason in my mind that a process shouldn't be allowed to control it's own access decisions in a more flexible way than rwx bits. Then again, I certainly don't see a reason that this syscall hardening patch should be held up while a whole new concept in computer security is contemplated...
Note, i'm not actually asking for the moon, a pony and more.
I fully submit that we are yet far away from being able to do a full LSM via this mechanism.
What i'm asking for is that because the syscall point steps taken by Will look very promising so it would be nice to do *that* in a slightly more flexible scheme that does not condemn it to be limited to the syscall boundary and such
...
Also, to answer you, do you say that by my argument someone should have stood up and said 'no' to the LSM mess that was introduced a couple of years ago and which caused so many problems:
- kernel inefficiencies and user-space overhead
- stalled security efforts
- infighting
- friction, fragmentation, overmodularization
- non-stackability
- security annoyances on the Linux desktop
- probably *less* Linux security
and should have asked them to do something better designed instead?
Thanks,
Ingo
Will Drewry
reviewerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 117 lines · Ingo Molnar
Quoted 11 lines · Eric Paris
[dropping microblaze and roland]
Quoted 5 lines · Ingo Molnar
* James Morris <[email protected]> wrote:
It is a simple and sensible security feature, agreed? It allows most code to run well and link to countless libraries - but no access to other files is allowed.
It's simple enough and sounds reasonable, but you can read all the discussion about AppArmour why many people don't really think it's the best. [...]
I have to say most of the participants of the AppArmour flamefests were dead wrong, and it wasnt the AppArmour folks who were wrong ...
The straight ASCII VFS namespace *makes sense*, and yes, the raw physical objects space that SELinux uses makes sense as well.
And no, i do not subscribe to the dogma that it is not possible to secure the ASCII VFS namespace: it evidently is possible, if you know and handle the ambiguitites. It is also obviously true that the ASCII VFS namespaces we use every day are a *lot* more intuitive than the labeled physical objects space
...What all the security flamewars missed is the simple fact that being intuitive matters a *lot* not just to not annoy users, but also to broaden the effective number of security-conscious developers ...
Quoted 17 lines
Quoted 7 lines
Unfortunately this audit callback cannot be used for my purposes, because the event is single-purpose for auditd and because it allows no feedback (no deny/accept discretion for the security policy).
But if had this simple event there:
? ? err =
event_vfs_getname(result);Wow it sounds so easy. ?Now lets keep extending your train of thought until we can actually provide the security provided by SELinux. ?What do we end up with? ?We end up with an event hook right next to every LSM hook. ?You know, the LSM hooks were placed where they are for a reason. Because those were the locations inside the kernel where you actually have information about the task doing an operation and the objects (files, sockets, directories, other tasks, etc) they are doing an operation on.
Quoted 2 lines
Honestly all you are talking about it remaking the LSM with 2 sets of hooks instead if 1. ?Why? [...]
Not at all. I am taking about using *one* set of events, to keep the intrusion at the lowest possible level.
LSM could make use of them as well.
Obviously for pragmatic reasons that might not be feasible initially.
Quoted 4 lines
[...] ?It seems much easier that if you want the language of the filter engine you would just make a new LSM that uses the filter engine for it's policy language rather than the language created by SELinux or SMACK or name your LSM implementation.
Correct, that is what i suggested.
Note that performance is a primary concern, so if certain filters are very popular then in practice this would come with support for a couple of 'built in' (pre-optimized) filters that the kernel can accelerate directly, so that we do not incure the cost of executing the filter preds for really common-sense security policies that almost everyone is using.
I.e. in the end we'd *roughly* end up with the same performance and security as we are today (i mean, SELinux and the other LSMs did a nice job of collecting the things that apps should be careful about), but the crutial difference isnt just the advantages i menioned, but the fact that the *development model* of security modules would be a *lot* more extensible.
So security efforts could move to a whole different level: they could move into key apps and they could integrate with the general mind-set of developers.
At least Will as an application framework developer cares, so that hope is justified i think.
Quoted 26 lines
Quoted 14 lines
?- unprivileged: ?application-definable, allowing the embedding of security ? ? ? ? ? ? ? ? ? policy in *apps* as well, not just the system
?- flexible: ? ? ?can be added/removed runtime unprivileged, and cheaply so
?- transparent: ? does not impact executing code that meets the policy
?- nestable: ? ? ?it is inherited by child tasks and is fundamentally stackable, ? ? ? ? ? ? ? ? ? multiple policies will have the combined effect and they ? ? ? ? ? ? ? ? ? are transparent to each other. So if a child task within a ? ? ? ? ? ? ? ? ? sandbox adds *more* checks then those add to the already ? ? ? ? ? ? ? ? ? existing set of checks. We only narrow permissions, never ? ? ? ? ? ? ? ? ? extend them.
Quoted 5 lines
?- generic: ? ? ? allowing observation and (safe) control of security relevant ? ? ? ? ? ? ? ? ? parameters not just at the system call boundary but at other ? ? ? ? ? ? ? ? ? relevant places of kernel execution as well: which ? ? ? ? ? ? ? ? ? points/callbacks could also be used for other types of event ? ? ? ? ? ? ? ? ? extraction such as perf. It could even be shared with audit ...
I'm not arguing that any of these things are bad things. ?What you describe is a new LSM that uses a discretionary access control model but with the granularity and flexibility that has traditionally only existed in the mandatory access control security modules previously implemented in the kernel.
Quoted 5 lines
I won't argue that's a bad idea, there's no reason in my mind that a process shouldn't be allowed to control it's own access decisions in a more flexible way than rwx bits. ?Then again, I certainly don't see a reason that this syscall hardening patch should be held up while a whole new concept in computer security is contemplated...
Note, i'm not actually asking for the moon, a pony and more.
Quoted 7 lines
I fully submit that we are yet far away from being able to do a full LSM via this mechanism.
What i'm asking for is that because the syscall point steps taken by Will look very promising so it would be nice to do *that* in a slightly more flexible scheme that does not condemn it to be limited to the syscall boundary and such
...
What do you suggest here?
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 10 lines · Will Drewry
Quoted 9 lines
Note, i'm not actually asking for the moon, a pony and more.
I fully submit that we are yet far away from being able to do a full LSM via this mechanism.
What i'm asking for is that because the syscall point steps taken by Will look very promising so it would be nice to do *that* in a slightly more flexible scheme that does not condemn it to be limited to the syscall boundary and such ...
Quoted 6 lines
What do you suggest here?
From my brief exploration of the ftrace/perf (and seccomp) code, I don't see a clean way of integrating over the existing interfaces to the ftrace framework (e.g., the global perf event pump seems to be a mismatch), but I may be missing something obvious. [...]
Well, there's no global perf event pump. Here we'd obviously want to use buffer-less events that do no output (pumping) whatsoever - i.e. just counting events with filters attached.
What i suggest is to:
- share syscall events # you are fine with that as your patch makes use of them
- share the scripting engine # you are fine with that as your patch makes use of it
- share *any* other event to do_exit() a process at syscall exit time
- share any other active event that kernel developers specifically enable for active use to impact security-relevant execution even sooner than syscall exit time - not just system calls
- share the actual facility that manages (sets/gets) filters
So right now you have this structure for your new feature:
Documentation/trace/seccomp_filter.txt | 75 +++++ arch/x86/kernel/ldt.c | 5 arch/x86/kernel/tls.c | 7 fs/proc/array.c | 21 + fs/proc/base.c | 25 + include/linux/ftrace_event.h | 9 include/linux/seccomp.h | 98 +++++++ include/linux/syscalls.h | 54 ++-- include/trace/syscall.h | 6 kernel/Makefile | 1 kernel/fork.c | 8 kernel/perf_event.c | 7 kernel/seccomp.c | 144 ++++++++++- kernel/seccomp_filter.c | 428 +++++++++++++++++++++++++++++++++ kernel/sys.c | 11 kernel/trace/Kconfig | 10 kernel/trace/trace_events_filter.c | 60 ++-- kernel/trace/trace_syscalls.c | 96 ++++++- 18 files changed, 986 insertions(+), 79 deletions(-)
Firstly, one specific problem i can see is that kernel/seccomp_filter.c hardcodes to the system call boundary. Which is fine to a prototype implementation (and obviously fine for something that builds upon seccomp) but not fine in terms of a flexible Linux security feature :-)
You have hardcoded these syscall assumptions via:
struct seccomp_filter {
struct list_head list;
struct rcu_head rcu;
int syscall_nr;
struct syscall_metadata *data;
struct event_filter *event_filter;};
Which comes just because you chose to enumerate only syscall events - instead of enumerating all events.
Instead of that please bind to all events instead - syscalls are just one of the many events we have. Type 'perf list' and see how many event types we have, and quite a few could be enabled for 'active feedback' sandboxing as well.
Secondly, and this is really a variant of the first problem you have, the way you process event filter 'failures' is pretty limited. You utilize the regular seccomp method which works by calling into __secure_computing() and silently accepting syscalls or generating a hard do_exit() on even the slightest of filter failures.
Instead of that what we'd want to have is to have regular syscall events registered, *and* enabled for such active filtering purposes. The moment the filter hits such an 'active' event would set the TIF_NOTIFY_RESUME flag and some other attribute in the task and the kernel would do a do_exit() at the earliest of opportunities before calling the syscall or at the return-from-syscall point latest.
Note that no seccomp specific code would have to execute here, we can already generate events both at syscall entry and at syscall exit points, the only new bit we'd need is for the 'kill the task' [or abort the syscall] policy action.
This is *far* more generic still yields the same short-term end result as far as your sandboxing is concerned.
What we'd need for this is a way to mark existing TRACE_EVENT()s as 'active'. We'd mark all syscall events as 'active' straight away.
[ Detail: these trace events return a return code, which the calling code can
use, that way event return values could be used sooner than syscall exit points. IRQ code could make use of it as well, so for example this way we could filter based early packet inspection, still in the IRQ code. ]
Then what your feature would do is to simply open up the events you are interested in (just as counting events, without any buffers), and set 'active' filters in them them to 'deny/allow' specific combinations of parameters only.
Thirdly, you have added a separate facility to set/query filters via /proc, this lives mostly in /proc/<pid>/seccomp_filter. This seccomp specificity would go away altogether: it should be possible to query existing events and filters even outside of the seccomp facility, so if you want something explicit here beyond the current event ioctl() to set filters please improve the generic facility - which right now is poorer than what you have implemented so it's in good need to be improved! :-)
All in one such a solution would enable us to reuse code in a lot more deeper fashion while still providing all the functionality you are interested in. These are all short-term concerns, not pie-in-the-sky future ideal LSM concerns.
Thanks,
Ingo
Will Drewry
reviewerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 15 lines · Ingo Molnar
Quoted 12 lines · Will Drewry
Quoted 9 lines
Note, i'm not actually asking for the moon, a pony and more.
I fully submit that we are yet far away from being able to do a full LSM via this mechanism.
What i'm asking for is that because the syscall point steps taken by Will look very promising so it would be nice to do *that* in a slightly more flexible scheme that does not condemn it to be limited to the syscall boundary and such ...
What do you suggest here?
Quoted 8 lines
Quoted 4 lines
From my brief exploration of the ftrace/perf (and seccomp) code, I don't see a clean way of integrating over the existing interfaces to the ftrace framework (e.g., the global perf event pump seems to be a mismatch), but I may be missing something obvious. [...]
Well, there's no global perf event pump. Here we'd obviously want to use buffer-less events that do no output (pumping) whatsoever - i.e. just counting events with filters attached.
Cool - I missed these entirely. I'll get reading :)
Quoted 5 lines
What i suggest is to:
?- share syscall events ? ? ? ?# you are fine with that as your patch makes use of them ?- share the scripting engine ?# you are fine with that as your patch makes use of it
Quoted 7 lines
?- share *any* other event to
do_exit()a process at syscall exit time?- share any other active event that kernel developers specifically enable ? for active use to impact security-relevant execution even sooner than ? syscall exit time - not just system calls
?- share the actual facility that manages (sets/gets) filters
These make sense to me at a high level. I'll throw in a few initial comments, but I'll be back for a round-two once I catch up on the rest of the perf code.
Quoted 29 lines
So right now you have this structure for your new feature:
?Documentation/trace/seccomp_filter.txt | ? 75 +++++ ?arch/x86/kernel/ldt.c ? ? ? ? ? ? ? ? ?| ? ?5 ?arch/x86/kernel/tls.c ? ? ? ? ? ? ? ? ?| ? ?7 ?fs/proc/array.c ? ? ? ? ? ? ? ? ? ? ? ?| ? 21 + ?fs/proc/base.c ? ? ? ? ? ? ? ? ? ? ? ? | ? 25 + ?include/linux/ftrace_event.h ? ? ? ? ? | ? ?9 ?include/linux/seccomp.h ? ? ? ? ? ? ? ?| ? 98 +++++++ ?include/linux/syscalls.h ? ? ? ? ? ? ? | ? 54 ++-- ?include/trace/syscall.h ? ? ? ? ? ? ? ?| ? ?6 ?kernel/Makefile ? ? ? ? ? ? ? ? ? ? ? ?| ? ?1 ?kernel/fork.c ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ?8 ?kernel/perf_event.c ? ? ? ? ? ? ? ? ? ?| ? ?7 ?kernel/seccomp.c ? ? ? ? ? ? ? ? ? ? ? | ?144 ++++++++++- ?kernel/seccomp_filter.c ? ? ? ? ? ? ? ?| ?428 +++++++++++++++++++++++++++++++++ ?kernel/sys.c ? ? ? ? ? ? ? ? ? ? ? ? ? | ? 11 ?kernel/trace/Kconfig ? ? ? ? ? ? ? ? ? | ? 10 ?kernel/trace/trace_events_filter.c ? ? | ? 60 ++-- ?kernel/trace/trace_syscalls.c ? ? ? ? ?| ? 96 ++++++-?18 files changed, 986 insertions(+), 79 deletions(-)
Firstly, one specific problem i can see is that
kernel/seccomp_filter.chardcodes to the system call boundary. Which is fine to a prototype implementation (and obviously fine for something that builds upon seccomp) but not fine in terms of a flexible Linux security feature :-)You have hardcoded these syscall assumptions via:
Quoted 7 lines
?struct
seccomp_filter{
? ? ? ?structlist_headlist;
? ? ? ?structrcu_headrcu;
? ? ? ?intsyscall_nr;
? ? ? ?structsyscall_metadata*data;
? ? ? ?structevent_filter*event_filter;
?};
(This structure is a bit different in the new rev of the patch, but nothing relevant to this specific part of the discussion :)
Quoted 2 lines
Which comes just because you chose to enumerate only syscall events - instead of enumerating all events.
While I'm willing to live with the tradeoff, using ftrace event numbers from FTRACE_SYSCALLS means that the filter will be unable to hook a fair number of syscalls: execve, clone, etc (all the ptregs fixup syscalls on x86) and anything that returns int instead of long (on x86). Though the last two patches in the initial patch series provided a proposed clean up for the latter :/
The current revision of the seccomp filter patch can function in a bitmask-like state when CONFIG_FTRACE is unset or CONFIG_FTRACE_SYSCALLS is unset. This also means any platform with CONFIG_SECCOMP support can start using this right away, but I realize that is more of a short-term gain rather than a long-term one.
Quoted 17 lines
Instead of that please bind to all events instead - syscalls are just one of the many events we have. Type 'perf list' and see how many event types we have, and quite a few could be enabled for 'active feedback' sandboxing as well.
Secondly, and this is really a variant of the first problem you have, the way you process event filter 'failures' is pretty limited. You utilize the regular seccomp method which works by calling into
__secure_computing()and silently accepting syscalls or generating a harddo_exit()on even the slightest of filter failures.Instead of that what we'd want to have is to have regular syscall events registered, *and* enabled for such active filtering purposes. The moment the filter hits such an 'active' event would set the
TIF_NOTIFY_RESUMEflag and some other attribute in the task and the kernel would do ado_exit()at the earliest of opportunities before calling the syscall or at the return-from-syscall point latest.
Quoted 6 lines
Note that no seccomp specific code would have to execute here, we can already generate events both at syscall entry and at syscall exit points, the only new bit we'd need is for the 'kill the task' [or abort the syscall] policy action.
This is *far* more generic still yields the same short-term end result as far as your sandboxing is concerned.
Almost :/ I still need to review the code you've pointed out, but, at present, the ftrace hooks occur after the seccomp and syscall auditing hooks. This means that that code is exposed no matter what in this model. To trim the exposed surface to userspace, we really need those early hooks. While I can see both hacky and less hacky approaches around this, it stills strikes me that the seccomp thread flag and early interception are good to reuse. One option might be to allow seccomp to be a secure-syscall event source, but I suspect that lands more on the hack-y side of the fence :)
Anyway, I'll read up on the other filters and try to understand exactly how per-task, counting perf events are being handled.
Quoted 2 lines
What we'd need for this is a way to mark existing TRACE_EVENT()s as 'active'. We'd mark all syscall events as 'active' straight away.
It seems like we'll still end up special casing system calls no matter what as they are the userland vector into the kernel. Marking syscalls active right away sounds roughly equivalent to calling prctl(PR_SET_SECCOMP, 2). This could easily be done following the model of syscall_nr_to_meta() since that enumerates every system call meta data entry which yields both entry and exit event numbers. Of course, I'd need to understand how that ties in with the per-task, counting code.
Quoted 9 lines
[ Detail: these trace events return a return code, which the calling code can ?use, that way event return values could be used sooner than syscall exit ?points. IRQ code could make use of it as well, so for example this way we ?could filter based early packet inspection, still in the IRQ code. ]
Then what your feature would do is to simply open up the events you are interested in (just as counting events, without any buffers), and set 'active' filters in them them to 'deny/allow' specific combinations of parameters only.
Quoted 7 lines
Thirdly, you have added a separate facility to set/query filters via /proc, this lives mostly in /proc/<pid>/seccomp_filter. This seccomp specificity would go away altogether: it should be possible to query existing events and filters even outside of the seccomp facility, so if you want something explicit here beyond the current event
ioctl()to set filters please improve the generic facility - which right now is poorer than what you have implemented so it's in good need to be improved! :-)
I haven't looked at the other interface, so I will. I know about the existence of the perf_event_open syscall, but that's about it. Would it make sense to use the same syscall in a unified-interface world? Even if the right way, semantically it seems awkward.
Quoted 4 lines
All in one such a solution would enable us to reuse code in a lot more deeper fashion while still providing all the functionality you are interested in. These are all short-term concerns, not pie-in-the-sky future ideal LSM concerns.
Thanks for the concrete feedback! I'll follow up again once I better understand the code you're citing as well as the existing userland interface to it.
cheers!
will
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 4 lines · Will Drewry
Quoted 2 lines
This is *far* more generic still yields the same short-term end result as far as your sandboxing is concerned.
Almost :/ [...]
Hey that's a pretty good result from a subsystem that was not written with your usecase in mind *at all* ;-)
Quoted 8 lines
[...] I still need to review the code you've pointed out, but, at present, the ftrace hooks occur after the seccomp and syscall auditing hooks. This means that that code is exposed no matter what in this model. To trim the exposed surface to userspace, we really need those early hooks. While I can see both hacky and less hacky approaches around this, it stills strikes me that the seccomp thread flag and early interception are good to reuse. One option might be to allow seccomp to be a secure-syscall event source, but I suspect that lands more on the hack-y side of the fence :)
Agreed, there should be no security compromise imposed on your usecase, at all.
You could move the event callback sooner into the syscall-entry sequence to make sure it's the highest priority thing to process?
There's no semantic dependency on its current location so this can be changed AFAICS.
Thanks,
Ingo
James Morris
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo MolnarQuoted 6 lines · Ingo Molnar
Say i'm a user-space sandbox developer who wants to enforce that sandboxed code should only be allowed to open files in
/home/sandbox/,/lib/and/usr/lib/.It is a simple and sensible security feature, agreed? It allows most code to run well and link to countless libraries - but no access to other files is allowed.
Not really.
Firstly, what is the security goal of these restrictions? Then, are the restrictions complete and unbypassable?
How do you reason about the behavior of the system as a whole?
Quoted 3 lines
I argue that this is the LSM and audit subsystems designed right: in the long run it could allow everything that LSM does at the moment - and so much more
...
Now you're proposing a redesign of the security subsystem. That's a significant undertaking.
In the meantime, we have a simple, well-defined enhancement to seccomp which will be very useful to current users in reducing their kernel attack surface.
We should merge that, and the security subsystem discussion can carry on separately.
- James
Signature · 2 lines
James Morris <[email protected]>
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 6 lines · James Morris
Quoted 4 lines · Ingo Molnar
Say i'm a user-space sandbox developer who wants to enforce that sandboxed code should only be allowed to open files in
/home/sandbox/,/lib/and/usr/lib/.
Quoted 7 lines
Quoted 3 lines
It is a simple and sensible security feature, agreed? It allows most code to run well and link to countless libraries - but no access to other files is allowed.
Not really.
Firstly, what is the security goal of these restrictions? [...]
To do what i described above? Namely:
" Sandboxed code should only be allowed to open files in /home/sandbox/, /lib/
and /usr/lib/ "
Quoted 1 line
[...] Then, are the restrictions complete and unbypassable?
If only the system calls i mentioned are allowed, and if the sandboxed VFS namespace itself is isolated from the rest of the system (no bind mounts, no hard links outside the sandbox, etc.) then its goal is to not be bypassable - what use is a sandbox if the sandbox can be bypassed by the sandboxed code?
There's a few ways how to alter (and thus bypass) VFS namespace lookups:
symlinks, chdir, chroot, rename, etc., which (as i mentioned) have to be excluded by default or filtered as well.
Quoted 1 line
How do you reason about the behavior of the system as a whole?
For some usecases i mainly want to reason about what the sandboxed code can do and can not do, within a fairly static and limited VFS namespace environment.
I might not want to have a full-blown 'physical barrier' for all objects labeled as inaccessible to sandboxed code (or labeled as accessible to sandboxed code).
Especially as manipulating file labels is not also slow (affects all files) but is also often an exclusively privileged operation even for owned files, for no good reason. For things like /lib/ and /usr/lib/ it also *has* to be a privileged operation.
Quoted 6 lines
Quoted 3 lines
I argue that this is the LSM and audit subsystems designed right: in the long run it could allow everything that LSM does at the moment - and so much more ...
Now you're proposing a redesign of the security subsystem. That's a significant undertaking.
It certainly is.
Quoted 5 lines
In the meantime, we have a simple, well-defined enhancement to seccomp which will be very useful to current users in reducing their kernel attack surface.
We should merge that, and the security subsystem discussion can carry on separately.
Is that the development and merge process along which the LSM subsystem got into its current state?
Thanks,
Ingo
James Morris
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 8 lines · Ingo Molnar
Quoted 3 lines
Not really.
Firstly, what is the security goal of these restrictions? [...]
To do what i described above? Namely:
" Sandboxed code should only be allowed to open files in
/home/sandbox/,/lib/
and/usr/lib/"
These are access rules, they don't really describe a high-level security goal. How do you know it's ok to open everything in these directories?
- James
Signature · 2 lines
James Morris <[email protected]>
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 6 lines · James Morris
Quoted 4 lines · Ingo Molnar
Quoted 3 lines
Not really.
Firstly, what is the security goal of these restrictions? [...]
Quoted 7 lines
Quoted 4 lines
To do what i described above? Namely:
" Sandboxed code should only be allowed to open files in
/home/sandbox/,/lib/
and/usr/lib/"These are access rules, they don't really describe a high-level security goal. [...]
Restrictng sandboxed code to only open files within a given VFS namespace boundary sure sounds like a high-level security goal to me.
If implemented and set up correctly then it restricts sandboxed code to only be able to open files reachable via that VFS sub-namespace.
That is a rather meaningful high-level concept. What higher level concept do you want to argue?
Quoted 1 line
[...] How do you know it's ok to open everything in these directories?
How do you know it's ok to open /etc/hosts? The sysadmin has configured the system that way.
How do you know that it's ok for sandboxed code to open files in /home/sandbox/? The sandbox developer has configured the system that way.
I'm not sure i get your point.
Thanks,
Ingo
James Morris
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 1 line · Ingo Molnar
I'm not sure i get your point.
Your example was not complete as described. After an apparently simple specification, you've since added several qualifiers and assumptions, and I still doubt that it's complete.
A higher level goal would look like
"Allow a sandbox app access only to approved resources, to contain the effects of flaws in the app", or similar.
Note that this includes a threat model (remote attacker taking control of the app) and a general and fully stated strategy for dealing with it.
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 6 lines · James Morris
Quoted 1 line · Ingo Molnar
I'm not sure i get your point.
Your example was not complete as described. After an apparently simple specification, you've since added several qualifiers and assumptions, [...]
I havent added any qualifiers really (i added examples/description), the opt-in method i mentioned in my first mail should be pretty robust:
| Firstly, using the filter code i deny the various link creation syscalls so | that sandboxed code cannot escape for example by creating a symlink to | outside the permitted VFS namespace. (Note: we opt-in to syscalls, that way | new syscalls added by new kernels are denied by defalt. The current symlink | creation syscalls are not opted in to.)
Quoted 1 line
[...] and I still doubt that it's complete.
I could too claim that i doubt that the SELinux kernel implementation is secure!
So how about we both come up with specific examples about how it's not secure, instead of going down the fear-uncertainty-and-doubt road? ;-)
Quoted 4 lines
A higher level goal would look like
"Allow a sandbox app access only to approved resources, to contain the effects of flaws in the app", or similar.
I see what you mean.
I really think that "restricting sandboxed code to only open files within a given VFS namespace boundary" is the most useful highlevel description here - which is really a subset of a "allow a sandbox app access only to an easily approved set of files" highlevel concept.
There's no "to contain ..." bit here: *all* of the sandboxed app code is untrusted, so there's no 'remote attacker' and we do not limit our threat to flaws in the app. We want to contain apps to within a small subset of Linux functionality, and we want to do that within regular apps (without having to be superuser), full stop.
Quoted 2 lines
Note that this includes a threat model (remote attacker taking control of the app) and a general and fully stated strategy for dealing with it.
Attacker does not have to be remote - most sandboxing concepts protect against locally installed plugins/apps/applets. In sandboxing the whole app is considered untrusted - not just some flaw in it, abused remotely.
Quoted 4 lines
From there, you can start to analyze how to implement the goal, at which point you'd start thinking about configuration, assumptions, filesystem access, namespaces, indirect access (e.g. via sockets, rpc, ipc, shared memory, invocation).
Sandboxed code generally does not have access to anything fancy like that - if it is added then all possible side effects have to be examined.
Thanks,
Ingo
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to James MorrisQuoted 8 lines · James Morris
On Fri, 13 May 2011, Ingo Molnar wrote:
How do you reason about the behavior of the system as a whole?Quoted 3 lines
I argue that this is the LSM and audit subsystems designed right: in the long run it could allow everything that LSM does at the moment - and so much more
...
Quoted 6 lines
Now you're proposing a redesign of the security subsystem. That's a significant undertaking.
In the meantime, we have a simple, well-defined enhancement to seccomp which will be very useful to current users in reducing their kernel attack surface.
Well, you can do the same with subterfugue, even without kernel changes. But that's ptrace -- slow. (And it already shows that syscall based filters are extremely tricky to configure).
If yu want speed, seccomp+server for non-permitted operations seems like reasonable way.
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 12 lines · Pavel Machek
Quoted 11 lines · James Morris
On Fri, 13 May 2011, Ingo Molnar wrote:
How do you reason about the behavior of the system as a whole?Quoted 3 lines
I argue that this is the LSM and audit subsystems designed right: in the long run it could allow everything that LSM does at the moment - and so much more
...Now you're proposing a redesign of the security subsystem. That's a significant undertaking.
Quoted 7 lines
Quoted 3 lines
In the meantime, we have a simple, well-defined enhancement to seccomp which will be very useful to current users in reducing their kernel attack surface.
Well, you can do the same with subterfugue, even without kernel changes. But that's ptrace -- slow. (And it already shows that syscall based filters are extremely tricky to configure).
Yes, if you use syscall based filters to implement access to underlying objects where the access methods do not capture essential lifetime events properly (such as files) they you'll quickly run into trouble achieving a secure solution.
But you can robustly use syscall filters to control the underlying primary *resource*: various pieces of kernel code with *negative* utility to the current app - which have no use to the app but pose risks in terms of potential exploits in them.
But you can use event filters to implement arbitrary security policies robustly.
For example file objects: if you generate the right events for a class of objects then you can control access to them very robustly.
It's not a surprise that this is what SELinux does primarily: it has lifetime event hooks at the inode object (and socket, packet, etc.) level and captures those access attempts and validates them against the permissions of that object, in light of the accessing task's credentials.
Exactly that can be done with Will's patch as well, if its potential scope of event-checking points is not stupidly limited to the syscall boundary alone ...
Thanks,
Ingo
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Ingo MolnarQuoted 25 lines · Ingo Molnar
To restrict execution to system calls.
Two observations:
1) We already have a specific ABI for this: you can set filters for events via an event fd.
Why not extend that mechanism instead and improve *both* your sandboxing bits and the events code? This new seccomp code has a lot more to do with trace event filters than the minimal old seccomp code ...
kernel/trace/trace_event_filter.cis 2000 lines of tricky code that interprets the ASCII filter expressions.kernel/seccomp.cis 86 lines of mostly trivial code.2) Why should this concept not be made available wider, to allow the restriction of not just system calls but other security relevant components of the kernel as well?
This too, if you approach the problem via the events code, will be a natural end result, while if you approach it from the seccomp prctl angle it will be a limited hack only.
Note, the end result will be the same - just using a different ABI.
Quoted 7 lines
So i really think the ABI itself should be closer related to the event code. What this "seccomp" code does is that it uses specific syscall events to restrict execution of certain event generating codepaths, such as system calls.
Thanks,
Ingo
What's positive with that approach is that the code is all there already. Create a perf event for a given trace event, attach a filter to it.
What needs to be added is an override of the effect of the filter. By default it's dropping the event, but there may be different flavours, including sending a signal. All in one, extending the current code to allow that looks trivial.
The negative points are that
* trace events are supposed to stay passive and not act on the system, except doing some endpoint things like writing to a buffer. We can't call do_exit() from a tracepoint for example, preemption is disabled there.
* Also, is it actually relevant to extend that seccomp filtering to other events than syscalls? Exposing kernel events to filtering sounds actually to me bringing a new potential security issue. But with fine restrictions this can probably be dealt with. Especially if by default only syscalls can be filtered
* I think Peter did not want to give such "active" role to perf in the system.
James Morris
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Will Drewrykernel/seccomp_filter.c line 281
Quoted 7 lines · Will Drewry
kernel/seccomp_filter.c new file +7
-0+void seccomp_filter_log_failure (int syscall) +{ +printk (KERN_INFO +"%s[%d]: system call %d (%s) blocked at ip:%lx\n" , + current->comm,task_pid_nr (current), syscall, +syscall_nr_to_name (syscall),KSTK_EIP (current)); +}
I think it'd be a good idea to utilize the audit facility here.
- James
Signature · 2 lines
James Morris <[email protected]>
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Will DrewryQuoted 7 lines · Will Drewry
This change adds a new seccomp mode based on the work by
agl at chromium.org in [1]. This new mode, "filter mode", provides a hash table ofseccomp_filterobjects. When in the new mode (2), all system calls are checked against the filters - first by system call number, then by a filter string. If an entry exists for a given system call and all filter predicates evaluate to true, then the task may proceed. Otherwise, the task is killed (as perseccomp_mode== 1).
I've got a question about this: Do you expect the typical usage to disallow ioctl()? Given that ioctl alone is responsible for a huge number of exploits in various drivers, while certain ioctls are immensely useful (FIONREAD, FIOASYNC, ...), do you expect to extend the mechanism to filter specific ioctl commands in the future?
Arnd
Will Drewry
reviewerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 9 lines · Arnd Bergmann
Quoted 7 lines · Will Drewry
This change adds a new seccomp mode based on the work by
agl at chromium.org in [1]. This new mode, "filter mode", provides a hash table ofseccomp_filterobjects. ?When in the new mode (2), all system calls are checked against the filters - first by system call number, then by a filter string. ?If an entry exists for a given system call and all filter predicates evaluate to true, then the task may proceed. Otherwise, the task is killed (as perseccomp_mode== 1).
Quoted 5 lines
I've got a question about this: Do you expect the typical usage to disallow
ioctl()? Given that ioctl alone is responsible for a huge number of exploits in various drivers, while certain ioctls are immensely useful (FIONREAD, FIOASYNC, ...), do you expect to extend the mechanism to filter specific ioctl commands in the future?
In many cases, I do expect ioctl's to be dropped, but it's totally up to whoever is setting the filters.
As is, it can already help out: [even though an LSM, if available, would be appropriate to define a fine-grained policy]
ioctl() is hooked by the ftrace syscalls infrastructure (via SYSCALL_DEFINE3):
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned
long, arg)
This means you can do:
sprintf(filter, "cmd == %u || cmd == %u", FIOASYNC, FIONREAD); prctl(PR_SET_SECCOMP_FILTER, __NR_ioctl, filter);
...
prctl(PR_SET_SECCOMP, 2, 0);
and then you'll be able to call ioctl on any fd with any argument but limited to only the FIOASYNC and FIONREAD commands.
Depending on integration, it could even be limited to ioctl commands that are appropriate to a known fd if the fd is opened prior to entering seccomp mode 2. Alternatively, __NR__ioctl could be allowed with a filter of "1" then narrowed through a later addition of something like "(fd == %u && (cmd == %u || cmd == %u))" or something along those lines.
Does that make sense?
In general, this interface won't need specific extensions for most system call oriented filtering events. ftrace events may be expanded (to include more system calls), but that's behind the scenes. Only arguments subject to time-of-check-time-of-use attacks (data living in userspace passed in by pointer) are not safe to use via this interface. In theory, that limitation could also be lifted in the implementation without changing the ABI.
Thanks!
will
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 8 lines · Will Drewry
Depending on integration, it could even be limited to ioctl commands that are appropriate to a known fd if the fd is opened prior to entering seccomp mode 2. Alternatively,
__NR__ioctlcould be allowed with a filter of "1" then narrowed through a later addition of something like "(fd == %u && (cmd == %u || cmd == %u))" or something along those lines.Does that make sense?
Thanks for the explanation. This sounds like it's already doing all we need.
Arnd
PATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 8 lines · Arnd Bergmann
Quoted 7 lines · Will Drewry
Depending on integration, it could even be limited to ioctl commands that are appropriate to a known fd if the fd is opened prior to entering seccomp mode 2. Alternatively,
__NR__ioctlcould be allowed with a filter of "1" then narrowed through a later addition of something like "(fd == %u && (cmd == %u || cmd == %u))" or something along those lines.
Quoted 4 lines
Quoted 1 line
Does that make sense?
Thanks for the explanation. This sounds like it's already doing all we need.
One thing we could do more clearly here is to help keep the filter expressions symbolic - i.e. help resolve the various ioctl variants as well, not just the raw syscall parameter numbers.
But yes, access to the raw syscall parameters and the ability to filter them already gives us the ability to exclude/include specific ioctls in a rather flexible way.
Thanks,
Ingo
Steven Rostedt
maintainerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
In reply to Will DrewrySorry to be absent from this thread so far, I just got back from my travels and I'm now catching up on email.
Quoted 158 lines · Will Drewry
arch/arm/Kconfig +10
-0@@ -1664,6 +1664,16 @@ config SECCOMP and the task is only allowed to execute a few safe syscalls defined by each seccomp mode. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL + help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + config CC_STACKPROTECTOR bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)" depends on EXPERIMENTALarch/microblaze/Kconfig +10
-0@@ -129,6 +129,16 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL + help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + endmenu menu "Advanced setup"arch/mips/Kconfig +10
-0@@ -2245,6 +2245,16 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL + help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + config USE_OF bool "Flattened Device Tree support" select OFarch/powerpc/Kconfig +10
-0@@ -605,6 +605,16 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL + help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + endmenu config ISA_DMA_APIarch/s390/Kconfig +10
-0@@ -614,6 +614,16 @@ config SECCOMP If unsure, say Y. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL + help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + endmenu menu "Power Management"arch/sh/Kconfig +10
-0@@ -676,6 +676,16 @@ config SECCOMP If unsure, say N. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL + help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + config SMP bool "Symmetric multi-processing support" depends on SYS_SUPPORTS_SMParch/sparc/Kconfig +10
-0@@ -270,6 +270,16 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL + help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + config HOTPLUG_CPU bool "Support for hot-pluggable CPUs" depends on SPARC64 && SMParch/x86/Kconfig +3
-0@@ -1485,6 +1485,16 @@ config SECCOMP If unsure, say Y. Only embedded should say N here. +config SECCOMP_FILTER + bool "Enable seccomp-based system call filtering" + depends on SECCOMP && EXPERIMENTAL
arch/x86/Kconfig config SECCOMP · line 1500
Quoted 10 lines
arch/x86/Kconfig +7
-0+ help + Per-process, inherited system call filtering using shared code + across seccomp and ftrace_syscalls. If CONFIG_FTRACE_SYSCALLS + is not available, enhanced filters will not be available. + + See Documentation/prctl/seccomp_filter.txt for more detail. + config CC_STACKPROTECTOR bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)" ---help---
You just cut-and-pasted 8 copies of a config selection. The proper way to do that is to add the Kconfig selection in a core kernel Kconfig, have it depend on "HAVE_SECCOMP_FILTER" and then in each of these configs, simply add in the arch Kconfig:
config <ARCH>
[...] select HAVE_SECCOMP_FILTER
That way you don't need to duplicate the config option all over the place.
-- Steve
Will Drewry
reviewerPATCH 3/5 v2 seccomp_filters: Enable ftrace-based system call filtering
Quoted 180 lines · Steven Rostedt
Sorry to be absent from this thread so far, I just got back from my travels and I'm now catching up on email.
Quoted 165 lines · Will Drewry
arch/arm/Kconfig +0
-0@@ -1664,6 +1664,16 @@ config SECCOMP? ? ? ? and the task is only allowed to execute a few safe syscalls ? ? ? ? defined by each seccomp mode.
arch/arm/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +?config
CC_STACKPROTECTOR
? ? ? bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)" ? ? ? depends on EXPERIMENTALarch/microblaze/Kconfig +0
-0@@ -129,6 +129,16 @@ config SECCOMP? ? ? ? If unsure, say Y. Only embedded should say N here.
arch/microblaze/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +?endmenu
?menu "Advanced setup"
arch/mips/Kconfig +0
-0@@ -2245,6 +2245,16 @@ config SECCOMP? ? ? ? If unsure, say Y. Only embedded should say N here.
arch/mips/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +?config
USE_OF
? ? ? bool "Flattened Device Tree support"
? ? ? select OFarch/powerpc/Kconfig +0
-0@@ -605,6 +605,16 @@ config SECCOMP? ? ? ? If unsure, say Y. Only embedded should say N here.
arch/powerpc/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +?endmenu
?config
ISA_DMA_APIarch/s390/Kconfig +0
-0@@ -614,6 +614,16 @@ config SECCOMP? ? ? ? If unsure, say Y.
arch/s390/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +?endmenu
?menu "Power Management"
arch/sh/Kconfig +0
-0@@ -676,6 +676,16 @@ config SECCOMP? ? ? ? If unsure, say N.
arch/sh/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +?config SMP
? ? ? bool "Symmetric multi-processing support"
? ? ? depends onSYS_SUPPORTS_SMParch/sparc/Kconfig +0
-0@@ -270,6 +270,16 @@ config SECCOMP? ? ? ? If unsure, say Y. Only embedded should say N here.
arch/sparc/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +?config
HOTPLUG_CPU
? ? ? bool "Support for hot-pluggable CPUs"
? ? ? depends on SPARC64 && SMParch/x86/Kconfig +0
-0@@ -1485,6 +1485,16 @@ config SECCOMP? ? ? ? If unsure, say Y. Only embedded should say N here.
arch/x86/Kconfig +10
-0+config SECCOMP_FILTER + ? ? bool "Enable seccomp-based system call filtering" + ? ? depends on SECCOMP && EXPERIMENTAL + ? ? help + ? ? ? Per-process, inherited system call filtering using shared code + ? ? ? across seccomp and ftrace_syscalls. ?If CONFIG_FTRACE_SYSCALLS + ? ? ? is not available, enhanced filters will not be available. + + ? ? ? See Documentation/prctl/seccomp_filter.txt for more detail. +Quoted 3 lines
?config
CC_STACKPROTECTOR
? ? ? bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)" ? ? ? ---help---You just cut-and-pasted 8 copies of a config selection. The proper way to do that is to add the
Kconfigselection in a core kernelKconfig, have it depend on "HAVE_SECCOMP_FILTER" and then in each of these configs, simply add in the archKconfig:
Quoted 7 lines
config <ARCH>
? ? ? ?[...]
? ? ? ?selectHAVE_SECCOMP_FILTERThat way you don't need to duplicate the config option all over the place.
Thanks! I had seen the HAVE_* format but failed to acknowledge why!
I'll fix it in the next rev (assuming it still fits there)! will
Steven Rostedt
maintainerRe: [PATCH 3/7] seccomp_filter: Enable ftrace-based system call filtering
In reply to Will DrewryQuoted 7 lines · Will Drewry
Quoted 2 lines
Perhaps, but those free standing functions (the ones that are exported to modules) seem like they can destroy state.
My intent was to make them available for use by
seccomp.cduring state teardown/dup. I don't think there's benefit to exposing them outside of that. Would dropping the export, and adding an localseccomp.hwith the shared functions in them resolve that more cleanly?
Yes, having a local seccomp.h in the same directory as seccomp.c is a way of saying "these are internal functions, don't use them directly". Adding a function to EXPORT_SYMBOL*() is saying "here you go, have a field day with it!".
Quoted 8 lines
Quoted 7 lines
Your code would have been correct if you could call kzalloc under
rcu_read_lock()(which you can on some kernel configurations but not all). The issue is that you need to pull out that allocation from thercu_read_lock()becausercu_read_lockassumes you can't preempt, and that allocation can schedule out. The access to the filters must be done underrcu_read_lock(), other than that, you're fine.
Quoted 8 lines
That makes sense. I think I'd prefer to not share those functions rather than guard the list just in case a future consumer of the interface comes along. Would that make sense to you? Since I don't see any other users right now other than
seccomp.c, it might make sense to tackle the impact when an actual need arises.I'll go whichever way pointed on this, though.
thanks again,
I'm fine either way. If you make them local internal functions, then you don't need the locking if they are safe in their current context.
-- Steve
Serge E. Hallyn
maintainerRe: [PATCH 3/7] seccomp_filter: Enable ftrace-based system call filtering
In reply to Will DrewryQuoted 27 lines · Will Drewry
Quoted 8 lines · Serge E. Hallyn
Quoted 7 lines · Steven Rostedt
Quoted 6 lines · Serge E. Hallyn
I actually thought you were going to be more extreme about the seccomp state than you are: I thought you were going to tie a filter list to seccomp state. So adding or removing a filter would have required duping the seccomp state, duping all the filters, making the change in the copy, and then swapping the new state into place. Slow in the hopefully rare update case, but safe.
Hrm, I think I'm confused now! This is exactly what I *thought* the code was doing.
At present,
seccomp_statecan be shared across predecessor/ancestor relationships using refcounting infork.c(get/put). However, the only way to change a givenseccomp_stateor its filters is either through the one-biton_next_syscallchange or throughprctl_set_seccomp. Inprctl_set_seccomp, it does:
state = (orig_state?seccomp_state_dup(orig_state):seccomp_state_new());operates on the new state and then rcu_assign_pointer()s it to the task. I didn't intentionally provide any way to drop filters from an existing state object nor change the filtered syscalls on an in-use object. That
_dupcall should hit the impromperlyrcu_lockedcopy_all_filtersreturning duplicates of the original filters by reparsing thefilter_string.
Quoted 2 lines
Did I accidentally provide a means to mutate a state object or filter list without dup()ing? :/
Ah - probably not, I probably misread your intent for the helpers.
So in that case you don't need to rcu-protect the filters at all when copying. You just need to inc the seccomp struct refcount under rcu lock, to make sure it all stays put. Drop rcu lock, take your time copying, then when you dec the refcount the whole seccomp struct either gets freed or not.
Excellent.
-serge
Re: [PATCH 3/7] seccomp_filter: Enable ftrace-based system call filtering
In reply to Will DrewryQuoted 17 lines · Will Drewry
This change adds a new seccomp mode based on the work by [email protected]. This mode comes with a bitmask of
NR_syscallssize and an optional linked list ofseccomp_filterobjects. When in mode 2, all system calls are first checked against the bitmask to determine if they are allowed or denied. If allowed, the list of filters is checked for the given syscall number. If all filter predicates for the system call match or the system call was allowed without restriction, the process continues. Otherwise, it is killed and aKERN_INFOnotification is posted.The filter language itself is provided by the ftrace filter engine. Related patches tweak to the perf filter trace and free allow the calls to be shared. Filters inherit their understanding of types and arguments for each system call from the
CONFIG_FTRACE_SYSCALLSsubsystem which predefines this information insyscall_metadataassociatedenter_event(andexit_event) structures.
Quoted 5 lines
The result is that a process may reduce its available interfaces to the kernel through
prctl()without knowing the appropriate system call number a priori and with the flexibility of filtering based on register-stored arguments. (String checks suffer from TOCTOU issues and should be left to LSMs to provide policy for! Don't get greedy :)
This is potentially very useful for qemu/kvm.
Signature · 1 line
error compiling committee.c: too many arguments to function
Will Drewry
reviewerPATCH 4/7 seccomp_filter: add process state reporting
In reply to Will DrewryAdds seccomp and seccomp_filter status reporting to proc. /proc/<pid>/status will include a Seccomp field, and /proc/<pid>/seccomp_filter will provide read-only access to the current filter and bitmask set for seccomp_filters.
--- fs/proc/array.c | 21 +++++++++++++++++++++ fs/proc/base.c | 25 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 0 deletions(-)
fs/proc/array.c +21 -0
@@ -77,6 +77,7 @@#include <linux/cpuset.h> #include <linux/rcupdate.h> #include <linux/delayacct.h> +#include <linux/seccomp.h> #include <linux/seq_file.h> #include <linux/pid_namespace.h> #include <linux/ptrace.h>
@@ -337,6 +338,19 @@ static void task_cpus_allowed(struct seq_file *m, struct task_struct *task)seq_putc (m,'\n' ); } +static void task_show_seccomp (struct seq_file *m,struct task_struct *p) { +#if defined(CONFIG_SECCOMP ) +int mode; +struct seccomp_state * state; +rcu_read_lock (); + state =get_seccomp_state (rcu_dereference (p->seccomp.state)); + mode = state ? state->mode :0 ; +rcu_read_unlock (); +put_seccomp_state (state); +seq_printf (m,"Seccomp:\t%d\n" , mode); +#endif +} +int proc_pid_status (struct seq_file *m,struct pid_namespace *ns,struct pid *pid,struct task_struct *task) {
@@ -354,6 +368,7 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns,task_cpus_allowed (m, task);cpuset_task_status_allowed (m, task);task_context_switch_counts (m, task); +task_show_seccomp (m, task);return 0 ; }
@@ -544,3 +559,9 @@ int proc_pid_statm(struct seq_file *m, struct pid_namespace *ns,return 0 ; } + +int proc_pid_seccomp (struct seq_file *m,struct pid_namespace *ns, +struct pid *pid,struct task_struct *task) +{ +return 0 ; +}
fs/proc/base.c +25 -0
@@ -73,6 +73,7 @@#include <linux/security.h> #include <linux/ptrace.h> #include <linux/tracehook.h> +#include <linux/seccomp.h> #include <linux/cgroup.h> #include <linux/cpuset.h> #include <linux/audit.h>
@@ -579,6 +580,24 @@ static int proc_pid_syscall(struct task_struct *task, char *buffer) }#endif /* CONFIG_HAVE_ARCH_TRACEHOOK */ +#ifdef CONFIG_SECCOMP_FILTER +/* +* Print out the current seccomp filter set for the task. +*/ +int proc_pid_seccomp_filter_show (struct seq_file *m,struct pid_namespace *ns, +struct pid *pid,struct task_struct *task) +{ +struct seccomp_state *state; + +rcu_read_lock (); + state =get_seccomp_state (task->seccomp.state); +rcu_read_unlock (); +seccomp_show_filters (state, m); +put_seccomp_state (state); +return 0 ; +} +#endif +/************************************************************************/ /* Here the fs part begins */ /************************************************************************/
@@ -2838,6 +2857,9 @@ static const struct pid_entry tgid_base_stuff[] = {#ifdef CONFIG_HAVE_ARCH_TRACEHOOK INF("syscall", S_IRUGO, proc_pid_syscall),#endif +#ifdef CONFIG_SECCOMP_FILTER +ONE ("seccomp_filter" ,S_IRUSR , proc_pid_seccomp_filter_show), +#endifINF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tgid_stat), ONE("statm", S_IRUGO, proc_pid_statm),
@@ -3180,6 +3202,9 @@ static const struct pid_entry tid_base_stuff[] = {#ifdef CONFIG_HAVE_ARCH_TRACEHOOK INF("syscall", S_IRUGO, proc_pid_syscall),#endif +#ifdef CONFIG_SECCOMP_FILTER +ONE ("seccomp_filter" ,S_IRUSR , proc_pid_seccomp_filter_show), +#endifINF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tid_stat), ONE("statm", S_IRUGO, proc_pid_statm),
Signature · 1 line
1.7.0.4
Re: [PATCH 4/7] seccomp_filter: add process state reporting
Quoted 10 lines
Adds seccomp and
seccomp_filterstatus reporting to proc. /proc/<pid>/status will include a Seccomp field, and /proc/<pid>/seccomp_filter will provide read-only access to the current filter and bitmask set forseccomp_filters.Signed-off-by Will Drewry <[email protected]>--- fs/proc/array.c | 21 +++++++++++++++++++++ fs/proc/base.c | 25 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 0 deletions(-)
I'm not againt seccomp_filter. but I dislike to increase /proc/<pid>/status mess.
1) it's read from a lot of applications, I don't want to worry about performance thing. 2) 99.99% user never use seccomp. this field is useless for them.
Can't you make individual seccomp specific file?
Will Drewry
reviewerRe: [PATCH 4/7] seccomp_filter: add process state reporting
Quoted 11 lines · KOSAKI Motohiro
Quoted 10 lines
Adds seccomp and
seccomp_filterstatus reporting to proc. /proc/<pid>/status will include a Seccomp field, and /proc/<pid>/seccomp_filter will provide read-only access to the current filter and bitmask set forseccomp_filters.Signed-off-by Will Drewry <[email protected]>---
fs/proc/array.c | 21 +++++++++++++++++++++
fs/proc/base.c | 25 +++++++++++++++++++++++++2 files changed, 46 insertions(+), 0 deletions(-)
Quoted 5 lines
I'm not againt
seccomp_filter. but I dislike to increase /proc/<pid>/status mess.
1) it's read from a lot of applications, I don't want to worry about performance thing. 2) 99.99% user never use seccomp. this field is useless for them.Can't you make individual seccomp specific file?
Definitely. Would it make sense to have /proc/<pid>/seccomp and /proc/<pid>/seccomp_filter?
Re: [PATCH 4/7] seccomp_filter: add process state reporting
Quoted 2 lines · Will Drewry
Definitely. Would it make sense to have /proc/<pid>/seccomp and /proc/<pid>/seccomp_filter?
Just one question: WTF bother with S_IRUSR? Note that it's absolutely _useless_ in procfs; any kind of permission checks must be done in read(2) time since doing it in open(2) is worthless. Consider execve() on suid binary; oops, there goes your uid and there goes the effect of checks done by open(2). And if you *do* checks in read(2), why bother duplicating them in open(2)?
Will Drewry
reviewerRe: [PATCH 4/7] seccomp_filter: add process state reporting
Quoted 11 lines · Al Viro
Quoted 2 lines · Will Drewry
Definitely. Would it make sense to have /proc/<pid>/seccomp and /proc/<pid>/seccomp_filter?
Just one question: WTF bother with
S_IRUSR? Note that it's absolutely _useless_ in procfs; any kind of permission checks must be done in read(2) time since doing it in open(2) is worthless. Considerexecve()on suid binary; oops, there goes your uid and there goes the effect of checks done by open(2). And if you *do* checks in read(2), why bother duplicating them in open(2)?
In earlier versions I was allowing filter/bitmask updating via the proc file (which I nixed :). Is S_IRUGO preferred? I don't see any crazy information leakage by sharing the filters/ruleset. I'll fold it into the next version of this patch.
thanks!
will
James Morris
maintainerRe: [PATCH 4/7] seccomp_filter: add process state reporting
In reply to Will DrewryQuoted 4 lines · Will Drewry
Quoted 1 line
Can't you make individual seccomp specific file?
Definitely. Would it make sense to have /proc/<pid>/seccomp and /proc/<pid>/seccomp_filter?
Do you need the separate seccomp file vs. just checking what's in seccomp_filter ?
Signature · 2 lines
James Morris <[email protected]>
Will Drewry
reviewerRe: [PATCH 4/7] seccomp_filter: add process state reporting
Quoted 9 lines · James Morris
Quoted 4 lines · Will Drewry
Quoted 1 line
Can't you make individual seccomp specific file?
Definitely. Would it make sense to have /proc/<pid>/seccomp and /proc/<pid>/seccomp_filter?
Do you need the separate seccomp file vs. just checking what's in
seccomp_filter?
Initially, I would've said yes, because I had modeled it such that any entries in /proc/<pid>/seccomp_filter could be fed right back into as filters. However, as I've reworked it from Frederic and other's feedback, I don't need to keep the separation - all the relevant info can just be in seccomp_filter and no secondary file will be useful.
Thanks for pointing it out! I'm tracking down what is, no doubt, a dumb bug (still), but once I sort it, I'll repost the series with this change included.
Cheers!
will
Will Drewry
reviewerPATCH 4/5 v2 seccomp_filter: add process state reporting
In reply to Will DrewryAdds seccomp_filter status reporting to proc. /proc/<pid>/seccomp_filter will provide read-only access to the current filter set.
v2: removed status entry, added seccomp file.
(requested by [email protected])
allowed S_IRUGO reading of entries
(requested by [email protected])
added flags
got rid of the seccomp_t type
dropped seccomp file
--- fs/proc/base.c | 25 +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0 deletions(-)
fs/proc/base.c +25 -0
@@ -73,6 +73,7 @@#include <linux/security.h> #include <linux/ptrace.h> #include <linux/tracehook.h> +#include <linux/seccomp.h> #include <linux/cgroup.h> #include <linux/cpuset.h> #include <linux/audit.h>
@@ -579,6 +580,24 @@ static int proc_pid_syscall(struct task_struct *task, char *buffer) }#endif /* CONFIG_HAVE_ARCH_TRACEHOOK */ +/* +* Print out the current seccomp filter set for the task. +*/ +#ifdef CONFIG_SECCOMP_FILTER +int proc_pid_seccomp_filter_show (struct seq_file *m,struct pid_namespace *ns, +struct pid *pid,struct task_struct *task) +{ +struct seccomp_state *state; + +rcu_read_lock (); + state =get_seccomp_state (task->seccomp); +rcu_read_unlock (); +seccomp_show_filters (state, m); +put_seccomp_state (state); +return 0 ; +} +#endif /* CONFIG_SECCOMP_FILTER */ +/************************************************************************/ /* Here the fs part begins */ /************************************************************************/
@@ -2838,6 +2857,9 @@ static const struct pid_entry tgid_base_stuff[] = {#ifdef CONFIG_HAVE_ARCH_TRACEHOOK INF("syscall", S_IRUGO, proc_pid_syscall),#endif +#ifdef CONFIG_SECCOMP_FILTER +ONE ("seccomp_filter" ,S_IRUGO , proc_pid_seccomp_filter_show), +#endifINF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tgid_stat), ONE("statm", S_IRUGO, proc_pid_statm),
@@ -3180,6 +3202,9 @@ static const struct pid_entry tid_base_stuff[] = {#ifdef CONFIG_HAVE_ARCH_TRACEHOOK INF("syscall", S_IRUGO, proc_pid_syscall),#endif +#ifdef CONFIG_SECCOMP_FILTER +ONE ("seccomp_filter" ,S_IRUGO , proc_pid_seccomp_filter_show), +#endifINF ("cmdline" ,S_IRUGO , proc_pid_cmdline),ONE ("stat" ,S_IRUGO , proc_tid_stat), ONE("statm", S_IRUGO, proc_pid_statm),
Signature · 1 line
1.7.0.4
Will Drewry
reviewerPATCH 5/7 seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Will DrewryAdds a text file covering what CONFIG_SECCOMP_FILTER is, how it is implemented presently, and what it may be used for. In addition, the limitations and caveats of the proposed implementation are included.
--- Documentation/trace/seccomp_filter.txt | 75 ++++++++++++++++++++++++++++++++ 1 files changed, 75 insertions(+), 0 deletions(-) create mode 100644 Documentation/trace/seccomp_filter.txt
Documentation/trace/seccomp_filter.txt new file +75 -0
@@ -0,0 +1,75 @@ + Seccomp filtering + ================= + +Introduction +------------ + +A large number of system calls are exposed to every userland process +with many of them going unused for the entire lifetime of the +application. As system calls change and mature, bugs are found and +quashed. A certain subset of userland applications benefit by having +a reduce set of available system calls. The reduced set reduces the +total kernel surface exposed to the application. System call filtering +is meant for use with those applications. + +The implementation currently leverages both the existing seccomp +infrastructure and the kernel tracing infrastructure. By centralizing +hooks for attack surface reduction in seccomp, it is possible to assure +attention to security that is less relevant in normal ftrace scenarios, +such as time of check, time of use attacks. However, ftrace provides a +rich, human-friendly environment for specifying system calls by name and +expected arguments. (As such, this requires FTRACE_SYSCALLS.) + + +What it isn't +------------- + +System call filtering isn't a sandbox. It provides a clearly defined +mechanism for minimizing the exposed kernel surface. Beyond that, policy for +logical behavior and information flow should be managed with an LSM of your +choosing. + + +Usage +----- + +An additional seccomp mode is exposed through mode '2'. This mode +depends on CONFIG_SECCOMP_FILTER which in turn depends on +CONFIG_FTRACE_SYSCALLS. + +A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it. + +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1 + sys_write: 1 + sys_mmap: 1 + sys_prctl: 1 + +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0"); + + +Caveats +------- + +The system call names come from ftrace events. At present, many system +calls are not hooked - such as x86's ptregs wrapped system calls. + +In addition compat_task()s will not be supported until a sys32s begin +being hooked.
Signature · 1 line
1.7.0.4
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 16 lines · Will Drewry
Documentation/trace/seccomp_filter.txt new file +16
-0+A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it.
Documentation/trace/seccomp_filter.txt line 65
Quoted 10 lines
Documentation/trace/seccomp_filter.txt new file +10
-0+ +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1 + sys_write: 1 + sys_mmap: 1 + sys_prctl: 1 + +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0");
Ok, color me thoroughly impressed - AFAICS you implemented my suggestions in:
http://lwn.net/Articles/332974/
and you made it work in practice!
We could split out the ftrace filter engine some more and make it more independent of ftrace. It's basically an in-kernel interpreter able to run off tracepoints.
I've Cc:-ed Linus and Andrew: are you guys opposed to such flexible, dynamic filters conceptually? I think we should really think hard about the actual ABI as this could easily spread to more applications than Chrome/Chromium.
Btw., i also think that such an approach is actually the sane(r) design to implement security modules: using such filters is far more flexible than the typical LSM approach of privileged user-space uploading various nasty objects into kernel space and implementing silly (and limited and intrusive) hooks there, like SElinux and the other security modules do.
This approach also has the ability to become recursive (gets inherited by child tasks, which could add their own filters) and unprivileged - unlike LSMs.
I like this *a lot* more than any security sandboxing approach i've seen before.
Thanks,
Ingo
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 22 lines · Ingo Molnar
Quoted 20 lines · Will Drewry
Documentation/trace/seccomp_filter.txt new file +20
-0+A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it. + +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1
Quoted 8 lines
Documentation/trace/seccomp_filter.txt line 65
Quoted 6 lines
Documentation/trace/seccomp_filter.txt new file +6
-0+ sys_write: 1 + sys_mmap: 1 + sys_prctl: 1 + +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0");Ok, color me thoroughly impressed
Me too!
Quoted 3 lines
I've Cc:-ed Linus and Andrew: are you guys opposed to such flexible, dynamic filters conceptually? I think we should really think hard about the actual ABI as this could easily spread to more applications than Chrome/Chromium.
I'll definitely port QEMU to use this new interface rather than my more rigid flexible (haha "rigid flexible") seccomp. I'll see if I run into any issues with this ABI in that porting...
Quoted 5 lines
Btw., i also think that such an approach is actually the sane(r) design to implement security modules: using such filters is far more flexible than the typical LSM approach of privileged user-space uploading various nasty objects into kernel space and implementing silly (and limited and intrusive) hooks there, like SElinux and the other security modules do.
Then you are wrong. There's no question that this interface can provide great extensions to the current discretionary functionality provided by legacy security controls but if you actually want to mediate what tasks can do to other tasks or can do to arbitrary objects on the system this doesn't cut it. Every system call that takes or uses a structure as an argument or that uses copy_from_user (for something other than just unparsed data) is uncontrollable.
This approach is great and with careful coding of userspace apps can be made very useful in constraining those apps, but a replacement for mandatory access control it is not.
Quoted 2 lines
This approach also has the ability to become recursive (gets inherited by child tasks, which could add their own filters) and unprivileged - unlike LSMs.
LSMs have that ability. There's nothing to prevent a module loading service to allow unpriv applications to further constrain themselves. It's just the different between DAC and MAC. You are clearly a DAC guy, and there is no question this change is great in that mindset, but you don't seem to understand either the flexibility of the LSM or the purpose of some of the modules implemented on top of the LSM.
Quoted 2 lines
I like this *a lot* more than any security sandboxing approach i've seen before.
I like this *a lot*. It will be a HUGE addition to the security sandboxing approaches I've seen before.
-Eric
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 32 lines · Eric Paris
Quoted 22 lines · Ingo Molnar
Quoted 20 lines · Will Drewry
Documentation/trace/seccomp_filter.txt new file +20
-0+A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it. + +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1Quoted 8 lines
Documentation/trace/seccomp_filter.txt line 65
Quoted 6 lines
+ sys_write: 1
+ sys_mmap: 1
+ sys_prctl: 1Documentation/trace/seccomp_filter.txt new file +3
-0+ +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0");Ok, color me thoroughly impressed
Quoted 5 lines
Me too!
Quoted 3 lines
I've Cc:-ed Linus and Andrew: are you guys opposed to such flexible, dynamic filters conceptually? I think we should really think hard about the actual ABI as this could easily spread to more applications than Chrome/Chromium.
Would it make sense to start, as Frederic has pointed out, by using the existing ABI - system call numbers - and not system call names? We could leave name resolution to userspace as it is for all other system call consumers now. It might leave the interface for this support looking more like:
prctl(PR_SET_SECCOMP, 2, _NR_mmap, "fd == 1"); prctl(PR_SET_SECCOMP_FILTER_APPLY, now|on_exec);
which may be less of a dramatic ABI change to start with.
Quoted 3 lines
I'll definitely port QEMU to use this new interface rather than my more rigid flexible (haha "rigid flexible") seccomp. I'll see if I run into any issues with this ABI in that porting...
Great - also let me know if you have a preference on the interface Frederic proposed, as it might reduce the parsing footprint and overall kernel-side complexity but add a little bit more burden on the userspace side.
Quoted 6 lines
Quoted 5 lines
Btw., i also think that such an approach is actually the sane(r) design to implement security modules: using such filters is far more flexible than the typical LSM approach of privileged user-space uploading various nasty objects into kernel space and implementing silly (and limited and intrusive) hooks there, like SElinux and the other security modules do.
Quoted 7 lines
Then you are wrong. There's no question that this interface can provide great extensions to the current discretionary functionality provided by legacy security controls but if you actually want to mediate what tasks can do to other tasks or can do to arbitrary objects on the system this doesn't cut it. Every system call that takes or uses a structure as an argument or that uses
copy_from_user(for something other than just unparsed data) is uncontrollable.
I think it'd take a fair amount of additional work to turn pure system call filtering into a robust policy engine (like systrace). It seems to me that right now, it would just be a great addition to the existing LSM model by providing infrastructure the LSMs can use with their higher level logic. (Plumbing in all the bits to understand the system call arguments completely and avoid time-of-check-time-of-use attacks would be a sizable undertaking - and that's without making sure the existing LSMs could live happily on top of it .)
Quoted 14 lines
This approach is great and with careful coding of userspace apps can be made very useful in constraining those apps, but a replacement for mandatory access control it is not.
Quoted 2 lines
This approach also has the ability to become recursive (gets inherited by child tasks, which could add their own filters) and unprivileged - unlike LSMs.
LSMs have that ability. There's nothing to prevent a module loading service to allow unpriv applications to further constrain themselves. It's just the different between DAC and MAC. You are clearly a DAC guy, and there is no question this change is great in that mindset, but you don't seem to understand either the flexibility of the LSM or the purpose of some of the modules implemented on top of the LSM.
Quoted 5 lines
Quoted 2 lines
I like this *a lot* more than any security sandboxing approach i've seen before.
I like this *a lot*. It will be a HUGE addition to the security sandboxing approaches I've seen before.
Thanks!
will
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 39 lines · Will Drewry
Quoted 32 lines · Eric Paris
Quoted 22 lines · Ingo Molnar
Quoted 20 lines · Will Drewry
Documentation/trace/seccomp_filter.txt new file +20
-0+A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it. + +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1Quoted 8 lines
Documentation/trace/seccomp_filter.txt line 65
Quoted 6 lines
+ sys_write: 1
+ sys_mmap: 1
+ sys_prctl: 1Documentation/trace/seccomp_filter.txt new file +3
-0+ +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0");Ok, color me thoroughly impressed
Quoted 5 lines
Me too!
Quoted 3 lines
I've Cc:-ed Linus and Andrew: are you guys opposed to such flexible, dynamic filters conceptually? I think we should really think hard about the actual ABI as this could easily spread to more applications than Chrome/Chromium.
Quoted 7 lines
Would it make sense to start, as Frederic has pointed out, by using the existing ABI - system call numbers - and not system call names? We could leave name resolution to userspace as it is for all other system call consumers now. It might leave the interface for this support looking more like:
prctl(PR_SET_SECCOMP, 2, _NR_mmap, "fd == 1");prctl(PR_SET_SECCOMP_FILTER_APPLY, now|on_exec);
PR_SET_SECCOMP_FILTER_APPLY seems only useful if you think there are other cases than enable_on_exec that would be useful for these filters.
We can think about a default enable on exec behaviour as Steve pointed out.
But I have no idea if other cases may be desirable to apply these filters.
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 52 lines · Frederic Weisbecker
Quoted 39 lines · Will Drewry
Quoted 32 lines · Eric Paris
Quoted 22 lines · Ingo Molnar
Quoted 20 lines · Will Drewry
Documentation/trace/seccomp_filter.txt new file +20
-0+A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it. + +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1Quoted 8 lines
Documentation/trace/seccomp_filter.txt line 65
Quoted 6 lines
+ sys_write: 1
+ sys_mmap: 1
+ sys_prctl: 1Documentation/trace/seccomp_filter.txt new file +3
-0+ +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0");Ok, color me thoroughly impressed
Quoted 5 lines
Me too!
Quoted 3 lines
I've Cc:-ed Linus and Andrew: are you guys opposed to such flexible, dynamic filters conceptually? I think we should really think hard about the actual ABI as this could easily spread to more applications than Chrome/Chromium.
Quoted 7 lines
Would it make sense to start, as Frederic has pointed out, by using the existing ABI - system call numbers - and not system call names? We could leave name resolution to userspace as it is for all other system call consumers now. It might leave the interface for this support looking more like:
prctl(PR_SET_SECCOMP, 2, _NR_mmap, "fd == 1");prctl(PR_SET_SECCOMP_FILTER_APPLY, now|on_exec);
PR_SET_SECCOMP_FILTER_APPLYseems only useful if you think there are other cases thanenable_on_execthat would be useful for these filters.
Quoted 5 lines
We can think about a default enable on exec behaviour as Steve pointed out.
But I have no idea if other cases may be desirable to apply these filters.
I nearly have all of the changes in, but I'm still updating my tests. In general, I think having both on_exec and now is reasonable is because you can write a much tighter filter set if it is embedded in the application. E.g., it may load all its shared libraries, which you allow, then lock itself down before touching untrusted content. That said, if the default behavior is enable_on_exec, then you'd only call PR_SET_SECCOMP_FILTER_APPLY when you want to apply _now_. I like that.
That said, I have a general interface question :) Right now I have:prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_ADD, syscall_nr, filter_string); prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, syscall_nr, filter_string_or_NULL);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_APPLY, apply_flags);
(I will change this to default to apply_on_exec and let FILTER_APPLY
make it apply _now_ exclusively. :)
This can easily be mapped to:
prctl(PR_SET_SECCOMP
PR_SET_SECOMP_FILTER_ADD PR_SET_SECOMP_FILTER_DROP PR_SET_SECOMP_FILTER_APPLY
if that'd be preferable (to keep it all in the prctl.h world).
Following along the suggestion of reducing custom parsing, it seemed to make a lot of sense to make add and drop actions very explicit. There is no guesswork so a system call filtered process will only be able to perform DROP operations (if prctl is allowed) to reduce the allowed system calls. This also allows more fine grained flexibility in addition to the in-kernel complexity reduction. E.g., Process starts with
__NR_read, "fd == 1"
__NR_read, "fd == 2"
later it can call:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, __NR_read, "fd == 2");
to drop one of the filters without disabling "fd == 1" reading. (Or it could pass in NULL to drop all filters).
FWIW, I also am updating the Kconfig to be dependent EXPERIMENTAL, as it might make sense to get some use prior to considering it finalized :) I'm not sure if that is appropriate though.
Thanks! I'll try to post the v2s today once they're working properly :) will
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 8 lines · Will Drewry
Quoted 7 lines · Frederic Weisbecker
PR_SET_SECCOMP_FILTER_APPLYseems only useful if you think there are other cases thanenable_on_execthat would be useful for these filters.We can think about a default enable on exec behaviour as Steve pointed out.
Quoted 8 lines
Quoted 2 lines
But I have no idea if other cases may be desirable to apply these filters.
I nearly have all of the changes in, but I'm still updating my tests. In general, I think having both
on_execand now is reasonable is because you can write a much tighter filter set if it is embedded in the application. E.g., it may load all its shared libraries, which you allow, then lock itself down before touching untrusted content.
Well, that makes sense.
Quoted 3 lines
That said, if the default behavior is
enable_on_exec, then you'd only callPR_SET_SECCOMP_FILTER_APPLYwhen you want to apply _now_. I like that.
It could be the default behaviour, which could be overriden with PR_SET_SECCOMP_FILTER_APPLY. However I'm wondering about that enable_on_exec.
Say you want to accept only stdin/stdout read/write, and you blocked mmap, open, etc... How can ld load the app and mmap all its shared libraries? The filters are going to be applied once the interpreter is launched. This makes me wonder now about the general usability of this and also about the relevance in a default enable on exec behaviour here.
Quoted 16 lines
That said, I have a general interface question :) Right now I have:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_ADD, syscall_nr, filter_string);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, syscall_nr, filter_string_or_NULL);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_APPLY, apply_flags);
(I will change this to default toapply_on_execand letFILTER_APPLY
make it apply _now_ exclusively. :)This can easily be mapped to:
prctl(PR_SET_SECCOMP
PR_SET_SECOMP_FILTER_ADDPR_SET_SECOMP_FILTER_DROPPR_SET_SECOMP_FILTER_APPLY
if that'd be preferable (to keep it all in theprctl.hworld).
Quoted 13 lines
Following along the suggestion of reducing custom parsing, it seemed to make a lot of sense to make add and drop actions very explicit. There is no guesswork so a system call filtered process will only be able to perform DROP operations (if prctl is allowed) to reduce the allowed system calls. This also allows more fine grained flexibility in addition to the in-kernel complexity reduction. E.g., Process starts with
__NR_read, "fd == 1"
__NR_read, "fd == 2"
later it can call:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, __NR_read, "fd == 2");
to drop one of the filters without disabling "fd == 1" reading. (Or it could pass in NULL to drop all filters).
Hm, but then you don't let the childs be able to restrict further what you allowed before.
Say I have foo(int a, int b), and I apply these filters:
__NR_foo, "a == 1"; __NR_foo, "a == 2";
This is basically "a == 1 || a == 2".
Now I apply the filters and I fork. How can the child (or current task after the filter is applied) restrict further by only allowing "b == 2", such that with the inherited parent filters we have:
"(a == 1 || a == 2) && b == 2"
So what you propose seems to me too limited. I'd rather have this:
SECCOMP_FILTER_SET = remove previous filter entirely and set a new one SECCOMP_FILTER_GET = get the string of the current filter
The rule would be that you can only set a filter that is intersected with the one that was previously applied.
It means that if you set filter A and you apply it. If you want to set filter B thereafter, it must be:
A && B
OTOH, as long as you haven't applied A, you can override it as you wish. Like you can have "A || B" instead. Or you can remove it with "1". Of course if a previous filter was applied before A, then your new filter must be
concatenated: "previous && (A || B)".
Right? And note in this scheme you can reproduce your DROP trick. If "A || B" is the current filter applied, then you can restrict B by
doing: "(A || B) && A".
So the role of SECCOMP_FILTER_GET is to get the string that matches the current applied filter.
The effect of this is infinite of course. If you apply A, then apply B then you need A && B. If later you want to apply C, then you need A && B && C, etc...
Does that look sane?
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
2011/5/3 Frederic Weisbecker <[email protected]>:
Quoted 69 lines · Will Drewry
Quoted 15 lines
That said, I have a general interface question :) Right now I have:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_ADD, syscall_nr, filter_string);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, syscall_nr, filter_string_or_NULL);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_APPLY, apply_flags); (I will change this to default toapply_on_execand letFILTER_APPLYmake it apply _now_ exclusively. :)This can easily be mapped to:
prctl(PR_SET_SECCOMP
PR_SET_SECOMP_FILTER_ADDPR_SET_SECOMP_FILTER_DROPPR_SET_SECOMP_FILTER_APPLY
if that'd be preferable (to keep it all in theprctl.hworld).Quoted 13 lines
Following along the suggestion of reducing custom parsing, it seemed to make a lot of sense to make add and drop actions very explicit. There is no guesswork so a system call filtered process will only be able to perform DROP operations (if prctl is allowed) to reduce the allowed system calls. This also allows more fine grained flexibility in addition to the in-kernel complexity reduction. E.g., Process starts with
__NR_read, "fd == 1"
__NR_read, "fd == 2"
later it can call:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, __NR_read, "fd == 2"); to drop one of the filters without disabling "fd == 1" reading. (Or it could pass in NULL to drop all filters).Hm, but then you don't let the childs be able to restrict further what you allowed before.
Say I have
foo(int a, int b), and I apply these filters:__NR_foo, "a == 1";
__NR_foo, "a == 2";This is basically "a == 1 || a == 2".
Now I apply the filters and I fork. How can the child (or current task after the filter is applied) restrict further by only allowing "b == 2", such that with the inherited parent filters we have:
"(a == 1 || a == 2) && b == 2"
So what you propose seems to me too limited. I'd rather have this:
SECCOMP_FILTER_SET= remove previous filter entirely and set a new oneSECCOMP_FILTER_GET= get the string of the current filterThe rule would be that you can only set a filter that is intersected with the one that was previously applied.
It means that if you set filter A and you apply it. If you want to set filter B thereafter, it must be:
A && B
OTOH, as long as you haven't applied A, you can override it as you wish. Like you can have "A || B" instead. Or you can remove it with "1". Of course if a previous filter was applied before A, then your new filter must be
concatenated: "previous && (A || B)".Right? And note in this scheme you can reproduce your DROP trick. If "A || B" is the current filter applied, then you can restrict B by
doing: "(A || B) && A".
Quoted 9 lines
So the role of
SECCOMP_FILTER_GETis to get the string that matches the current applied filter.The effect of this is infinite of course. If you apply A, then apply B then you need A && B. If later you want to apply C, then you need A && B && C, etc...
Does that look sane?
Even better: applying a filter would always automatically be an intersection of the previous one.
If you do:
SECCOMP_FILTER_SET, __NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET, __NR_foo, "b == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET, __NR_foo, "c == 3"SECCOMP_FILTER_APPLY
The end result is:
"(a == 1 || a == 2) && b == 2 && c == 3"
So that we don't push the burden in the kernel to compare the applied expression with a new one that may or may not be embraced by parenthesis and other trickies like that. We simply append to the working one.
Ah and OTOH this:
SECCOMP_FILTER_SET, __NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET, __NR_foo, "b == 2"SECCOMP_FILTER_SET, __NR_foo, "c == 3"
has the following end result:
"(a == 1 || a == 2) && c == 3"
As long as you don't apply the filter, the temporary part is overriden, but still we keep
the applied part.
Still sane? (or completely nuts?)
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 109 lines · Frederic Weisbecker
2011/5/3 Frederic Weisbecker <[email protected]>:
Quoted 69 lines · Will Drewry
Quoted 15 lines
That said, I have a general interface question :) Right now I have:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_ADD, syscall_nr, filter_string);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, syscall_nr, filter_string_or_NULL);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_APPLY, apply_flags); (I will change this to default toapply_on_execand letFILTER_APPLYmake it apply _now_ exclusively. :)This can easily be mapped to:
prctl(PR_SET_SECCOMP
PR_SET_SECOMP_FILTER_ADDPR_SET_SECOMP_FILTER_DROPPR_SET_SECOMP_FILTER_APPLY
if that'd be preferable (to keep it all in theprctl.hworld).Quoted 13 lines
Following along the suggestion of reducing custom parsing, it seemed to make a lot of sense to make add and drop actions very explicit. There is no guesswork so a system call filtered process will only be able to perform DROP operations (if prctl is allowed) to reduce the allowed system calls. This also allows more fine grained flexibility in addition to the in-kernel complexity reduction. E.g., Process starts with
__NR_read, "fd == 1"
__NR_read, "fd == 2"
later it can call:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, __NR_read, "fd == 2"); to drop one of the filters without disabling "fd == 1" reading. (Or it could pass in NULL to drop all filters).Hm, but then you don't let the childs be able to restrict further what you allowed before.
Say I have
foo(int a, int b), and I apply these filters:__NR_foo, "a == 1";
__NR_foo, "a == 2";This is basically "a == 1 || a == 2".
Now I apply the filters and I fork. How can the child (or current task after the filter is applied) restrict further by only allowing "b == 2", such that with the inherited parent filters we have:
"(a == 1 || a == 2) && b == 2"
So what you propose seems to me too limited. I'd rather have this:
SECCOMP_FILTER_SET= remove previous filter entirely and set a new oneSECCOMP_FILTER_GET= get the string of the current filterThe rule would be that you can only set a filter that is intersected with the one that was previously applied.
It means that if you set filter A and you apply it. If you want to set filter B thereafter, it must be:
A && B
OTOH, as long as you haven't applied A, you can override it as you wish. Like you can have "A || B" instead. Or you can remove it with "1". Of course if a previous filter was applied before A, then your new filter must be
concatenated: "previous && (A || B)".Right? And note in this scheme you can reproduce your DROP trick. If "A || B" is the current filter applied, then you can restrict B by
doing: "(A || B) && A".Quoted 9 lines
So the role of
SECCOMP_FILTER_GETis to get the string that matches the current applied filter.The effect of this is infinite of course. If you apply A, then apply B then you need A && B. If later you want to apply C, then you need A && B && C, etc...
Does that look sane?
Even better: applying a filter would always automatically be an intersection of the previous one.
If you do:
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "c == 3"SECCOMP_FILTER_APPLYThe end result is:
"(a == 1 || a == 2) && b == 2 && c == 3"
So that we don't push the burden in the kernel to compare the applied expression with a new one that may or may not be embraced by parenthesis and other trickies like that. We simply append to the working one.
Ah and OTOH this:
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"has the following end result:
Quoted 7 lines
"(a == 1 || a == 2) && c == 3"
As long as you don't apply the filter, the temporary part is overriden, but still we keep
the applied part.Still sane? (or completely nuts?)
Okay - so I *think* I'm following. I really like the use of SET and GET to allow for further constraint based on additional argument restrictions instead of purely reducing the filters available. The only part I'm stumbling on is using APPLY on a per-filter basis. In my current implementation, I consider APPLY to be the global enable bit. Whatever filters are set become set in stone and only &&s are handled. I'm not sure I understand why it would make sense to do a per-syscall-filter apply call. It's certainly doable, but it will mean that we may be logically storing something like:
__NR_foo: (a == 1 || a == 2), applied__NR_foo: b == 2, not applied__NR_foo: c == 3, not applied
after
SECCOMP_FILTER_SET, __NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET, __NR_foo, "b == 2"SECCOMP_FILTER_SET, __NR_foo, "c == 3"
In that case, would a call to sys_foo even be tested against the non-applied constraints of b==2 or c==3? Or would the call to set "c == 3" replace the "b == 2" entry. I'm not sure I see that the benefit exceeds the ambiguity that might introduce. However, if the default behavior it to always extend with &&, then a consumer of the interface could just do:
SECCOMP_FILTER_SET, __NR_prctl, "option == 2"SECCOMP_FILTER_SET, __NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_SET, __NR_foo, "b == 2"SECCOMP_FILTER_APPLY
This would yield a final filter for foo of "(a == 1 || a == 2) && b == 2". The call to APPLY would initiate the enforcement of the syscall filtering and enforce that no new filters may be added for syscalls that aren't already constrained. So you could still call
SECCOMP_FILTER_SET, __NR_foo, "0"
which is logically "((a == 1 || a == 2) && b == 2) && 0" and would be interpreted as just a DROP. But you could not do,
SECCOMP_FILTER_SET, __NR_foo, "0"SECCOMP_FILTER_SET, __NR_foo, "1"
orSECCOMP_FILTER_SET, __NR_read, "1"
(since the implicit filter for all syscalls after an APPLY call should be "0" and additions would just be "0 && whatever").
Am I missing something? If that makes sense, then we may even be able to reduce the extra directives by one and get a resulting interface that looks something like:
/* Appends (&&) a new filter string for a syscall to the current filter value. "0" clears the filter. */ prctl(PR_SET_SECCOMP_FILTER, syscall_nr, filter_string); /* Returns the current explicit filter string for a syscall */ prctl(PR_GET_SECCOMP_FILTER, syscall_nr, buf, buflen); /* Transition to a secure computing mode:
* 1 - enables traditional seccomp behavior
* 2 - enables seccomp filter enforcement and changes the implicit
filter for syscalls from "1" to "0"
*/prctl(PR_SET_SECCOMP, mode);
I'll set aside the v2 of the patch that uses ADD, DROP, and APPLY and work up a version with this interface. Do you (or anyone else :) feel strongly about per-syscall APPLY? I like the above version, but I'm certainly willing to explore the other path. As is, I'll go through my usecases (and tests once I have a new cut of the patch) and see how it feels. At first blush, this appears more succinct _and_ more expressive than the prior versions!
~~
As to the use of apply_on_exec, even if you whitelist: mmap, fstat64, brk, uname, open, read, close, set_thread_area, mprotect, munmap, and access _just_ to allow a process to be exec'd, it is still a significant reduction in kernel attack surface. Pairing that with a LSM, delayed chroot, etc could fill in the gaps with respect to a greater sandboxing solution. I'd certainly take that tradeoff for running binaries that I don't control the source for :) That said, LD_PRELOAD, ptrace injection, and other tricks could allow for the injection of a very targeted filterset, but I don't think that invalidates the on-exec case given the brittleness relying exclusively on such tactics. Does that seem reasonable?
Thanks!
will
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 195 lines · Will Drewry
Quoted 109 lines · Frederic Weisbecker
2011/5/3 Frederic Weisbecker <[email protected]>:
Quoted 69 lines · Will Drewry
Quoted 15 lines
That said, I have a general interface question :) Right now I have:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_ADD, syscall_nr, filter_string);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, syscall_nr, filter_string_or_NULL);prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_APPLY, apply_flags); (I will change this to default toapply_on_execand letFILTER_APPLYmake it apply _now_ exclusively. :)This can easily be mapped to:
prctl(PR_SET_SECCOMP
PR_SET_SECOMP_FILTER_ADDPR_SET_SECOMP_FILTER_DROPPR_SET_SECOMP_FILTER_APPLY
if that'd be preferable (to keep it all in theprctl.hworld).Quoted 13 lines
Following along the suggestion of reducing custom parsing, it seemed to make a lot of sense to make add and drop actions very explicit. There is no guesswork so a system call filtered process will only be able to perform DROP operations (if prctl is allowed) to reduce the allowed system calls. This also allows more fine grained flexibility in addition to the in-kernel complexity reduction. E.g., Process starts with
__NR_read, "fd == 1"
__NR_read, "fd == 2"
later it can call:
prctl(PR_SET_SECCOMP, 2, SECCOMP_FILTER_DROP, __NR_read, "fd == 2"); to drop one of the filters without disabling "fd == 1" reading. (Or it could pass in NULL to drop all filters).Hm, but then you don't let the childs be able to restrict further what you allowed before.
Say I have
foo(int a, int b), and I apply these filters:__NR_foo, "a == 1";
__NR_foo, "a == 2";This is basically "a == 1 || a == 2".
Now I apply the filters and I fork. How can the child (or current task after the filter is applied) restrict further by only allowing "b == 2", such that with the inherited parent filters we have:
"(a == 1 || a == 2) && b == 2"
So what you propose seems to me too limited. I'd rather have this:
SECCOMP_FILTER_SET= remove previous filter entirely and set a new oneSECCOMP_FILTER_GET= get the string of the current filterThe rule would be that you can only set a filter that is intersected with the one that was previously applied.
It means that if you set filter A and you apply it. If you want to set filter B thereafter, it must be:
A && B
OTOH, as long as you haven't applied A, you can override it as you wish. Like you can have "A || B" instead. Or you can remove it with "1". Of course if a previous filter was applied before A, then your new filter must be
concatenated: "previous && (A || B)".Right? And note in this scheme you can reproduce your DROP trick. If "A || B" is the current filter applied, then you can restrict B by
doing: "(A || B) && A".Quoted 9 lines
So the role of
SECCOMP_FILTER_GETis to get the string that matches the current applied filter.The effect of this is infinite of course. If you apply A, then apply B then you need A && B. If later you want to apply C, then you need A && B && C, etc...
Does that look sane?
Even better: applying a filter would always automatically be an intersection of the previous one.
If you do:
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "c == 3"SECCOMP_FILTER_APPLYThe end result is:
"(a == 1 || a == 2) && b == 2 && c == 3"
So that we don't push the burden in the kernel to compare the applied expression with a new one that may or may not be embraced by parenthesis and other trickies like that. We simply append to the working one.
Ah and OTOH this:
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"has the following end result:
Quoted 7 lines
"(a == 1 || a == 2) && c == 3"
As long as you don't apply the filter, the temporary part is overriden, but still we keep
the applied part.Still sane? (or completely nuts?)
Okay - so I *think* I'm following. I really like the use of SET and GET to allow for further constraint based on additional argument restrictions instead of purely reducing the filters available. The only part I'm stumbling on is using APPLY on a per-filter basis. In my current implementation, I consider APPLY to be the global enable bit. Whatever filters are set become set in stone and only &&s are handled. I'm not sure I understand why it would make sense to do a per-syscall-filter apply call. It's certainly doable, but it will mean that we may be logically storing something like:
__NR_foo: (a == 1 || a == 2), applied__NR_foo: b == 2, not applied__NR_foo: c == 3, not appliedafter
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"In that case, would a call to
sys_fooeven be tested against the non-applied constraints of b==2 or c==3? Or would the call to set "c == 3" replace the "b == 2" entry. I'm not sure I see that the benefit exceeds the ambiguity that might introduce. However, if the default behavior it to always extend with &&, then a consumer of the interface could just do:
SECCOMP_FILTER_SET,__NR_prctl, "option == 2"SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_APPLYThis would yield a final filter for foo of "(a == 1 || a == 2) && b == 2". The call to APPLY would initiate the enforcement of the syscall filtering and enforce that no new filters may be added for syscalls that aren't already constrained. So you could still call
SECCOMP_FILTER_SET,__NR_foo, "0"which is logically "((a == 1 || a == 2) && b == 2) && 0" and would be interpreted as just a DROP. But you could not do,
SECCOMP_FILTER_SET,__NR_foo, "0"SECCOMP_FILTER_SET,__NR_foo, "1"
orSECCOMP_FILTER_SET,__NR_read, "1"(since the implicit filter for all syscalls after an APPLY call should be "0" and additions would just be "0 && whatever").
Am I missing something? If that makes sense, then we may even be able to reduce the extra directives by one and get a resulting interface that looks something like:
/* Appends (&&) a new filter string for a syscall to the current filter value. "0" clears the filter. */
prctl(PR_SET_SECCOMP_FILTER, syscall_nr, filter_string); /* Returns the current explicit filter string for a syscall */prctl(PR_GET_SECCOMP_FILTER, syscall_nr, buf, buflen); /* Transition to a secure computing mode:
* 1 - enables traditional seccomp behavior
* 2 - enables seccomp filter enforcement and changes the implicit filter for syscalls from "1" to "0"
*/prctl(PR_SET_SECCOMP, mode);I'll set aside the v2 of the patch that uses ADD, DROP, and APPLY and work up a version with this interface. Do you (or anyone else :) feel strongly about per-syscall APPLY? I like the above version, but I'm certainly willing to explore the other path. As is, I'll go through my usecases (and tests once I have a new cut of the patch) and see how it feels. At first blush, this appears more succinct _and_ more expressive than the prior versions!
~~
Quoted 11 lines
As to the use of
apply_on_exec, even if you whitelist: mmap, fstat64, brk, uname, open, read, close,set_thread_area, mprotect, munmap, and access _just_ to allow a process to be exec'd, it is still a significant reduction in kernel attack surface. Pairing that with a LSM, delayed chroot, etc could fill in the gaps with respect to a greater sandboxing solution. I'd certainly take that tradeoff for running binaries that I don't control the source for :) That said,LD_PRELOAD, ptrace injection, and other tricks could allow for the injection of a very targeted filterset, but I don't think that invalidates the on-exec case given the brittleness relying exclusively on such tactics. Does that seem reasonable?
With a little more thinking :), I don't think there's an obvious hook for "on-exec" bit in the interface proposed above. I think that might
be a worthwhile tradeoff given how much cleaner it is. It'd still be
possible to write a launcher to provide a reduced kernel surface, it'd just also need a filter for the exec syscall.
cheers!
will
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Will DrewryQuoted 8 lines · Will Drewry
Okay - so I *think* I'm following. I really like the use of SET and GET to allow for further constraint based on additional argument restrictions instead of purely reducing the filters available. The only part I'm stumbling on is using APPLY on a per-filter basis. In my current implementation, I consider APPLY to be the global enable bit. Whatever filters are set become set in stone and only &&s are handled. I'm not sure I understand why it would make sense to do a per-syscall-filter apply call.
Nope it's still a global apply..
Quoted 7 lines
It's certainly doable, but it will
mean that we may be logically storing something like:
__NR_foo: (a == 1 || a == 2), applied__NR_foo: b == 2, not applied__NR_foo: c == 3, not applied
Quoted 6 lines
after
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"
No, the c == 3 would override b == 2.
Quoted 2 lines
In that case, would a call to
sys_fooeven be tested against the non-applied constraints of b==2 or c==3?
No, not as long as it's not applied.
Quoted 3 lines
Or would the call to set "c
== 3" replace the "b == 2" entry. I'm not sure I see that the benefit exceeds the ambiguity that might introduce.
The rationale behind it is that as long as you haven't applied your filter, you should be able to override it.
And the simplest way to override it is to do it completely. Remove what was there before (that wasn't applied).
You could opt for a FILTER_DROP.
Let's take that example:
SECCOMP_FILTER_SET, __NR_foo, "b == 2"SECCOMP_FILTER_SET, __NR_foo, "c == 3"
Following your logic, we should have "b == 2 && c == 3" after that. How are you going to remove only the c == 3 sequence?
You would need SECCOMP_FILTER_DROP, __NR_foo, "c == 3"
That said, using a string with a filter expression as an id looks a bit odd to me. That doesn't work anymore with "((c == 3))", or "c== 3", unless you compare against the resulting tree but that's complicated.
I mean, that works, most of the time you keep your expression and pass it as is. But the expression should be identified by its meaning, not by some random language layout.
That also forces you to scatter your filter representation:
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3"
For me this shouldn't be different than
SECCOMP_FILTER_SET, __NR_foo, "b == 2 && c == 3"
Still nobody will be able to remove a part of the above expression.
So, I'm more for having something that removes everything not applied than just a part of the non-applied filter.
Two possibilities I see:
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3" // And result is "c == 3"
Or:
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3" // And result is "b == 2 && c == 3" SECCOMP_FILTER_RESET, __NR_foo //rewind to filter "0" SECCOMP_FILTER_SET, __NR_foo, "c == 4" // And result is "c == 4"
How does that look?
Quoted 16 lines
However, if the default
behavior it to always extend with &&, then a consumer of the interface could just do:
SECCOMP_FILTER_SET,__NR_prctl, "option == 2"SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_APPLYThis would yield a final filter for foo of "(a == 1 || a == 2) && b == 2". The call to APPLY would initiate the enforcement of the syscall filtering and enforce that no new filters may be added for syscalls that aren't already constrained. So you could still call
SECCOMP_FILTER_SET,__NR_foo, "0"
Quoted 7 lines
which is logically "((a == 1 || a == 2) && b == 2) && 0" and would be interpreted as just a DROP. But you could not do,
SECCOMP_FILTER_SET,__NR_foo, "0"SECCOMP_FILTER_SET,__NR_foo, "1"
orSECCOMP_FILTER_SET,__NR_read, "1"
Why?
"((a == 1 || a == 2) && b == 2) && 0 && 1"
or
"((a == 1 || a == 2) && b == 2) && 1"
does still follow our previous constraints.
Quoted 3 lines
(since the implicit filter for all syscalls after an APPLY call should be "0" and additions would just be "0 && whatever").
If the default filter is 0 after apply, setting whatever afterward is not an issue. You'll have "0 && whatever" which still means 0, that's fine.
Quoted 10 lines
Am I missing something? If that makes sense, then we may even be able to reduce the extra directives by one and get a resulting interface that looks something like:
/* Appends (&&) a new filter string for a syscall to the current filter value. "0" clears the filter. */
prctl(PR_SET_SECCOMP_FILTER, syscall_nr, filter_string); /* Returns the current explicit filter string for a syscall */prctl(PR_GET_SECCOMP_FILTER, syscall_nr, buf, buflen);
So GET is eventually optional here, as a new filter automatically append to the previous applied one.
But if a process needs some information about current filter, this makes sense.
Quoted 10 lines
/* Transition to a secure computing mode:
* 1 - enables traditional seccomp behavior
* 2 - enables seccomp filter enforcement and changes the implicit
filter for syscalls from "1" to "0"
*/prctl(PR_SET_SECCOMP, mode);I'll set aside the v2 of the patch that uses ADD, DROP, and APPLY and work up a version with this interface. Do you (or anyone else :) feel strongly about per-syscall APPLY?
I'm still not sure what you meant by per syscall APPLY :)
Quoted 13 lines
~~
As to the use of
apply_on_exec, even if you whitelist: mmap, fstat64, brk, uname, open, read, close,set_thread_area, mprotect, munmap, and access _just_ to allow a process to be exec'd, it is still a significant reduction in kernel attack surface. Pairing that with a LSM, delayed chroot, etc could fill in the gaps with respect to a greater sandboxing solution. I'd certainly take that tradeoff for running binaries that I don't control the source for :) That said,LD_PRELOAD, ptrace injection, and other tricks could allow for the injection of a very targeted filterset, but I don't think that invalidates the on-exec case given the brittleness relying exclusively on such tactics. Does that seem reasonable?
Right. But then, does a default enable_on_exec behaviour really matches your needs? Given your description, it seems that applying it on the next fork would work too, as a random example.
Steven Rostedt
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 7 lines · Frederic Weisbecker
Quoted 7 lines
It's certainly doable, but it will
mean that we may be logically storing something like:
__NR_foo: (a == 1 || a == 2), applied__NR_foo: b == 2, not applied__NR_foo: c == 3, not applied
Quoted 8 lines
Quoted 6 lines
after
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"No, the c == 3 would override b == 2.
I honestly hate the "override" mode. I like that SETs are or'd among each other and an APPLY is a "commit"; meaning that you can only limit it further, but can not extend it (an explicit &&)
Quoted 4 lines
Quoted 3 lines
In that case, would a call to
sys_fooeven be tested against the non-applied constraints of b==2 or c==3?
Quoted 8 lines
No, not as long as it's not applied.
Quoted 3 lines
Or would the call to set "c
== 3" replace the "b == 2" entry. I'm not sure I see that the benefit exceeds the ambiguity that might introduce.The rationale behind it is that as long as you haven't applied your filter, you should be able to override it.
We need a "UNSET" (I like that better than DROP).
Quoted 10 lines
And the simplest way to override it is to do it completely. Remove what was there before (that wasn't applied).
You could opt for a
FILTER_DROP.
Let's take that example:
SECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"
Quoted 8 lines
Following your logic, we should have "b == 2 && c == 3" after that. How are you going to remove only the c == 3 sequence?
You would need
SECCOMP_FILTER_DROP,__NR_foo, "c == 3"That said, using a string with a filter expression as an id looks a bit odd to me. That doesn't work anymore with "((c == 3))", or "c== 3", unless you compare against the resulting tree but that's complicated.
Nah, it should be easy. Actually, in "set" mode (before the apply) we should keep a link list of "trees" of sets. Then we just find the "tree" that matches the UNSET and remove it.
Quoted 7 lines
I mean, that works, most of the time you keep your expression and pass it as is. But the expression should be identified by its meaning, not by some random language layout.
That also forces you to scatter your filter representation:
Quoted 8 lines
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3"For me this shouldn't be different than
SECCOMP_FILTER_SET, __NR_foo, "b == 2 && c == 3"Still nobody will be able to remove a part of the above expression.
Correct, as the sets will be just link lists of sets. Once we apply it, it goes into the main tree.
Thus, to find the UNSET set, we would evaluate the tree of that unset, and search for it. If it is found, remove it, otherwise return EINVAL or something.
Quoted 24 lines
So, I'm more for having something that removes everything not applied than just a part of the non-applied filter.
Two possibilities I see:
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3" // And result is "c == 3"Or:
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3" // And result is "b == 2 && c == 3" SECCOMP_FILTER_RESET, __NR_foo //rewind to filter "0" SECCOMP_FILTER_SET, __NR_foo, "c == 4" // And result is "c == 4"
Quoted 1 line
How does that look?
The thing is, I don't understand where we would use (or want) an override. I could understand a UNSET, which I described in another reply. Also, I like the fact that sets can be self contained because then the user can add wrapper functions for them.
add_filter("foo: c == 4");add_filter("bar: b < 3");apply_filter();
That wont work with an override, unless we did the work in userspace to keep string, and then we need to worry about "string matches" as you stated if we want to implement a remove_filter("foo: c==4"). (see it would fail, because this version is missing spaces)
-- Steve
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 16 lines · Steven Rostedt
Quoted 14 lines · Frederic Weisbecker
Quoted 13 lines
It's certainly doable, but it will
mean that we may be logically storing something like:
__NR_foo: (a == 1 || a == 2), applied__NR_foo: b == 2, not applied__NR_foo: c == 3, not appliedafter
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"
Quoted 5 lines
Quoted 1 line
No, the c == 3 would override b == 2.
I honestly hate the "override" mode. I like that SETs are or'd among each other and an APPLY is a "commit"; meaning that you can only limit it further, but can not extend it (an explicit &&)
I'm confused with what you just said...
Somehow I could understand it that way:
SECCOMP_FILTER_SET, __NR_foo, "a == 1"SECCOMP_FILTER_SET, __NR_foo, "a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET, __NR_foo, "b == 1"
Would produce:
"(a == 1 || a == 2) && b == 1"
That makes a pretty confusing behaviour for users I think.
Quoted 7 lines
Quoted 7 lines
Quoted 4 lines
In that case, would a call to
sys_fooeven be tested against the non-applied constraints of b==2 or c==3?No, not as long as it's not applied.
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines
Or would the call to set "c
== 3" replace the "b == 2" entry. I'm not sure I see that the benefit exceeds the ambiguity that might introduce.The rationale behind it is that as long as you haven't applied your filter, you should be able to override it.
We need a "UNSET" (I like that better than DROP).
What about a complete erase (RESET) of the temporary filter? Like I explained below from my previous mail.
Quoted 90 lines
Quoted 11 lines
And the simplest way to override it is to do it completely. Remove what was there before (that wasn't applied).
You could opt for a
FILTER_DROP.
Let's take that example:
SECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"Quoted 8 lines
Following your logic, we should have "b == 2 && c == 3" after that. How are you going to remove only the c == 3 sequence?
You would need
SECCOMP_FILTER_DROP,__NR_foo, "c == 3"That said, using a string with a filter expression as an id looks a bit odd to me. That doesn't work anymore with "((c == 3))", or "c== 3", unless you compare against the resulting tree but that's complicated.
Nah, it should be easy. Actually, in "set" mode (before the apply) we should keep a link list of "trees" of sets. Then we just find the "tree" that matches the UNSET and remove it.
Quoted 7 lines
I mean, that works, most of the time you keep your expression and pass it as is. But the expression should be identified by its meaning, not by some random language layout.
That also forces you to scatter your filter representation:
Quoted 8 lines
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3"For me this shouldn't be different than
SECCOMP_FILTER_SET, __NR_foo, "b == 2 && c == 3"Still nobody will be able to remove a part of the above expression.
Correct, as the sets will be just link lists of sets. Once we apply it, it goes into the main tree.
Thus, to find the UNSET set, we would evaluate the tree of that unset, and search for it. If it is found, remove it, otherwise return EINVAL or something.
Quoted 24 lines
So, I'm more for having something that removes everything not applied than just a part of the non-applied filter.
Two possibilities I see:
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3" // And result is "c == 3"Or:
SECCOMP_FILTER_SET, __NR_foo, "b == 2" SECCOMP_FILTER_SET, __NR_foo, "c == 3" // And result is "b == 2 && c == 3" SECCOMP_FILTER_RESET, __NR_foo //rewind to filter "0" SECCOMP_FILTER_SET, __NR_foo, "c == 4" // And result is "c == 4"Quoted 1 line
How does that look?
The thing is, I don't understand where we would use (or want) an override. I could understand a UNSET, which I described in another reply. Also, I like the fact that sets can be self contained because then the user can add wrapper functions for them.
add_filter("foo: c == 4");add_filter("bar: b < 3");apply_filter();That wont work with an override, unless we did the work in userspace to keep string, and then we need to worry about "string matches" as you stated if we want to implement a
remove_filter("foo: c==4"). (see it would fail, because this version is missing spaces)-- Steve
Steven Rostedt
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 27 lines · Frederic Weisbecker
Quoted 16 lines · Steven Rostedt
Quoted 14 lines · Frederic Weisbecker
Quoted 13 lines
It's certainly doable, but it will
mean that we may be logically storing something like:
__NR_foo: (a == 1 || a == 2), applied__NR_foo: b == 2, not applied__NR_foo: c == 3, not appliedafter
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"Quoted 5 lines
Quoted 1 line
No, the c == 3 would override b == 2.
I honestly hate the "override" mode. I like that SETs are or'd among each other and an APPLY is a "commit"; meaning that you can only limit it further, but can not extend it (an explicit &&)
I'm confused with what you just said...
Somehow I could understand it that way:
Quoted 8 lines
SECCOMP_FILTER_SET,__NR_foo, "a == 1"SECCOMP_FILTER_SET,__NR_foo, "a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 1"Would produce:
"(a == 1 || a == 2) && b == 1"
No, it would produce:
(a == 1 || a == 2)
The b == 1 will not be added until the next apply.
Quoted 2 lines
That makes a pretty confusing behaviour for users I think.
Not really, if it is documented well. Or we can call it:
SECCOMP_FILTER_SET_OR and SECCOMP_FILTER_APPLY_AND
to remove the ambiguity. The reason is actually quite simple. Before you do an apply, you can modify it to whatever you want. But once you do an apply, you just limited yourself. An apply can not be reversed.
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 5 lines
In that case, would a call to
sys_fooeven be tested against the non-applied constraints of b==2 or c==3?No, not as long as it's not applied.
Quoted 3 lines
Or would the call to set "c
== 3" replace the "b == 2" entry. I'm not sure I see that the benefit exceeds the ambiguity that might introduce.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
The rationale behind it is that as long as you haven't applied your filter, you should be able to override it.
We need a "UNSET" (I like that better than DROP).
What about a complete erase (RESET) of the temporary filter? Like I explained below from my previous mail.
What is a temporary filter? And a RESET could be there too to just remove all sets that are pending.
I was thinking that we add "SETS" which the kernel can verify are correct and let the user know at the time of the command if it is valid. But these sets are not actually implemented until an APPLY is hit.
-- Steve
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 75 lines · Steven Rostedt
Quoted 27 lines · Frederic Weisbecker
Quoted 16 lines · Steven Rostedt
Quoted 14 lines · Frederic Weisbecker
Quoted 13 lines
It's certainly doable, but it will
mean that we may be logically storing something like:
__NR_foo: (a == 1 || a == 2), applied__NR_foo: b == 2, not applied__NR_foo: c == 3, not appliedafter
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_SET,__NR_foo, "c == 3"Quoted 5 lines
Quoted 1 line
No, the c == 3 would override b == 2.
I honestly hate the "override" mode. I like that SETs are or'd among each other and an APPLY is a "commit"; meaning that you can only limit it further, but can not extend it (an explicit &&)
I'm confused with what you just said...
Somehow I could understand it that way:
Quoted 8 lines
SECCOMP_FILTER_SET,__NR_foo, "a == 1"SECCOMP_FILTER_SET,__NR_foo, "a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 1"Would produce:
"(a == 1 || a == 2) && b == 1"
No, it would produce:
(a == 1 || a == 2)
The b == 1 will not be added until the next apply.
Quoted 2 lines
That makes a pretty confusing behaviour for users I think.
Not really, if it is documented well. Or we can call it:
SECCOMP_FILTER_SET_ORandSECCOMP_FILTER_APPLY_ANDto remove the ambiguity. The reason is actually quite simple. Before you do an apply, you can modify it to whatever you want. But once you do an apply, you just limited yourself. An apply can not be reversed.
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 5 lines
In that case, would a call to
sys_fooeven be tested against the non-applied constraints of b==2 or c==3?No, not as long as it's not applied.
Quoted 3 lines
Or would the call to set "c
== 3" replace the "b == 2" entry. I'm not sure I see that the benefit exceeds the ambiguity that might introduce.Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
The rationale behind it is that as long as you haven't applied your filter, you should be able to override it.
We need a "UNSET" (I like that better than DROP).
What about a complete erase (RESET) of the temporary filter? Like I explained below from my previous mail.
Quoted 7 lines
What is a temporary filter? And a RESET could be there too to just remove all sets that are pending.
I was thinking that we add "SETS" which the kernel can verify are correct and let the user know at the time of the command if it is valid. But these sets are not actually implemented until an APPLY is hit.
The past few mails have proposed quite a few interesting variants, but I wonder if Eric's code samples show something useful about the not-yet-applied filters in addition to the behavioral differences between changing implicit operations (&& versus ||).
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance, Eric's second sample showed a call that took an array of ints and coalesced them into "fd == %d || ...". That simple example shows that we could easily get by with a pretty minimal kernel-supported interface as long as the richer behavior could live userspace side -- even if just in a simple helper library. It'd be pretty easy to implement a userspace library that exposed add_filter(syscall_nr, filter) and apply_filters() such that it could manage building the final filter string for a given syscall and pushing it to prctl on apply.
I think that could also help simplify the primitives. For instance, if any separate SET called on a system call resulting in an && operation, then the behavior could be consistent prior to enforcement of the filtering and after. E.g.,
SET, __NR_read, "fd == 1"
SET, __NR_read, "len < 4097"
would result in an evaluated "fd == 1 && len < 4097". It would do so after a single APPLY call too:
SET, __NR_read, "1"
APPLY
SET, __NR_read, "fd == 1"
SET, __NR_read, "len < 4097"
Results in: "1 && fd == 1 && len < 4097", and SET, nr, "0" would nullify the syscall filter in total. It seems like that would be enough to build the SET-SET-...-APPLY, SET-SET-...-SET-APPLY logic into a userspace library so that all temporary unapplied state doesn't have to be explicitly managed by the kernel.
While I completely agree with the comment around ease-of-use as being key to security, I also find that the more the state diagram explodes, the harder it is to feel confident that a solution is actually secure.
To try to achieve both objectives, I'd like to limit the kernel
interface to the bare minimum of primitives and build any API fanciness into userspace.
Does it seem that the tradeoff isn't worth it, or are there some specific behaviors that aren't addressed using that model?
While writing that, another option occurred to me that touches on the other proposals but makes the behaviors much more explicit.
A prctl prototype could be provided:
prctl(<SET|GET>, <AND|OR>, <syscall_nr>, <filter string>)
e.g.,
prctl(PR_SET_SECCOMP_FILTER, PR_SECCOMP_FILTER_OR, __NR_read, "fd == 2");
The explicit prctl argument list would allow the filter strings to be self-referential and allow the userspace app to decide what behaviors are allowed and when. If we followed that route, all implicit filters would be "0" and the initial call to get things started might be:
#define SET 33
#define OR 0
#define AND 1
SET, OR, __NR_prctl, "option == 33 && (arg1 == 0 || arg1 == 1)" prctl(PR_SET_SECCOMP, 2);
So now the "locked down" binary can call prctl to set an OR or AND filter for any syscall. A subsequent call could change that:
SET, OR, __NR_read, "fd == 2" /* => "0 || fd == 2" */
SET, AND, __NR_prctl, "(arg2 != 63 || arg1 != 0)" /* __NR_read == 63 */
This would OR in a __NR_read filter, then disallow a future call to prctl to OR in more NR_read filters, but for other syscalls ANDing and ORing is still possible until you pass in something like:
SET, AND, __NR_prctl, "arg1 == 1"
which would lock down all future prctl calls to only ANDing filters in. (The numbers in the examples could then be properly managed in a userspace library to ensure platform correctness.)
While this would reduce the primitives a bit further, I'm not sure if this would be the right approach either, but it would open the door to pushing even more down to userspace very explicitly and further removing magic policy logic from the kernel-side. Is this vaguely interesting or just another layer of confusing-ness?
I'll follow Eric's lead and try out a few different interfaces proposed earlier and the ones I laid out above and see if it seems to come out any clearer (for me at least). I'd love to know if anyone else thinks we can get away with less primitives and put more of the complex/delayed logic in userspace exclusively.
thanks!
will
Serge E. Hallyn
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 3 lines · Will Drewry
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance,
Hi Will,
just one note - in my original comment I wasn't actually suggesting disabling setting of filters through a writeable file - I was only suggesting restricting writing to one's own filters file.
All the better if it is possible to get a nice prctl-only interface, but if it ends up limiting rule expressiveness (or taking years to define an interface) then perhaps we should stick with prctl for setting seccomp mode, and a more expressive file interface for defining filters.
-serge
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 11 lines · Serge E. Hallyn
Quoted 3 lines · Will Drewry
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance,
Hi Will,
just one note - in my original comment I wasn't actually suggesting disabling setting of filters through a writeable file - I was only suggesting restricting writing to one's own filters file.
Quoted 5 lines
All the better if it is possible to get a nice prctl-only interface, but if it ends up limiting rule expressiveness (or taking years to define an interface) then perhaps we should stick with prctl for setting seccomp mode, and a more expressive file interface for defining filters.
Didn't want you to think I missed this -- thanks for clarifying! I've attempted to pull together a prctl interface that balances the directions proposed by Eric, Steven, Frederic, and co. Upon reflection of the /proc interface, it seems to have similar challenges, but if the new patchset tanks and a /proc interface would have more flexibility, I'll definitely explore that route. I'd certainly like to avoid spending years defining this, especially upfront, and I'll take any guidance as to how to best reach a reasonable starting place! (Of course, I'd appreciate feedback on this round of patches too :)
Thanks!
will
Steven Rostedt
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Will DrewryQuoted 4 lines · Will Drewry
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance, Eric's second sample showed a call that took an array of ints and
Quoted 8 lines
coalesced them into "fd == %d || ...". That simple example shows that we could easily get by with a pretty minimal kernel-supported interface as long as the richer behavior could live userspace side -- even if just in a simple helper library. It'd be pretty easy to implement a userspace library that exposed
add_filter(syscall_nr, filter)andapply_filters()such that it could manage building the final filter string for a given syscall and pushing it to prctl on apply.
I'm fine with a single kernel call and the "temporary filter" be done in userspace. Making the kernel code less complex is better :)
Quoted 15 lines
I think that could also help simplify the primitives. For instance, if any separate SET called on a system call resulting in an && operation, then the behavior could be consistent prior to enforcement of the filtering and after. E.g.,
SET,__NR_read, "fd == 1"
SET,__NR_read, "len < 4097"
would result in an evaluated "fd == 1 && len < 4097". It would do so after a single APPLY call too:
SET,__NR_read, "1"
APPLY
SET,__NR_read, "fd == 1"
SET,__NR_read, "len < 4097"
Results in: "1 && fd == 1 && len < 4097", and SET, nr, "0" would nullify the syscall filter in total.
Only that that was not applied? We can't let tasks nullify their restrictions once they have been applied. This keeps the kernel code simpler.
Quoted 4 lines
It seems like that would be
enough to build the SET-SET-...-APPLY, SET-SET-...-SET-APPLY logic into a userspace library so that all temporary unapplied state doesn't have to be explicitly managed by the kernel.
Thus, the SETs are done in the userspace library that does not need to interact with the kernel (besides perhaps allocating memory). Then the apply would send all the filters to the kernel which would restrict the task (or the task on exec) further.
Quoted 7 lines
While I completely agree with the comment around ease-of-use as being key to security, I also find that the more the state diagram explodes, the harder it is to feel confident that a solution is actually secure.
To try to achieve both objectives, I'd like to limit the kernel
interface to the bare minimum of primitives and build any API fanciness into userspace.
Fair enough.
Quoted 30 lines
Does it seem that the tradeoff isn't worth it, or are there some specific behaviors that aren't addressed using that model?
While writing that, another option occurred to me that touches on the other proposals but makes the behaviors much more explicit.
A prctl prototype could be provided:
prctl(<SET|GET>, <AND|OR>, <syscall_nr>, <filter string>)
e.g.,
prctl(PR_SET_SECCOMP_FILTER, PR_SECCOMP_FILTER_OR, __NR_read, "fd == 2");The explicit prctl argument list would allow the filter strings to be self-referential and allow the userspace app to decide what behaviors are allowed and when. If we followed that route, all implicit filters would be "0" and the initial call to get things started might be:
#define SET 33 #define OR 0 #define AND 1 SET, OR, __NR_prctl, "option == 33 && (arg1 == 0 || arg1 == 1)" prctl(PR_SET_SECCOMP, 2);So now the "locked down" binary can call prctl to set an OR or AND filter for any syscall. A subsequent call could change that:
SET, OR,__NR_read, "fd == 2" /* => "0 || fd == 2" */
SET, AND,__NR_prctl, "(arg2 != 63 || arg1 != 0)" /*__NR_read== 63 */This would OR in a
__NR_readfilter, then disallow a future call to prctl to OR in moreNR_readfilters, but for other syscalls ANDing and ORing is still possible until you pass in something like:
Quoted 5 lines
SET, AND,
__NR_prctl, "arg1 == 1"which would lock down all future prctl calls to only ANDing filters in. (The numbers in the examples could then be properly managed in a userspace library to ensure platform correctness.)
I don't know about this. It seems to be starting to get too complex, and thus error prone. Is there any reason we should allow an OR to the task? Why would we want to restrict a task where the task could easily unrestrict itself?
Quoted 6 lines
While this would reduce the primitives a bit further, I'm not sure if this would be the right approach either, but it would open the door to pushing even more down to userspace very explicitly and further removing magic policy logic from the kernel-side. Is this vaguely interesting or just another layer of confusing-ness?
I'm confused, thus I must have hit that layer ;)
-- Steve
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 15 lines · Steven Rostedt
Quoted 4 lines · Will Drewry
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance, Eric's second sample showed a call that took an array of ints and
Quoted 8 lines
coalesced them into "fd == %d || ...". That simple example shows that we could easily get by with a pretty minimal kernel-supported interface as long as the richer behavior could live userspace side -- even if just in a simple helper library. It'd be pretty easy to implement a userspace library that exposed
add_filter(syscall_nr, filter)andapply_filters()such that it could manage building the final filter string for a given syscall and pushing it to prctl on apply.
Quoted 2 lines
I'm fine with a single kernel call and the "temporary filter" be done in userspace. Making the kernel code less complex is better :)
I'm also starting to think a userspace library is a good idea as well. Everything in the kernel is an && with nothing but SET and APPLY. We provide interfaces to build the strings as we go along including UNSET and stuff like that if there are users....
-Eric
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Steven RostedtQuoted 34 lines · Steven Rostedt
Quoted 4 lines · Will Drewry
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance, Eric's second sample showed a call that took an array of ints and
Quoted 8 lines
coalesced them into "fd == %d || ...". That simple example shows that we could easily get by with a pretty minimal kernel-supported interface as long as the richer behavior could live userspace side -- even if just in a simple helper library. It'd be pretty easy to implement a userspace library that exposed
add_filter(syscall_nr, filter)andapply_filters()such that it could manage building the final filter string for a given syscall and pushing it to prctl on apply.I'm fine with a single kernel call and the "temporary filter" be done in userspace. Making the kernel code less complex is better :)
Quoted 15 lines
I think that could also help simplify the primitives. For instance, if any separate SET called on a system call resulting in an && operation, then the behavior could be consistent prior to enforcement of the filtering and after. E.g.,
SET,__NR_read, "fd == 1"
SET,__NR_read, "len < 4097"
would result in an evaluated "fd == 1 && len < 4097". It would do so after a single APPLY call too:
SET,__NR_read, "1"
APPLY
SET,__NR_read, "fd == 1"
SET,__NR_read, "len < 4097"
Results in: "1 && fd == 1 && len < 4097", and SET, nr, "0" would nullify the syscall filter in total.
Quoted 3 lines
Only that that was not applied? We can't let tasks nullify their restrictions once they have been applied. This keeps the kernel code simpler.
Ah - so I really need to be more explicit when discussing these things! In the "simplification" effort, I was thinking any syscall with no entry has a "0" rule. So if if nullify it, it becomes a complete block and if you can't OR, then you can't add permissions.
Quoted 9 lines
Quoted 4 lines
It seems like that would be
enough to build the SET-SET-...-APPLY, SET-SET-...-SET-APPLY logic into a userspace library so that all temporary unapplied state doesn't have to be explicitly managed by the kernel.Thus, the SETs are done in the userspace library that does not need to interact with the kernel (besides perhaps allocating memory). Then the apply would send all the filters to the kernel which would restrict the task (or the task on exec) further.
Exactly. Smaller patch and less state per-filter entry (I hope!).
Quoted 42 lines
Quoted 7 lines
While I completely agree with the comment around ease-of-use as being key to security, I also find that the more the state diagram explodes, the harder it is to feel confident that a solution is actually secure. To try to achieve both objectives, I'd like to limit the kernel interface to the bare minimum of primitives and build any API fanciness into userspace.
Fair enough.
Quoted 32 lines
Does it seem that the tradeoff isn't worth it, or are there some specific behaviors that aren't addressed using that model?
While writing that, another option occurred to me that touches on the other proposals but makes the behaviors much more explicit.
A prctl prototype could be provided:
prctl(<SET|GET>, <AND|OR>, <syscall_nr>, <filter string>) e.g.,
prctl(PR_SET_SECCOMP_FILTER,PR_SECCOMP_FILTER_OR,__NR_read, "fd == 2");The explicit prctl argument list would allow the filter strings to be self-referential and allow the userspace app to decide what behaviors are allowed and when. If we followed that route, all implicit filters would be "0" and the initial call to get things started might be:
#define SET 33
#define OR 0
#define AND 1
SET, OR,__NR_prctl, "option == 33 && (arg1 == 0 || arg1 == 1)"prctl(PR_SET_SECCOMP, 2);So now the "locked down" binary can call prctl to set an OR or AND filter for any syscall. A subsequent call could change that:
SET, OR,__NR_read, "fd == 2" /* => "0 || fd == 2" */
SET, AND,__NR_prctl, "(arg2 != 63 || arg1 != 0)" /*__NR_read== 63 */This would OR in a
__NR_readfilter, then disallow a future call to prctl to OR in moreNR_readfilters, but for other syscalls ANDing and ORing is still possible until you pass in something like:SET, AND,
__NR_prctl, "arg1 == 1"
Quoted 8 lines
Quoted 3 lines
which would lock down all future prctl calls to only ANDing filters in. (The numbers in the examples could then be properly managed in a userspace library to ensure platform correctness.)
I don't know about this. It seems to be starting to get too complex, and thus error prone. Is there any reason we should allow an OR to the task? Why would we want to restrict a task where the task could easily unrestrict itself?
No idea! I can't think of any good examples where you'd want to do it, just contrived ones. In general, I think the above approach would rarely be used since I expect that something like 80% of the places where this will be used will just be one-time, upfront filter installs without any surface reduction after the fact.
That said, if there's no reason to support OR after the fact, then the interface can just _only_ support &&s and leave the installation to userspace. It might makes the multiple-fd-ORing case less fun in userspace, but it should work for most cases I think.
Quoted 8 lines
Quoted 6 lines
While this would reduce the primitives a bit further, I'm not sure if this would be the right approach either, but it would open the door to pushing even more down to userspace very explicitly and further removing magic policy logic from the kernel-side. Is this vaguely interesting or just another layer of confusing-ness?
I'm confused, thus I must have hit that layer ;)
Sounds like it. I'm always a sucker for self-referential mechanisms. I've been travelling a bit recently so my code output has been a bit low, but I'll pull together the most minimal approach that I think we've been iterating toward and hopefully post something in the not too distant future.
thanks!
Will Drewry
reviewerPATCH 5/5 v2 seccomp_filter: Document what seccomp_filter is and how it works.
Adds a text file covering what CONFIG_SECCOMP_FILTER is, how it is implemented presently, and what it may be used for. In addition, the limitations and caveats of the proposed implementation are included.
v2: moved to prctl/
updated for the v2 syntax.
adds a note about compat behavior
--- Documentation/prctl/seccomp_filter.txt | 156 ++++++++++++++++++++++++++++++++ 1 files changed, 156 insertions(+), 0 deletions(-) create mode 100644 Documentation/prctl/seccomp_filter.txt
Documentation/prctl/seccomp_filter.txt new file +156 -0
@@ -0,0 +1,156 @@ + Seccomp filtering + ================= + +Introduction +------------ + +A large number of system calls are exposed to every userland process +with many of them going unused for the entire lifetime of the process. +As system calls change and mature, bugs are found and eradicated. A +certain subset of userland applications benefit by having a reduce set +of available system calls. The reduced set reduces the total kernel +surface exposed to the application. System call filtering is meant for +use with those applications. + +The implementation currently leverages both the existing seccomp +infrastructure and the kernel tracing infrastructure. By centralizing +hooks for attack surface reduction in seccomp, it is possible to assure +attention to security that is less relevant in normal ftrace scenarios, +such as time-of-check, time-of-use attacks. However, ftrace provides a +rich, human-friendly environment for interfacing with system call +specific arguments. (As such, this requires FTRACE_SYSCALLS for any +introspective filtering support.) + + +What it isn't +------------- + +System call filtering isn't a sandbox. It provides a clearly defined +mechanism for minimizing the exposed kernel surface. Beyond that, +policy for logical behavior and information flow should be managed with +an LSM of your choosing. Filtering based on the ftrace filter engine +provides further options down this path (avoiding pathological sizes, +for instance), but it could be misconstrued for a real sandbox. + + +Usage +----- + +An additional seccomp mode is exposed through mode '2', +PR_SECCOMP_MODE_FILTER. This mode depends on CONFIG_SECCOMP_FILTER +which in turn depends on CONFIG_FTRACE_SYSCALLS. + +A collection of filters may be supplied via prctl, and the current set +of filters is exposed in /proc/<pid>/seccomp_filter. + +Interacting with seccomp filters can be done through three new prctl calls +and one existing one. + +PR_SET_SECCOMP: A pre-existing option for enabling strict seccomp + mode (1) or filtering seccomp. This option now takes an + additional "flags" argument. + + Usage: + prctl(PR_SET_SECCOMP, 1); + prctl(PR_SET_SECCOMP, PR_SECCOMP_MODE_FILTER, 0); + Flags: + - 0: Empty set. + - PR_SECCOMP_FLAG_FILTER_ON_EXEC: Delays enforcement of seccomp + enforcment only on MODE_FILTER until an exec() call is seen. + +PR_SET_SECCOMP_FILTER: Allows the specification of a new filter for + a given system call, by number, and filter string. If + CONFIG_FTRACE_SYSCALLS is supported, the filter string may be + any valid value for the given system call. If it is not + supported, the filter string may only be "1" or "0". + + All calls to PR_SET_SECCOMP_FILTER for a given system + call will append the supplied string to any existing filters. + Filter construction looks as follows: + (Nothing) + "fd == 1 || fd == 2" => fd == 1 || fd == 2 + ... + "fd != 2" => (fd == 1 || fd == 2) && fd != 2 + ... + "size < 100" => + ((fd == 1 || fd == 2) && fd != 2) && size < 100 + If there is no filter and the seccomp mode has already + transitioned to filtering, additions cannot be made. Filters + may only be added that reduce the available kernel surface. + + Usage (per the construction example above): + prctl(PR_SET_SECCOMP_FILTER, __NR_write, "fd == 1 || fd == 2"); + prctl(PR_SET_SECCOMP_FILTER, __NR_write, "fd != 2"); + prctl(PR_SET_SECCOMP_FILTER, __NR_write, "size < 100"); + +PR_CLEAR_SECCOMP_FILTER: Removes all filter entries for a given system + call number. When called prior to entering seccomp filtering + mode, it allows for new filters to be applied to the same system + call. After transition, however, it completely drops access to + the call. + + Usage: + prctl(PR_CLEAR_SECCOMP_FILTER, __NR_open); + +PR_GET_SECCOMP_FILTER: Returns the aggregated filter string for a system + call into a user-supplied buffer of a given length. + + Usage: + prctl(PR_GET_SECCOMP_FILTER, __NR_write, buf, + sizeof(buf)); + +All of the above calls return 0 on success and non-zero on error. + + +Example +------- + +Assume a process would like to cleanly read and write to stdin/out/err +as well as access its filters after seccomp enforcement begins. This +may be done as follows: + + prctl(PR_SET_SECCOMP_FILTER, __NR_read, "fd == 0"); + prctl(PR_SET_SECCOMP_FILTER, __NR_write, "fd == 1 || fd == 2"); + prctl(PR_SET_SECCOMP_FILTER, __NR_exit, "1"); + prctl(PR_SET_SECCOMP_FILTER, __NR_prctl, "1"); + + prctl(PR_SET_SECCOMP, PR_SECCOMP_MODE_FILTER, 0); + + /* Do stuff with fdset . . .*/ + + /* Drop read access and keep only write access to fd 1. */ + prctl(PR_CLEAR_SECCOMP_FILTER, __NR_read); + prctl(PR_SET_SECCOMP_FILTER, __NR_write, "fd != 2"); + + /* Perform any final processing . . . */ + syscall(__NR_exit, 0); + +If the initial setup had been handled through a launcher of some sort, +the call to PR_SET_SECCOMP may have been replaced with: + prctl(PR_SET_SECCOMP, PR_SECCOMP_MODE_FILTER, PR_SECCOMP_FLAG_FILTER_ON_EXEC); + /* ... */ + execve(path, args); + +This will continue to allow system calls to proceed uninspected until an +exec*() call is seen. From that point onward, the calling process will +have filters enforced. + + +Caveats +------- + +- The filter event subsystem comes from CONFIG_TRACE_EVENTS, and the +system call events come from CONFIG_FTRACE_SYSCALLS. However, if +neither are available, a filter string of "1" will be honored, and it may +be removed using PR_CLEAR_SECCOMP_FILTER. With ftrace filtering, +calling PR_SET_SECCOMP_FILTER with a filter of "0" would have similar +affect but would not be consistent on a kernel without the support. + +- Some platforms support a 32-bit userspace with 64-bit kernels. In +these cases (CONFIG_COMPAT), system call numbers may not match across +64-bit and 32-bit system calls. This may be especially relevant when +filters are inherited across execution contexts. If filters are created +in a non-compat context then inherited into a compat context, the +inheriting process will be terminated if seccomp filtering mode is +enabled. If it is not yet enabled, the inheriting process may iterate +over the available system calls clearing any existing values. Once no +filters remain, it can begin setting new filters based on its own +context. (This behavior is bidirectional: compat->non-compat, +non-compat->compat.)
Signature · 1 line
1.7.0.4
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Will DrewryQuoted 14 lines · Will Drewry
On Wed, May 4, 2011 at 11:46 AM, Steven Rostedt <[email protected]> wrote:
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance, Eric's second sample showed a call that took an array of ints and coalesced them into "fd == %d || ...". That simple example shows that we could easily get by with a pretty minimal kernel-supported interface as long as the richer behavior could live userspace side -- even if just in a simple helper library. It'd be pretty easy to implement a userspace library that exposed
add_filter(syscall_nr, filter)andapply_filters()such that it could manage building the final filter string for a given syscall and pushing it to prctl on apply.
Had a few minutes this morning so I started writing something that looks sorta like a userspace library. It dosn't have unset yet but I don't think it'll be that hard for me to implement. You can build some pretty complex filters easily. Not sure when I'll get to work on it more, but it at least shows the idea of doing the complex work in a library and keeping a simple kernel interface.... Any thoughts?
-Eric
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libseccomp.h"
/*
* () around the string is 2
* '\0' at the end is 1
* Total extra space needed is 3
*/
#define SECCOMP_STRING_BYTES 3
typedef struct seccomp_filter {
#define SECCOMP_FILTER_TYPE_UNSET 0
#define SECCOMP_FILTER_TYPE_NODE 1
#define SECCOMP_FILTER_TYPE_LEAF 2 char type; struct seccomp_filter *parent; struct seccomp_filter *left; struct seccomp_filter *right;
#define SECCOMP_FILTER_OP_UNSET 0
#define SECCOMP_FILTER_OP_AND " && "
#define SECCOMP_FILTER_OP_OR " || "
#define SECCOMP_FILTER_OP_EQUAL " == "
#define SECCOMP_FILTER_OP_NOT_EQUAL " != "
#define SECCOMP_FILTER_OP_LESS " < "
#define SECCOMP_FILTER_OP_LESS_EQUAL " <= "
#define SECCOMP_FILTER_OP_GREATER " > "
#define SECCOMP_FILTER_OP_GREATER_EQUAL " >= "
#define SECCOMP_FILTER_OP_ONE " 1 "
#define SECCOMP_FILTER_OP_ZERO " 0 " const char *operation; char *arg; char *value; } seccomp_filter_t;
char *seccomp_filter_to_string(seccomp_filter_t *sf)
{
size_t newlen, len;
char *left = NULL;
char *right = NULL;
char *out;
char *p;
if (sf->type == SECCOMP_FILTER_TYPE_NODE) {
left = seccomp_filter_to_string(sf->left);
if (!left)
return NULL;
right = seccomp_filter_to_string(sf->right);
if (!right) {
free(left);
return NULL;
}
if (strcmp(sf->operation, SECCOMP_FILTER_OP_AND) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_OR))
assert(0);
} else if (sf->type == SECCOMP_FILTER_TYPE_LEAF) {
if (sf->arg) {
left = strdup(sf->arg);
if (!left)
return NULL;
}
if (sf->value) {
right = strdup(sf->value);
if (!right) {
free(left);
return NULL;
}
}
if (strcmp(sf->operation, SECCOMP_FILTER_OP_EQUAL) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_NOT_EQUAL) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_LESS) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_LESS_EQUAL) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_GREATER) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_GREATER_EQUAL) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_ONE) &&
strcmp(sf->operation, SECCOMP_FILTER_OP_ZERO))
assert(0);
} else {
assert(0);
}
newlen = 0;
if (left)
newlen += strlen(left);
newlen += strlen(sf->operation);
if (right)
newlen += strlen(right);
newlen += SECCOMP_STRING_BYTES;
out = malloc(newlen);
if (!out) {
free(left);
free(right);
return NULL;
}
p = out;
len = sprintf(p, "(");
p += len;
if (left) {
len = sprintf(p, "%s", left);
p += len;
}
len = sprintf(p, "%s", sf->operation);
p += len;
if (right) {
len = sprintf(p, "%s", right);
p += len;
}
len = sprintf(p, ")");
p += len;
assert((size_t)(p-out) == (newlen - 1));
free(left);
free(right);
return out;
}seccomp_filter_t *__seccomp_filter_alloc(void)
{
seccomp_filter_t *sf;
sf = malloc(sizeof(*sf));
if (!sf)
return NULL;
memset(sf, 0, sizeof(*sf));
return sf;
}void seccomp_filter_free(seccomp_filter_t *sf)
{
if (sf->type == SECCOMP_FILTER_TYPE_NODE) {
seccomp_filter_free(sf->left);
seccomp_filter_free(sf->right);
} else if (sf->type == SECCOMP_FILTER_TYPE_LEAF) {
free(sf->arg);
free(sf->value);
}
free(sf);
}seccomp_filter_t *seccomp_and_filters(seccomp_filter_t *left, seccomp_filter_t *right)
{
seccomp_filter_t *sf;
sf = __seccomp_filter_alloc();
if (!sf)
return NULL;
left->parent = sf;
right->parent = sf;
sf->type = SECCOMP_FILTER_TYPE_NODE;
sf->operation = SECCOMP_FILTER_OP_AND;
sf->left = left;
sf->right = right;
return sf;
}seccomp_filter_t *seccomp_or_filters(seccomp_filter_t *left, seccomp_filter_t *right)
{
seccomp_filter_t *sf;
sf = __seccomp_filter_alloc();
if (!sf)
return NULL;
left->parent = sf;
right->parent = sf;
sf->type = SECCOMP_FILTER_TYPE_NODE;
sf->operation = SECCOMP_FILTER_OP_OR;
sf->left = left;
sf->right = right;
return sf;
}int __seccomp_filter_same(seccomp_filter_t *sf1, seccomp_filter_t *sf2)
{
if (sf1->type != sf2->type)
return 0;
if (strcmp(sf1->operation, sf2->operation))
return 0;
if (sf1->type == SECCOMP_FILTER_TYPE_NODE) {
if (!__seccomp_filter_same(sf1->left, sf2->left))
return 0;
if (!__seccomp_filter_same(sf1->right, sf2->right))
return 0;
} else if (sf1->type == SECCOMP_FILTER_TYPE_LEAF) {
if (sf1->arg && !sf2->arg)
return 0;
if (!sf1->arg && sf2->arg)
return 0;
if (sf1->arg && strcmp(sf1->arg, sf2->arg))
return 0;
if (sf1->value && !sf2->value)
return 0;
if (!sf1->value && sf2->value)
return 0;
if (sf1->value && strcmp(sf1->value, sf2->value))
return 0;
} else {
assert(0);
}
return 1;
}seccomp_filter_t *__seccomp_arg_value(char *operation, char *arg, char *value)
{
seccomp_filter_t *sf;
sf = __seccomp_filter_alloc();
if (!sf)
return NULL;
sf->type = SECCOMP_FILTER_TYPE_LEAF;
sf->operation = operation;
if (arg) {
sf->arg = strdup(arg);
if (!sf->arg)
goto err;
}
if (value) {
sf->value = strdup(value);
if (!sf->value)
goto err;
}
return sf;
err:
free(sf->arg);
free(sf->value);
free(sf);
return NULL;
}seccomp_filter_t *seccomp_one(void)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_ONE, NULL, NULL);
}seccomp_filter_t *seccomp_zero(void)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_ZERO, NULL, NULL);
}seccomp_filter_t *seccomp_equal_arg_value(char *arg, char *value)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_EQUAL, arg, value);
}seccomp_filter_t *seccomp_not_equal_arg_value(char *arg, char *value)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_NOT_EQUAL, arg, value);
}seccomp_filter_t *seccomp_less_arg_value(char *arg, char *value)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_LESS, arg, value);
}seccomp_filter_t *seccomp_less_equal_arg_value(char *arg, char *value)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_LESS_EQUAL, arg, value);
}seccomp_filter_t *seccomp_greater_arg_value(char *arg, char *value)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_GREATER, arg, value);
}seccomp_filter_t *seccomp_greater_equal_arg_value(char *arg, char *value)
{
return __seccomp_arg_value(SECCOMP_FILTER_OP_GREATER_EQUAL, arg, value);
}int seccomp_filter_unset(seccomp_filter_t *tree __attribute__((unused)),
seccomp_filter_t *subset __attribute__((unused)))
{
/*
seccompt_filter_t *sf;
if (__seccomp_filter_same(tree, subset)) {
} else if (tree->type == SECCOMP_FILTER_TYPE_NODE) {
if (!seccomp_filter_unset(tree->left, subset))
return 0;
if (!seccomp_filter_unset(tree->right, subset))
return 0;
} else {
return -EINVAL;
}
*/
return 0;
}typedef struct seccomp_filter seccomp_filter_t;
char *seccomp_filter_to_string(seccomp_filter_t *sf);seccomp_filter_t *seccomp_and_filters(seccomp_filter_t *left, seccomp_filter_t *right); seccomp_filter_t *seccomp_or_filters(seccomp_filter_t *left, seccomp_filter_t *right); seccomp_filter_t *seccomp_equal_arg_value(char *arg, char *value); seccomp_filter_t *seccomp_not_equal_arg_value(char *arg, char *value); seccomp_filter_t *seccomp_less_arg_value(char *arg, char *value); seccomp_filter_t *seccomp_less_equal_arg_value(char *arg, char *value); seccomp_filter_t *seccomp_greater_arg_value(char *arg, char *value); seccomp_filter_t *seccomp_greater_equal_arg_value(char *arg, char *value); seccomp_filter_t *seccomp_one(void);seccomp_filter_t *seccomp_zero(void);
void seccomp_filter_free(seccomp_filter_t *sf);
all: libseccomp.o test
libseccomp.o: libseccomp.c libseccomp.h Makefile
gcc -o libseccomp.o -Wall -Wextra -Werror -W -g -c libseccomp.c
test: test.c libseccomp.h libseccomp.o Makefile
gcc -o test -Wall -Wextra -Werror -W -g test.c libseccomp.o
clean:
rm -f test libseccomp.o
#include <stdio.h>
#include "libseccomp.h"
int main(void)
{
seccomp_filter_t *left, *right, *sf1, *sf2, *sf;
char *string;
left = seccomp_equal_arg_value("a0", "1");
if (!left)
return 1;
right = seccomp_not_equal_arg_value("a0", "0");
if (!right)
return 1;
sf1 = seccomp_and_filters(left, right);
if (!sf1)
return 1;
left = seccomp_greater_arg_value("a1", "100");
if (!left)
return 1;
right = seccomp_less_equal_arg_value("a1", "1000");
if (!right)
return 1;
sf2 = seccomp_or_filters(left, right);
if (!sf2)
return 1;
sf = seccomp_and_filters(sf1, sf2);
if (!sf)
return 1;
right = seccomp_one();
if (!right)
return 1;
sf = seccomp_and_filters(sf, right);
if (!sf)
return 1;
string = seccomp_filter_to_string(sf);
if (!string)
return 1;
printf("%s\n", string);
return 0;
}Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 4 lines · Eric Paris
I'm also starting to think a userspace library is a good idea as well. Everything in the kernel is an && with nothing but SET and APPLY. We provide interfaces to build the strings as we go along including UNSET and stuff like that if there are users....
That sounds ideal to me. I think it'll be worth tagging this config option as EXPERIMENTAL to begin with so that there's a chance to see those of us who want it using it more widely and find out if there are missing pieces or dumb limitations. Hopefully by starting minimal, it won't be a huge departure to evolve it if needed.
Quoted 16 lines · Eric Paris
Quoted 14 lines · Will Drewry
On Wed, May 4, 2011 at 11:46 AM, Steven Rostedt <[email protected]> wrote:
In particular, if the userspace code wants to stage some filters and apply them all at once, when ready, I'm not sure that it makes sense to me to put that complexity in the kernel itself. For instance, Eric's second sample showed a call that took an array of ints and coalesced them into "fd == %d || ...". That simple example shows that we could easily get by with a pretty minimal kernel-supported interface as long as the richer behavior could live userspace side -- even if just in a simple helper library. It'd be pretty easy to implement a userspace library that exposed
add_filter(syscall_nr, filter)andapply_filters()such that it could manage building the final filter string for a given syscall and pushing it to prctl on apply.
Quoted 6 lines
Had a few minutes this morning so I started writing something that looks sorta like a userspace library. It dosn't have unset yet but I don't think it'll be that hard for me to implement. You can build some pretty complex filters easily. Not sure when I'll get to work on it more, but it at least shows the idea of doing the complex work in a library and keeping a simple kernel interface.... Any thoughts?
Cool! I was thrown a little bit by spelling out the operations (eq, ne, ..), but I can see how it might be useful for being able to synthesize good filters before passing them off to the kernel.
It sounds like we might be moving towards a good starting point. I'll start playing with the interface some (and start on the kernel support too :).
thanks!
Steven Rostedt
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Frederic WeisbeckerQuoted 14 lines · Frederic Weisbecker
2011/5/3 Frederic Weisbecker <[email protected]>:
Even better: applying a filter would always automatically be an intersection of the previous one.
If you do:
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "c == 3"SECCOMP_FILTER_APPLY
Quoted 4 lines
The end result is:
"(a == 1 || a == 2) && b == 2 && c == 3"
I'm a little confused. Why do we have both a FILTER_SET and a FILTER_APPLY? Maybe this was discussed earlier in the thread and I missed it or simply forgot.
Why not just apply on the set call?
-- Steve
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 17 lines · Steven Rostedt
Quoted 16 lines · Frederic Weisbecker
2011/5/3 Frederic Weisbecker <[email protected]>:
Even better: applying a filter would always automatically be an intersection of the previous one.
If you do:
SECCOMP_FILTER_SET,__NR_foo, "a == 1 || a == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "b == 2"SECCOMP_FILTER_APPLYSECCOMP_FILTER_SET,__NR_foo, "c == 3"SECCOMP_FILTER_APPLYThe end result is:
Quoted 8 lines
Quoted 2 lines
"(a == 1 || a == 2) && b == 2 && c == 3"
I'm a little confused. Why do we have both a
FILTER_SETand aFILTER_APPLY? Maybe this was discussed earlier in the thread and I missed it or simply forgot.Why not just apply on the set call?
As this is a deny by default interface which only allows you to further restrict you couldn't add more than 1 syscall if you didn't have an explict 'apply' action.
SECCOMP_FILTER_SET, __NR_fo, "a=0"SECCOMP_FILTER_SET, __NR_read, "1" == EPERM
Maybe apply on set is fine after the first apply, but we definitely need some way to do more than 1 set before the rules are applied....
-Eric
Steven Rostedt
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 9 lines · Eric Paris
As this is a deny by default interface which only allows you to further restrict you couldn't add more than 1 syscall if you didn't have an explict 'apply' action.
SECCOMP_FILTER_SET,__NR_fo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1" == EPERMMaybe apply on set is fine after the first apply, but we definitely need some way to do more than 1 set before the rules are applied....
So we could have SET be 'or' and APPLY be 'and'.
SECCOMP_FILTER_SET, __NR_foo, "a=0"SECCOMP_FILTER_SET, __NR_read, "1" == EPERMSECCOPM_FILTER_APPLY
SECCOMP_FILTER_SET, __NR_foo, "b=0"SECCOPM_FILTER_APPLY
Will end up being:
(foo: a == 0 || read: "1" == EPERM) && (foo: b == 0)
The second set/apply now removes the read option, and foo only works if a is 0 and b is 0.
This would also work for children, as they can only restrict (with 'and') and can not add more control.
-- Steve
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 9 lines · Steven Rostedt
Quoted 7 lines · Eric Paris
As this is a deny by default interface which only allows you to further restrict you couldn't add more than 1 syscall if you didn't have an explict 'apply' action.
SECCOMP_FILTER_SET,__NR_fo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1" == EPERM
Quoted 7 lines
Quoted 2 lines
Maybe apply on set is fine after the first apply, but we definitely need some way to do more than 1 set before the rules are applied....
So we could have SET be 'or' and APPLY be 'and'.
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1" == EPERM
When I said "== EPERM" I meant that the given prctl call would return EPERM. I'm going to pretend that you didn't type it.
Quoted 7 lines
SECCOPM_FILTER_APPLY
SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOPM_FILTER_APPLYWill end up being:
Quoted 7 lines
(foo: a == 0 || read: "1") && (foo: b == 0)
The second set/apply now removes the read option, and foo only works if a is 0 and b is 0.
This would also work for children, as they can only restrict (with 'and') and can not add more control.
I think we pretty much agree although I'm pretty that we will have 1 filter per syscall. So the rules would really be (in your syntax)
Rule1: (foo: a == 0 && b == 0)
OR
Rule2: (read: "1")
Although logically the same, it's not just one huge rule. I don't see any need for any operation other than an &&. Before the first "set" you can add new syscalls. After the first set you can only && onto existing syscalls. So the following set of operations:
SECCOMP_FILTER_SET, __NR_foo, "a=0"SECCOMP_FILTER_SET, __NR_read, "1"SECCOPM_FILTER_APPLY
SECCOMP_FILTER_SET, __NR_foo, "b=0"SECCOMP_FILTER_APPLY
SECCOMP_FILTER_SET, __NR_write, "1"SECCOMP_FILTER_APPLY
Would return EPERM for the __NR_write entry since it was a new syscall after a set. I think we agree on all this.
I do have a question on some syntax proposed a while back. Given:SECCOMP_FILTER_SET, __NR_foo, "a=0"SECCOMP_FILTER_SET, __NR_foo, "b=0" SECCOMP_FILTER_APPLY
I would think to keep the interface consistent that should result in
foo: (a=0) && (b=0)
But I think the proposal was that we should instead have just
foo: (b=0)
What's the logic behind having a second call overwrite uncommitted changes? I sorta feel like if I put it in there, I must have wanted it
in there :)
-Eric
Steven Rostedt
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 5 lines · Eric Paris
Quoted 2 lines
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1" == EPERMWhen I said "== EPERM" I meant that the given prctl call would return EPERM. I'm going to pretend that you didn't type it.
I was confused about that, but I didn't type that. You did. I just cut and pasted it ;)
Quoted 32 lines
Quoted 8 lines
SECCOPM_FILTER_APPLY
SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOPM_FILTER_APPLYWill end up being:
Quoted 7 lines
(foo: a == 0 || read: "1") && (foo: b == 0)
The second set/apply now removes the read option, and foo only works if a is 0 and b is 0.
This would also work for children, as they can only restrict (with 'and') and can not add more control.
I think we pretty much agree although I'm pretty that we will have 1 filter per syscall. So the rules would really be (in your syntax)
Rule1: (foo: a == 0 && b == 0)
OR
Rule2: (read: "1")Although logically the same, it's not just one huge rule. I don't see any need for any operation other than an &&. Before the first "set" you can add new syscalls. After the first set you can only && onto existing syscalls. So the following set of operations:
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1"SECCOPM_FILTER_APPLY
Quoted 8 lines
SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOMP_FILTER_APPLY
SECCOMP_FILTER_SET,__NR_write, "1"SECCOMP_FILTER_APPLYWould return EPERM for the
__NR_writeentry since it was a new syscall after a set. I think we agree on all this.
Do you mean "after a apply"? As the second line above is a new syscall after the first set.
Quoted 8 lines
I do have a question on some syntax proposed a while back. Given:
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOMP_FILTER_APPLYI would think to keep the interface consistent that should result in
foo: (a=0) && (b=0)
I agree.
Quoted 3 lines
But I think the proposal was that we should instead have just
foo: (b=0)
Yeah, that's what it looked like Frederic showed. I rather have the first instance.
Perhaps we could have a "unset"? that would only work on things that haven't been applied yet.
Quoted 4 lines
What's the logic behind having a second call overwrite uncommitted changes? I sorta feel like if I put it in there, I must have wanted it
in there :)
Perhaps for making the user code simpler?
SET a=1
SET b=2
[ some nasty if logic ]
UNSET b=2
APPLY
Thus a default setting can be made and then we can selectively remove settings before we do the apply based on information about the process that we will exec. We can start out with "limit the hell out of it" and then selectively remove things. I think this is a simply way to understand what is being done. Kind of like iptables, where you can set up default rules, but then selectively override them.
One thing I know about security, the easier it is to set up, the more secure it becomes.
-- Steve
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 1 line · Steven Rostedt
On Wed, 2011-05-04 at 12:22 -0400, Eric Paris wrote:
[rewriting history]
Quoted 12 lines
Quoted 12 lines
Although logically the same, it's not just one huge rule. I don't see any need for any operation other than an &&. Before the first "apply" you can add new syscalls. After the first apply you can only && onto existing syscalls. So the following set of operations:
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1"SECCOPM_FILTER_APPLY
SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOMP_FILTER_APPLY
Quoted 8 lines
Quoted 5 lines
SECCOMP_FILTER_SET,__NR_write, "1"SECCOMP_FILTER_APPLYWould return EPERM for the
__NR_writeentry since it was a new syscall after an apply. I think we agree on all this.Do you mean "after a apply"? As the second line above is a new syscall after the first set.
Clearly. Especially since given my revisionist history it's what I said!
Quoted 45 lines
Quoted 8 lines
I do have a question on some syntax proposed a while back. Given:
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOMP_FILTER_APPLYI would think to keep the interface consistent that should result in
foo: (a=0) && (b=0)I agree.
Quoted 3 lines
But I think the proposal was that we should instead have just
foo: (b=0)Yeah, that's what it looked like Frederic showed. I rather have the first instance.
Perhaps we could have a "unset"? that would only work on things that haven't been applied yet.
Quoted 4 lines
What's the logic behind having a second call overwrite uncommitted changes? I sorta feel like if I put it in there, I must have wanted it
in there :)Perhaps for making the user code simpler?
SET a=1
SET b=2[ some nasty if logic ]
UNSET b=2
APPLY
Thus a default setting can be made and then we can selectively remove settings before we do the apply based on information about the process that we will exec. We can start out with "limit the hell out of it" and then selectively remove things. I think this is a simply way to understand what is being done. Kind of like iptables, where you can set up default rules, but then selectively override them.
Quoted 2 lines
One thing I know about security, the easier it is to set up, the more secure it becomes.
I'm ok with an explicit unset/remove/delete for rules since the last apply if anyone thinks it will be useful. But I don't like an implicit 'overwrite.'
I'm starting to debate if I think between rules should be an implicit && or should be an implicit ||. After an 'apply' I believe the next block definitely needs an &&. I feel like the code in userspace becomes simpler with || but maybe it would be more confusing for the human coder to have to know the distinctions. The example in my head, which I think will be common, involves handling multiple fd's for read. We could either do:
int handle_read_fd(int fd)
{
char buf[4096];
snprintf(buf, 4096, "a0=%d", fd)
return prctl(SECCOMP_FILTER_SET, __NR_read, buf);
}main()
{
....
fd1 = open("readfile1", O_RDONLY);
if (fd1 < 0) return
if (handle_read_fd(fd1)) return
....
fd2 = open("readfile2, O_RDONLY);
if (fd2 < 0) return
if (handle_read_fd(fd2)) return
....
prctl(SECCOMP_FILTER_APPLY);
}Or case2 with the implicit && like we talked about to handle an arbitrary number of read fd's you need (pseudocode):
int handle_read_fds(int *fds, int len)
{
char buf[4096];
size_t saved;
while(len--) {
saved = snprintf(buf, 4096, "a0=%d ||");
buf += saved;
}
return prctl(SECCOMP_FILTER_SET, __NR_read, buf)
}
main()
{
int fd[2];
....
fd[0] = open("readfile1", O_RDONLY);
if (fd[0] < 0) return
....
fd[1] = open("readfile2, O_RDONLY);
if (fd[1] < 0) return
....
handle_read_fds(fd, 2)
prctl(SECCOMP_FILTER_APPLY);
}The latter of the examples requires what I think to be the common case of complex rules to be required to hold state whereas the other is done in the kernel. It's a lot easier to code the first one but it might be harder on the coder to decide "now is than an && or an ||"? Like I said, I'm ok with just declaring everything &&, especially if people think complex filters are likely to be anything more than rule1 || rule2 || rule3 || rule4 where ruleX is an independent clause, but I figured I'd throw it out there....
No matter what after the APPLY I think that:SECCOMP_FILTER, __NR_read, "0" should result in:
(rule1 || rule2 || rule3 || rule4) && 0
-Eric
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Eric ParisQuoted 52 lines · Eric Paris
Quoted 9 lines · Steven Rostedt
Quoted 7 lines · Eric Paris
As this is a deny by default interface which only allows you to further restrict you couldn't add more than 1 syscall if you didn't have an explict 'apply' action.
SECCOMP_FILTER_SET,__NR_fo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1" == EPERMQuoted 7 lines
Quoted 2 lines
Maybe apply on set is fine after the first apply, but we definitely need some way to do more than 1 set before the rules are applied....
So we could have SET be 'or' and APPLY be 'and'.
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1" == EPERMWhen I said "== EPERM" I meant that the given prctl call would return EPERM. I'm going to pretend that you didn't type it.
Quoted 7 lines
SECCOPM_FILTER_APPLY
SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOPM_FILTER_APPLYWill end up being:
Quoted 7 lines
(foo: a == 0 || read: "1") && (foo: b == 0)
The second set/apply now removes the read option, and foo only works if a is 0 and b is 0.
This would also work for children, as they can only restrict (with 'and') and can not add more control.
I think we pretty much agree although I'm pretty that we will have 1 filter per syscall. So the rules would really be (in your syntax)
Rule1: (foo: a == 0 && b == 0)
OR
Rule2: (read: "1")Although logically the same, it's not just one huge rule. I don't see any need for any operation other than an &&. Before the first "set" you can add new syscalls. After the first set you can only && onto existing syscalls. So the following set of operations:
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1"SECCOPM_FILTER_APPLY
Quoted 8 lines
SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOMP_FILTER_APPLY
SECCOMP_FILTER_SET,__NR_write, "1"SECCOMP_FILTER_APPLYWould return EPERM for the
__NR_writeentry since it was a new syscall after a set. I think we agree on all this.
No, why?
The default filter for a syscall, if none have been given for it, is "0".
Thus, if you write "1" later, the entire filter is going to be:
"0 && 1"
Which is fine, we are not overriding already applied permissions there.
So where is the need to return -EPERM in such a specific case? Is it worth the corner case to check in the kernel, and to handle in userspace? And for what reason?
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 22 lines · Frederic Weisbecker
On Wed, May 04, 2011 at 12:22:40PM -0400, Eric Paris wrote:
So the following set of operations:Quoted 5 lines
SECCOMP_FILTER_SET,__NR_foo, "a=0"SECCOMP_FILTER_SET,__NR_read, "1"SECCOPM_FILTER_APPLYQuoted 8 lines
SECCOMP_FILTER_SET,__NR_foo, "b=0"SECCOMP_FILTER_APPLY
SECCOMP_FILTER_SET,__NR_write, "1"SECCOMP_FILTER_APPLYWould return EPERM for the
__NR_writeentry since it was a new syscall after a set. I think we agree on all this.No, why?
The default filter for a syscall, if none have been given for it, is "0".
Thus, if you write "1" later, the entire filter is going to be:
Quoted 7 lines
"0 && 1"Which is fine, we are not overriding already applied permissions there.
So where is the need to return
-EPERMin such a specific case? Is it worth the corner case to check in the kernel, and to handle in userspace? And for what reason?
I assumed without looking at the code (always a bad idea) that he wasn't going to explicitly create a rule with "0" and was going to implicitly deny anything without a rule. If there is an explicit "0" rule then you are right, i don't see a need to deny the set operation in the kernel. But if it is implicit in the non-existence of a filter then it should be easy to tell userspace it isn't allowed any more.
-Eric
-Eric
Serge E. Hallyn
maintainerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Ingo MolnarQuoted 3 lines · Ingo Molnar
I've Cc:-ed Linus and Andrew: are you guys opposed to such flexible, dynamic filters conceptually? I think we should really think hard about the actual ABI as this could easily spread to more applications than Chrome/Chromium.
We want to use it for containers, to try and provide some bit of mitigation for the fact that they are sharing a kernel with the host.
thanks,
-serge
Re: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
In reply to Will DrewryQuoted 18 lines · On Wed, 27 Apr 2011 22:08:49 -0500 Will Drewry wrote:
Adds a text file covering what
CONFIG_SECCOMP_FILTERis, how it is implemented presently, and what it may be used for. In addition, the limitations and caveats of the proposed implementation are included.Signed-off-by Will Drewry <[email protected]>--- Documentation/trace/seccomp_filter.txt | 75 ++++++++++++++++++++++++++++++++ 1 files changed, 75 insertions(+), 0 deletions(-) create mode 100644 Documentation/trace/seccomp_filter.txtDocumentation/trace/seccomp_filter.txt new file +1
-0@@ -0,0 +1,75 @@ + Seccomp filtering
Documentation/trace/seccomp_filter.txt line 11
Quoted 10 lines
Documentation/trace/seccomp_filter.txt new file +10
-0+ ================= + +Introduction +------------ + +A large number of system calls are exposed to every userland process +with many of them going unused for the entire lifetime of the +application. As system calls change and mature, bugs are found and +quashed. A certain subset of userland applications benefit by having +a reduce set of available system calls. The reduced set reduces the
reduced
Quoted 54 lines
Documentation/trace/seccomp_filter.txt new file +54
-0+total kernel surface exposed to the application. System call filtering +is meant for use with those applications. + +The implementation currently leverages both the existing seccomp +infrastructure and the kernel tracing infrastructure. By centralizing +hooks for attack surface reduction in seccomp, it is possible to assure +attention to security that is less relevant in normal ftrace scenarios, +such as time of check, time of use attacks. However, ftrace provides a +rich, human-friendly environment for specifying system calls by name and +expected arguments. (As such, this requires FTRACE_SYSCALLS.) + + +What it isn't +------------- + +System call filtering isn't a sandbox. It provides a clearly defined +mechanism for minimizing the exposed kernel surface. Beyond that, policy for +logical behavior and information flow should be managed with an LSM of your +choosing. + + +Usage +----- + +An additional seccomp mode is exposed through mode '2'. This mode +depends on CONFIG_SECCOMP_FILTER which in turn depends on +CONFIG_FTRACE_SYSCALLS. + +A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it. + +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1 + sys_write: 1 + sys_mmap: 1 + sys_prctl: 1 + +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0");
Documentation/trace/seccomp_filter.txt line 75
Quoted 10 lines
Documentation/trace/seccomp_filter.txt new file +10
-0+ + +Caveats +------- + +The system call names come from ftrace events. At present, many system +calls are not hooked - such as x86's ptregs wrapped system calls. + +In addition compat_task()s will not be supported until a sys32s begin +being hooked.
Last sentence is hard to read IMO:
a. what are compat_task()s?
b. what is a sys32s begin?
c. awkward wording, maybe change to: until a sys32s begin has been hooked.
thanks,
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
Will Drewry
reviewerRe: [PATCH 5/7] seccomp_filter: Document what seccomp_filter is and how it works.
Quoted 94 lines · Randy Dunlap
Quoted 18 lines · On Wed, 27 Apr 2011 22:08:49 -0500 Will Drewry wrote:
Adds a text file covering what
CONFIG_SECCOMP_FILTERis, how it is implemented presently, and what it may be used for. In addition, the limitations and caveats of the proposed implementation are included.Signed-off-by Will Drewry <[email protected]>---
Documentation/trace/seccomp_filter.txt | 75 ++++++++++++++++++++++++++++++++1 files changed, 75 insertions(+), 0 deletions(-)create mode 100644
Documentation/trace/seccomp_filter.txtDocumentation/trace/seccomp_filter.txt new file +1
-0@@ -0,0 +1,75 @@ + Seccomp filteringDocumentation/trace/seccomp_filter.txt line 11
Quoted 10 lines
Documentation/trace/seccomp_filter.txt new file +10
-0+ ================= + +Introduction +------------ + +A large number of system calls are exposed to every userland process +with many of them going unused for the entire lifetime of the +application. As system calls change and mature, bugs are found and +quashed. A certain subset of userland applications benefit by having +a reduce set of available system calls. The reduced set reduces thereduced
Quoted 61 lines
Documentation/trace/seccomp_filter.txt new file +61
-0+total kernel surface exposed to the application. System call filtering +is meant for use with those applications. + +The implementation currently leverages both the existing seccomp +infrastructure and the kernel tracing infrastructure. By centralizing +hooks for attack surface reduction in seccomp, it is possible to assure +attention to security that is less relevant in normal ftrace scenarios, +such as time of check, time of use attacks. However, ftrace provides a +rich, human-friendly environment for specifying system calls by name and +expected arguments. (As such, this requires FTRACE_SYSCALLS.) + + +What it isn't +------------- + +System call filtering isn't a sandbox. It provides a clearly defined +mechanism for minimizing the exposed kernel surface. Beyond that, policy for +logical behavior and information flow should be managed with an LSM of your +choosing. + + +Usage +----- + +An additional seccomp mode is exposed through mode '2'. This mode +depends on CONFIG_SECCOMP_FILTER which in turn depends on +CONFIG_FTRACE_SYSCALLS. + +A collection of filters may be supplied via prctl, and the current set of +filters is exposed in /proc/<pid>/seccomp_filter. + +For instance, + const char filters[] = + "sys_read: (fd == 1) || (fd == 2)\n" + "sys_write: (fd == 0)\n" + "sys_exit: 1\n" + "sys_exit_group: 1\n" + "on_next_syscall: 1"; + prctl(PR_SET_SECCOMP, 2, filters); + +This will setup system call filters for read, write, and exit where reading can +be done only from fds 1 and 2 and writing to fd 0. The "on_next_syscall" directive tells +seccomp to not enforce the ruleset until after the next system call is run. This allows +for launchers to apply system call filters to a binary before executing it. + +Once enabled, the access may only be reduced. For example, a set of filters may be: + + sys_read: 1 + sys_write: 1 + sys_mmap: 1 + sys_prctl: 1 + +Then it may call the following to drop mmap access: + prctl(PR_SET_SECCOMP, 2, "sys_mmap: 0"); + + +Caveats +------- + +The system call names come from ftrace events. At present, many system +calls are not hooked - such as x86's ptregs wrapped system calls.
Quoted 8 lines
Documentation/trace/seccomp_filter.txt line 75
Quoted 3 lines
Documentation/trace/seccomp_filter.txt new file +3
-0+ +In addition compat_task()s will not be supported until a sys32s begin +being hooked.Last sentence is hard to read IMO:
a. what are compat_task()s?
b. what is a sys32s begin?
c. awkward wording, maybe change to: until a sys32s begin has been hooked.
I'll clean it up and try again. I believe the other thread discussing the interface will change this last sentence anyway, so once it settles, I'll update this patch to reflect the new reality.
thanks!
Will Drewry
reviewerPATCH 6/7 include/linux/syscalls.h: add __ layer of macros with return types.
In reply to Will DrewryThis change addresses two issues directly:
1. The inability for ftrace to hook system calls that don't return a long.
2. The fact that SYSCALL_DEFINE() does not touch ftrace at all
1 is fixed by adding __SYSCALL_DEFINEx/0 and __SYSCALL_TRACEx/0 macros which lay underneath the normal call, SYSCALL_DEFINE0, and SYSCALL_DEFINE[1-6]. All existing calls will continue to work normally and SYSCALL_DEFINE calls will become ftrace-able but without argument inspection support.
2 is addressed by separating out SYSCALL_TRACE0 and pulling it under SYSCALL_DEFINE. It means that calls that may lack argument introspection will still be traceable in a binary way without any additional code changes.
There are still some challenges for wrapping calls that have a trampoline directly in the assembly, like the ptregs calls in arch/x86/kernel (e.g., sys_clone). Given that the arguments to the function do not directly map to those of the system call, the main macros are not friendly for use. However, the SYSCALL_TRACE0 macro in this change could be used as a placeholder (declared above the function definition) to allow limited tracing functionality until a better solution is presented.
--- include/linux/syscalls.h | 52 +++++++++++++++++++++++++++++++--------------- 1 files changed, 35 insertions(+), 17 deletions(-)
include/linux/syscalls.h +36 -18
@@ -171,7 +171,7 @@ extern struct trace_event_functions exit_syscall_print_funcs; __attribute__((section("__syscalls_metadata"))) \ *__p_syscall_meta_##sname = &__syscall_meta_##sname; -#define SYSCALL_DEFINE0 (sname) \ +#define __SYSCALL_TRACE0 (linkage, ret, sname) \ SYSCALL_TRACE_ENTER_EVENT(_##sname); \ SYSCALL_TRACE_EXIT_EVENT(_##sname); \ static struct syscall_metadata __used \
@@ -185,12 +185,16 @@ extern struct trace_event_functions exit_syscall_print_funcs; }; \ static struct syscall_metadata __used \ __attribute__((section("__syscalls_metadata"))) \ - *__p_syscall_meta_##sname = &__syscall_meta__##sname; \ - asmlinkage long sys_##sname(void) + *__p_syscall_meta_##sname = &__syscall_meta__##sname;#else -#define SYSCALL_DEFINE0 (name) asmlinkage long sys_##name(void) +#define __SYSCALL_TRACE0 (linkage, ret, sname)#endif +#define __SYSCALL_DEFINE0 (linkage, ret, sname) \ + __SYSCALL_TRACE0(linkage, ret, sname) \ + linkage ret sys_##sname(void) + +#define SYSCALL_DEFINE0 (name) __SYSCALL_DEFINE0(asmlinkage, long, name)#define SYSCALL_DEFINE1 (name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)#define SYSCALL_DEFINE2 (name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)#define SYSCALL_DEFINE3 (name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
@@ -214,39 +218,53 @@ extern struct trace_event_functions exit_syscall_print_funcs;#ifdef CONFIG_FTRACE_SYSCALLS #define SYSCALL_DEFINEx (x, sname, ...) \ + __SYSCALL_DEFINEx(asmlinkage, long, x, sname, __VA_ARGS__) + +#define __SYSCALL_TRACEx (linkage, ret, x, sname, ...) \ static const char *types_##sname[] = { \ __SC_STR_TDECL##x(__VA_ARGS__) \ }; \ static const char *args_##sname[] = { \ __SC_STR_ADECL##x(__VA_ARGS__) \ }; \ - SYSCALL_METADATA(sname, x); \ - __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) + SYSCALL_METADATA(sname, x);#else #define SYSCALL_DEFINEx (x, sname, ...) \ - __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) + __SYSCALL_DEFINEx(asmlinkage, long, x, sname, __VA_ARGS__) +#define __SYSCALL_TRACEx (linkage, ret, x, sname, ...)#endif #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -#define SYSCALL_DEFINE (name) static inline long SYSC_##name - -#define __SYSCALL_DEFINEx (x, name, ...) \ - asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \ - static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ - asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \ +#define SYSCALL_DEFINE (name) \ + __SYSCALL_DEFINE(long, name) +#define __SYSCALL_DEFINE (ret, name) \ + __SYSCALL_TRACE0(, ret, name) + static inline ret SYSC_##name + +#define __SYSCALL_DEFINEx (linkage, ret, x, name, ...) \ + __SYSCALL_TRACEx(linkage, ret, x, name, __VA_ARGS__) \ + linkage ret sys##name(__SC_DECL##x(__VA_ARGS__)); \ + static inline ret SYSC##name(__SC_DECL##x(__VA_ARGS__)); \ + linkage ret SyS##name(__SC_LONG##x(__VA_ARGS__)) \ { \ __SC_TEST##x(__VA_ARGS__); \ - return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \ + return (ret) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \ } \ SYSCALL_ALIAS(sys##name, SyS##name); \ - static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)) + static inline ret SYSC##name(__SC_DECL##x(__VA_ARGS__))#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */ -#define SYSCALL_DEFINE (name) asmlinkage long sys_##name -#define __SYSCALL_DEFINEx (x, name, ...) \ - asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)) +#define SYSCALL_DEFINE (name) \ + __SYSCALL_DEFINE(asmlinkage, long, name) +#define __SYSCALL_DEFINE (linkage, ret, name) \ + __SYSCALL_TRACE0(linkage, ret, name) \ + linkage ret sys_##name + +#define __SYSCALL_DEFINEx (linkage, ret, x, name, ...) \ + __SYSCALL_TRACEx(linkage, ret, x, name, __VA_ARGS__) \ + linkage ret sys##name(__SC_DECL##x(__VA_ARGS__))#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */
Signature · 1 line
1.7.0.4
Will Drewry
reviewerPATCH 7/7 arch/x86: hook int returning system calls
In reply to Will DrewryUsing the newly specified __SYSCALL_DEFINEx helpers, redefine int-returning system calls with the macro to enable ftrace access.
--- arch/x86/kernel/ldt.c | 5 +++-- arch/x86/kernel/tls.c | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-)
arch/x86/kernel/ldt.c +3 -2
@@ -10,6 +10,7 @@#include <linux/gfp.h> #include <linux/sched.h> #include <linux/string.h> +#include <linux/syscalls.h> #include <linux/mm.h> #include <linux/smp.h> #include <linux/vmalloc.h>
@@ -245,8 +246,8 @@ out:return error; } -asmlinkage int sys_modify_ldt(int func, void __user *ptr, -unsigned long bytecount) +__SYSCALL_DEFINEx (asmlinkage, int,3 , _modify_ldt, + int, func, void __user *, ptr, unsigned long, bytecount) {int ret = -ENOSYS ;
arch/x86/kernel/tls.c +5 -2
@@ -1,6 +1,7 @@#include <linux/kernel.h> #include <linux/errno.h> #include <linux/sched.h> +#include <linux/syscalls.h> #include <linux/user.h> #include <linux/regset.h>
@@ -90,7 +91,8 @@ int do_set_thread_area(struct task_struct *p, int idx,return 0 ; } -asmlinkage int sys_set_thread_area(struct user_desc __user *u_info) +__SYSCALL_DEFINEx (asmlinkage, int,1 , _set_thread_area, + struct user_desc __user *, u_info) {int ret =do_set_thread_area (current,-1 , u_info,1 );asmlinkage_protect (1 , ret, u_info);
@@ -140,7 +142,8 @@ int do_get_thread_area(struct task_struct *p, int idx,return 0 ; } -asmlinkage int sys_get_thread_area(struct user_desc __user *u_info) +__SYSCALL_DEFINEx (asmlinkage, int,1 , _get_thread_area, + struct user_desc __user *, u_info) {int ret =do_get_thread_area (current,-1 , u_info);asmlinkage_protect (1 , ret, u_info);
Signature · 1 line
1.7.0.4