twtiger / gosecco

Go seccomp parser and compiler
GNU Lesser General Public License v3.0
53 stars 7 forks source link

Bitwise comparison with specific argument value on right side of expression #47

Closed dma closed 8 years ago

dma commented 8 years ago

Hey,

I was chatting with @shw700 and it seems that we can't do a check like so:

open: arg1 == (arg1 & O_RDWR)

Should this be possible in gosecco? Would be useful to use the argument value on the right side of an expression to efficiently look for specific bit flags. In this case the filter would would permit the call iff arg1 has the bits for O_RDWR set.

user@subgraph:~/go/src/github.com/subgraph/oz$ grep open /home/user/go/src/github.com/subgraph/oz/profiles/hexchat-whitelist.seccomp open: arg1 == (arg1 & O_RDWR)

And then run with the filter installed:

user@subgraph:~/go/src/github.com/twtiger/gosecco/tester$ strace -fF -e trace=open $GOPATH/bin/oz-seccomp -mode whitelist -policy /home/user/go/src/github.com/subgraph/oz/profiles/hexchat-whitelist.seccomp /usr/bin/id --help >/tmp/ls.out < /var/lib/oz/cells.d/hexchat.json
[..] open("/proc/filesystems", O_RDONLY) = 3 [..]

This would be an important feature because we need to be able from time to time use the argument value as part of a macro expansion.

dma commented 8 years ago

I guess it goes without saying that the bpf code should be generated that stores (arg1 & O_RDWR) in another bpf register as a copy just for that evaluation, rather than modify the original, as the original could be needed for other evaluations, say if there are multiple expressions separated with ||.. It's getting late, pardon if I'm less comprehensible..

olabini commented 8 years ago

Yes, you should be able to do this. I'm not sure what is going on with it right now - give me a few secs.

olabini commented 8 years ago

Ah wait. I know why this happens. It has to do with the stupidity of the BPF stuff. Basically, arg's are 64bits but values in BPF can only be 32 bits. We do have some extra helpers for doing comparisons on full 64 bit values, but we do not allow arbitrary arithmetic on them, since the resulting code would usually not do what is expected. So instead you have to be explicit. In this case, you can do it using the separate high and low components of the argument, such as: open: argL1 == (argL1 & O_RDONLY) && argH1 == (argH1 & (O_RDONLY >> 32))

The second part is really not very useful, since we can only represent 32 bit constants in the language because of the limitations of the BPF processing.

You can find more info in the Arguments section here: https://github.com/twtiger/gosecco/blob/master/docs/seccomp-policy-language.md

dma commented 8 years ago

Ah yes, I remember chatting about that in the channel now. I used a bad example - the flags for open are discrete values, and not bit masks. FYI, originally I had used mprotect(2) as my example but I realized it was golang runtime calling mprotect before the seccomp filter was installed, so I quickly changed to open(2). This is what happens when you code/debug beyond 0h4:00 after coming home..

shw700 commented 8 years ago

Hi Ola,

Hopefully I'm understanding what's going on here correctly... my proposal would be to change the notation of bitwise operations. I understand that we are providing a C-like syntax, but I think in our particular case it would be ideal if the expression above could be written as:

open: arg1 |= O_RDONLY

Again, to somebody familiar with other programming languages, it might seem like this is an assignment operation - but you're writing your own syntax and you're not beholden to any such expectations.

I think in the overwhelming majority of cases we would only want to examine a bitmask against a register, and would almost never have the need to compare the result of that bitmask to any other value or register - so I really think this notation could work and keep the underlying 32-bit idiosyncrasies of BPF transparent in the process.

Please let me know what you think, and thanks for the fast turnaround so far!

olabini commented 8 years ago

Yeah, that kind of thing would be possible - although I don't think the pipe is the right symbol. And the equal sign will be tricky. What about doing something like arg1 &? O_RDONLY That should be easy and non-ambigous and basically expand to the comparison we are looking for.

olabini commented 8 years ago

So, I think what I just pushed makes this happen. I'm a bit wary of the edge conditions, but it will probably be fine! ;)

dma commented 8 years ago

This is really nice. Thanks Ola.

shw700 commented 8 years ago

Haven't had a chance to test yet, but looks like it should work great. Thank you, too!