-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sys/linux: automatic syscall interface extraction #590
Comments
The Android Security research team is really interested in getting this feature off the ground. I'm going to start hacking on a tool that starts doing some of this using clang (so as to avoid dependence on other projects). In particular, we're going to start with # 1 as outlined above, specifically targeting Android source as a starting target, but attempt to build-in flexibility later for adding other operating systems. This particularly impacts target # 3 above, since how file operations, sockets, ioctls, etc are all done are definitely going to depend heavily on the OS. |
@mspecter This is great! Yes, it would be nice to separate common code and linux-specific code form day one. Ideally, OS-specific info is provided as some set of declarative rules. Whole syzkaller used to be heavily linux-specific throughout, and then it was painful to de-linuxify the codebase. |
As soon as you have something that converts a C struct to a basic syzkaller form, we can start merging. I think it's important to figure out and agree on user interfaces (how it will look for end users). For step 1, I am thinking about something along the lines of:
and this will print syzkaller descriptions from the header to stdout. For step 2, ultimately I would like to run:
and that will print set of warnings (we will figure out some story for false positive warnings). |
Another high-level question is how to split code between C++ and Go. |
Re: code split between Go and C++, I'd love some guidance. The way I was considering doing this was by writing a clangtool (much like the prototype you linked) to walk the AST since I generally don't trust the Go clang bindings, then leverage the code you already have in pkg/ast/ast.go and pkg/ast/format.go, along with a few Go->C++ bindings to actually create nodes and serialize them out; doing something like an AST->AST conversion. This has the benefit of relying less on a secondary generator of syzkaller syntax. So, if the project decides to add tokens or change what a token means, this part won't have to be modified as well. The downside appears to be a not-insignificant amount of added complexity. Thoughts? |
Also, I completely agree on the user interface, all of that makes sense. ^_^ |
Do you want to call Go from C++, or C++ from Go? It's possible but writing bindings can be painful, also it will be harder to build and test. The pure Go part will build and run trivially an can be tested, so the idea was to localize dependencies on clang to as small binary as possible (not sure if we will be able to tests it on travis CI). |
I've added descriptions of 7 main Linux kernel interfaces that I know of: |
I can't currently think of anything you're missing, and that list is incredibly helpful, thanks! |
A related functionality that may be easy to build on top is collecting set of functions reachable from syscalls. This would be useful to provide meaningful % of covered code in coverage reports. There are lots of functions that are not reachable from syscalls at all (interrupts, soft interrupts, init functions, rcu/timer callbacks, background threads, etc). If we calculate coverage % based on all code, the number will be too pessimistic and not meaningful and it won't be possible to get close to 100%. If we take only reachable functions as the base, then the % must be much more meaningful and optimistic. |
syz-check parses vmlinux dwarf, extracts struct descriptions, compares them with what we have (size, fields, alignment, etc) and produces .warn files. This is first raw version, it can be improved in a number of ways. But it already helped to identify a critical issue google#1542 and shows some wrong struct descriptions. Update google#590
syz-check parses vmlinux dwarf, extracts struct descriptions, compares them with what we have (size, fields, alignment, etc) and produces .warn files. This is first raw version, it can be improved in a number of ways. But it already helped to identify a critical issue #1542 and shows some wrong struct descriptions. Update #590
We assumed that for ConstType alignment is equal to size, which is perfectly reasonable for normal int8/16/32/64/ptr. However, padding is also represented by ConstType of arbitrary size, so if we added 157 bytes of padding that becomes alignment of the padding field and as the result of the whole struct. This affects very few structs, but quite radically and quite important structs. Discovered thanks to syz-check. Update #590
We used size as alignment, this is very wrong. Found thanks to syz-check. Update #590
Sweeping fix of everything up to socket_netlink_route.txt. Update #590
The only remaining part now is dev_video4linux.txt Update #590
Also rename some netfilter types to eliminate massive amounts of template warnings. Update #590
Turns out int64 alignment is 4 on 386... But on arm it's still 8. Another amusing finding thanks to syz-check. Update #590
Lots of interesting findings... Especially 2 byte uid/gid/pid. Update #590
They mostly duplicate the warnings we already have for amd64/386. But uncovered few very interesting local things (e.g. epoll_event is packed only on amd64, so arm/arm64 layout is wrong, but 386 is correct because int64 alignment is different). Update google#590
They mostly duplicate the warnings we already have for amd64/386. But uncovered few very interesting local things (e.g. epoll_event is packed only on amd64, so arm/arm64 layout is wrong, but 386 is correct because int64 alignment is different). Update #590
Overall idea of netlink checking. Currnetly we check netlink policies for common detectable mistakes. First, we detect what looks like a netlink policy in our descriptions (these are structs/unions only with nlattr/nlnext/nlnetw fields). Then we find corresponding symbols (offset/size) in vmlinux using nm. Then we read elf headers and locate where these symbols are in the rodata section. Then read in the symbol data, which is an array of nla_policy structs. These structs allow to easily figure out type/size of attributes. Finally we compare our descriptions with the kernel policy description. Update #590
1. Match policies that has a _suffix in our descriptions (we frequently do this to improve precision or avoid dup names). 2. Rename policies in descriptions to match kernel names. 3. Match policy if there are several such names in kernel. 4. Recognize policies with helper sub-policies. Update #590
As far as I understand most subsystems don't care about the nest flag, but some do. But marking them as nest won't harm (?). Let's mark all of them. Caught several cases where should have been used array[policy] but used just policy. Update #590
Handle NLA_BITFIELD32. Match string attribtues better. Calculate and check min size for varlen structs. Fix NLA_UNSPEC size check. Fix some things in descriptions. Update #590
Stop at the fist varlen field, but check the preceeding ones. Frequently the varlen array is the last field, so we should get good checking for these cases. Update #590
Thanks to syz-check for catching this. Update #590
One thing that may be useful and possible with smarter code analysis: detecting unused/reserved/padding fields (esp in structs, but may be direct syscall args too). These either appear unused in code, or only checked against 0. We should use |
FTR here is an interface extraction utility for NetBSD, can extract ioctl definitions: |
For fuchsia there is fidlgen: https://fuchsia.googlesource.com/fuchsia/+/4ad0d5717d65/tools/fidl/fidlgen_syzkaller/main.go |
For netlink we could consider extracting info from ynl project once it's merged upstream: |
FTR another paper on automatic interface extraction: |
https://github.com/seclab-ucr/SyzDescribe another paper which claims to be better than KSG |
One interesting addition to this is analysis of what interfaces are reachable on with different privileges (non-privileged, requires userns, root-only). This info can also be crossed with coverage reports to see e.g. what unpriv interfaces are not covered. |
This topic pops up periodically. Filing this as tracking bug.
We could use clang static analysis capabilities, or Coccinelle or Smatch. See also difuze which already does something similar using llvm bitcode. And also IMF: Inferred Model-based Fuzzer.
We probably should stick with clang.
Staged implementation plan should probably be:
Start with a tool that is given a header file and generates skeleton of syzkaller descriptions from it (structs with fields, enums as flags).
Extend the tool to read in existing description file and parse corresponding kernel headers and produce warnings about possible mismatches. E.g. descriptions have struct foo with 2 fields, but kernel headers have struct foo with 3 fields (or field sizes/alignment mismatch).
Locate all kernel interfaces (file_operations, sockets, netlink, filesystems, etc).
At this point we have 3 useful functionalities and some infrastructure code to parse kernel headers, extract some info from them and generate syzkaller descriptions from it. Then we can attack:
Automatically extract descriptions. Probably starting from some simple common cases.
Iterate on 4 to extract more descriptions and of higher quality.
A related functionality that may be easy to build on top is collecting set of functions reachable from syscalls. This would be useful to provide meaningful % of covered code in coverage reports. There are lots of functions that are not reachable from syscalls at all (interrupts, soft interrupts, init functions, rcu/timer callbacks, background threads, etc). If we calculate coverage % based on all code, the number will be too pessimistic and not meaningful and it won't be possible to get close to 100%. If we take only reachable functions as the base, then the % must be much more meaningful and optimistic.
Important aspects for interface auto-generation:
For linux the main interfaces are:
SYSCALL_DEFINE
macros, e.g.:struct file_operations
, e.g.:There can be associated with anon files returned by syscalls (timer_fd), or mounted to devfs, procfs, binfmt_misc and other special file systems.
struct proto_ops
, e.g.:Each set of operations is also associated with a specific socket family/type/protocol. In particular we need to understand sockaddr type used for this socket in connect/bind/sendto/etc.
As a special case, ioctl's and set/getsockopt's. These hang off file_operations and proto_ops. These usually contain a switch on commands and we need the switch cases and argument types.
File systems, denoted by
struct file_system_type
, e.g.:Each file system has set of options that is useful to understand, some require an image in particular format.
struct nla_policy
, e.g.:But some messages may also have no policy. There are 3 main levels that I know of: netlink as-is, rtnl, genetlink. All are useful to support.
struct xt_target
andstruct xt_match
, e.g.:One potential improvement: detect unused/reserved/padding fields (esp in structs, but may be direct syscall args too). These either appear unused in code, or only checked against 0 (to ensure reserved fields have 0 values). We should use const[0] in descriptions for these. Need to be careful to not confuse them with "bool" flags that are also only compared with 0.
The text was updated successfully, but these errors were encountered: