Stephen Kell via llvm-dev
2017-Apr-25 13:54 UTC
[llvm-dev] Contributing a new sanitizer for pointer casts
Hi all, Some of you might remember that at EuroLLVM last year in Barcelona, Chris Diamand and I gave a talk about Clang/libcrunch, a run-time checking system which can be thought of as another flavour of sanitizer. It checks pointer casts, using run-time type information. Roughly the check is that the pointer really points to an instance of the target type, though there are refinements to deal with various idioms violating that. <http://www.llvm.org/devmtg/2016-03/#presentation9> (I dropped a mention of this in the recent TBAA sanitizer thread, but consensus was that on balance it's a different enough tool to want both.) My current research funding has some room for tech transfer activity, so I've been spending some time on improving the code, with a hope of eventually contributing it to LLVM. This mail is just to get a handle on two questions: how much interest is there in this, and what changes are most important in order to get something contributable? The system is a bit complex, so let me give you an overview of how it currently works. (If you want full technical details, there are a couple of research papers you could read -- see the bottom.) - Instrumentation: this adds checks on (most) pointer casts, and in a few other places. It also does a little source-level analysis to dump information about allocation sites. We have both (my original) CIL and (Chris's) Clang/LLVM implementations of this. The Clang version is not too pretty at present: it uses -include'd inline helper functions written in C and shared with the CIL implementation. It also requires a bit of a hack to propagate certain type info (in uses of "sizeof") onwards to LLVM so it can be used in a data-flow analysis. - Hints from the programmer: these are necessary to declare allocation functions, besides standard ones (malloc etc.). This is currently done with an environment variable (LIBALLOCS_ALLOC_FNS) though I've thought of adding a command-line option too. These declarations have effect at both compile and link time. - Compiler wrapper and helper tools: currently a mixture of shell, Python and C++ helpers building on a pile of my own libraries (libdwarfpp, dwarfidl, liballocstool), for DWARF analysis and postprocessing. Roughly, these are responsible for generating and linking the run-time type information itself. - Type information. This is autogenerated uniqued / COMDAT'd instances of a moderately complex (but compact) C struct for each distinct data type. The model of type info (but not the representation) is somewhat DWARF-inspired. - Runtime. This is a preloadable shared library which does the dispatching of the checks. It also gets its hooks into various places to load type info as necessary, and to observe various kinds of allocation happening within the process. Again it builds on a pile of my other stuff (liballocs, which builds on trap-syscalls, mallochooks, libdlbind). Currently, my plan in a nutshell is to eliminate the C inline helpers in favour of fully IR-level instrumentation, and also eliminate the compiler wrapper in favour of a gold plugin (and maybe a bit of help in the clang driver). This should result in a contributable diff that adds a new sanitizer option (currently "-fsanitize=crunch", but name negotiable :-). Binaries built this way will also require the gold plugin and runtime (both out-of-tree) to do useful checking. I don't intend to port the runtime. Although in principle this could share code with the sanitizer runtimes, that's a lot of work and I don't have the resource to visit this right now... barring major rewrites, the runtime pretty much has to be GPL-licensed anyway, since it borrows code from glibc and Xen (for purposes I'm pretty sure are not covered by the sanitizer runtimes). So my questions for you are whether this contribution would be welcome, and in particular any red lines about how to do instrumentation, how to factor everything, and how to deal with the external dependencies. As I currently envisage things, the gold plugin must live out-of-tree since it will require my libraries to build; I don't believe equivalent library support exists within LLVM. This being out-of-tree seems not a huge loss given that the runtime also will be. Oh, and runtime support exists for x86-64/Linux only at the moment, though there is a bit of code for FreeBSD. For the interested, here are the research papers I mentioned. "Dynamically diagnosing run-time type errors in unsafe code" (OOPSLA '16) http://www.cl.cam.ac.uk/~srk31/#oopsla16a "Towards a dynamic object model within Unix processes" (Onward! '15) http://www.cl.cam.ac.uk/~srk31/#onward15 Code: <https://github.com/stephenrkell/liballocs> <https://github.com/stephenrkell/libcrunch> <https://github.com/stephenrkell/clangcrunch>. All thoughts appreciated... let me know if you see any obstacles to contribution, or if you're able to help, or just if you have questions. Much obliged, Stephen.
Vedant Kumar via llvm-dev
2017-Apr-25 18:50 UTC
[llvm-dev] Contributing a new sanitizer for pointer casts
Hi Stephen, I enjoyed reading through your EuroLLVM slides and OOPSLA paper. Detecting the creation of contract-violating pointers is an interesting idea, and your paper demonstrates that the checking can be comprehensive and effective. However, I have concerns about the quality of diagnostics, the complexity of the driver, and about maintaining an out-of-tree runtime. First, I'm concerned about libcrunch diagnostics which are morally correct, but which do not point out imminent correctness problems (for convenience I'll refer to these as false positives). You address this in the "Accommodating Sloppy C" section of your paper with the __like_a() runtime check. In my experience, users are reluctant to deploy the type of suppressions described here, and are more likely to abandon the sanitizer instead. What has your experience been with FPs? The results from your SPEC testing look promising, but I'm curious about any FP-related feedback you've gotten from other developers, or about the results of a libcrunch-enabled world build of FreeBSD. I've certainly seen code at Apple which would fail libcrunch's "is-a" checks, but which "works fine" according to the code owners. Would you be open to reducing the number of cases libcrunch is able to diagnose in order to reduce FP rates? Second, do you see any way to simplify the wrappers required to invoke libcrunch? E.g is the runtime preload step necessary? Apart from the runtime shared object, can the other tools required to use libcrunch be consolidated into the clang binary? Third, how do you plan on keeping the libcrunch integration into clang in-sync with the runtime, if the runtime is to live outside of the llvm tree? How will the feature be tested? Currently, the sanitizer runtimes ship with official LLVM downloads: what would the deployment story for libcrunch be (how would users get it)? I don't mean any of this as a "do not proceed" message. These are simply some of the issues which I think are worth discussing early on. best, vedant> On Apr 25, 2017, at 6:54 AM, Stephen Kell via llvm-dev <llvm-dev at lists.llvm.org> wrote: > > Hi all, > > Some of you might remember that at EuroLLVM last year in Barcelona, > Chris Diamand and I gave a talk about Clang/libcrunch, a run-time > checking system which can be thought of as another flavour of sanitizer. > It checks pointer casts, using run-time type information. Roughly the > check is that the pointer really points to an instance of the target > type, though there are refinements to deal with various idioms violating > that. <http://www.llvm.org/devmtg/2016-03/#presentation9> > > (I dropped a mention of this in the recent TBAA sanitizer thread, but > consensus was that on balance it's a different enough tool to want > both.) > > My current research funding has some room for tech transfer activity, so > I've been spending some time on improving the code, with a hope of > eventually contributing it to LLVM. > > This mail is just to get a handle on two questions: how much interest is > there in this, and what changes are most important in order to get > something contributable? > > The system is a bit complex, so let me give you an overview of how it > currently works. (If you want full technical details, there are a couple > of research papers you could read -- see the bottom.) > > - Instrumentation: this adds checks on (most) pointer casts, and in a > few other places. It also does a little source-level analysis to dump > information about allocation sites. We have both (my original) CIL and > (Chris's) Clang/LLVM implementations of this. The Clang version is not > too pretty at present: it uses -include'd inline helper functions > written in C and shared with the CIL implementation. It also requires a > bit of a hack to propagate certain type info (in uses of "sizeof") > onwards to LLVM so it can be used in a data-flow analysis. > > - Hints from the programmer: these are necessary to declare allocation > functions, besides standard ones (malloc etc.). This is currently done > with an environment variable (LIBALLOCS_ALLOC_FNS) though I've thought > of adding a command-line option too. These declarations have effect at > both compile and link time. > > - Compiler wrapper and helper tools: currently a mixture of shell, > Python and C++ helpers building on a pile of my own libraries > (libdwarfpp, dwarfidl, liballocstool), for DWARF analysis and > postprocessing. Roughly, these are responsible for generating and > linking the run-time type information itself. > > - Type information. This is autogenerated uniqued / COMDAT'd instances > of a moderately complex (but compact) C struct for each distinct data > type. The model of type info (but not the representation) is somewhat > DWARF-inspired. > > - Runtime. This is a preloadable shared library which does the > dispatching of the checks. It also gets its hooks into various places to > load type info as necessary, and to observe various kinds of allocation > happening within the process. Again it builds on a pile of my other > stuff (liballocs, which builds on trap-syscalls, mallochooks, > libdlbind). > > Currently, my plan in a nutshell is to eliminate the C inline helpers in > favour of fully IR-level instrumentation, and also eliminate the > compiler wrapper in favour of a gold plugin (and maybe a bit of help in > the clang driver). This should result in a contributable diff that adds > a new sanitizer option (currently "-fsanitize=crunch", but name > negotiable :-). Binaries built this way will also require the gold > plugin and runtime (both out-of-tree) to do useful checking. > > I don't intend to port the runtime. Although in principle this could > share code with the sanitizer runtimes, that's a lot of work and I don't > have the resource to visit this right now... barring major rewrites, the > runtime pretty much has to be GPL-licensed anyway, since it borrows code > from glibc and Xen (for purposes I'm pretty sure are not covered by the > sanitizer runtimes). > > So my questions for you are whether this contribution would be welcome, > and in particular any red lines about how to do instrumentation, how to > factor everything, and how to deal with the external dependencies. As I > currently envisage things, the gold plugin must live out-of-tree since > it will require my libraries to build; I don't believe equivalent > library support exists within LLVM. This being out-of-tree seems not a > huge loss given that the runtime also will be. > > Oh, and runtime support exists for x86-64/Linux only at the moment, > though there is a bit of code for FreeBSD. > > For the interested, here are the research papers I mentioned. > > "Dynamically diagnosing run-time type errors in unsafe code" (OOPSLA '16) > http://www.cl.cam.ac.uk/~srk31/#oopsla16a > > "Towards a dynamic object model within Unix processes" (Onward! '15) > http://www.cl.cam.ac.uk/~srk31/#onward15 > > Code: <https://github.com/stephenrkell/liballocs> > <https://github.com/stephenrkell/libcrunch> > <https://github.com/stephenrkell/clangcrunch>. > > All thoughts appreciated... let me know if you see any obstacles to > contribution, or if you're able to help, or just if you have questions. > Much obliged, > > Stephen. > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Stephen Kell via llvm-dev
2017-Apr-26 10:58 UTC
[llvm-dev] Contributing a new sanitizer for pointer casts
Hi Vedant,> I enjoyed reading through your EuroLLVM slides and OOPSLA paper. > Detecting the creation of contract-violating pointers is an > interesting idea, and your paper demonstrates that the checking can > be comprehensive and effective.Glad you enjoyed them. :-)> However, I have concerns about the quality of diagnostics, the > complexity of the driver, and about maintaining an out-of-tree > runtime. > > First, I'm concerned about libcrunch diagnostics which are morally > correct, but which do not point out imminent correctness problems > (for convenience I'll refer to these as false positives). You address > this in the "Accommodating Sloppy C" section of your paper with the > __like_a() runtime check. In my experience, users are reluctant to > deploy the type of suppressions described here, and are more likely > to abandon the sanitizer instead. What has your experience been with > FPs? The results from your SPEC testing look promising, but I'm > curious about any FP-related feedback you've gotten from other > developers, or about the results of a libcrunch-enabled world build > of FreeBSD.Great question. The truth is that I don't yet have enough experience to answer this, and part of the point of contributing this to LLVM would be to get more people to try it out and contribute improvements. However, I would say that I am already pleasantly surprised with how on-point the warnings are... although there are false positives, they tend to be about genuinely nasty/naughty code. I take your point about the danger that "people would turn it off". As an aside -- more below -- one thing I've worked quite hard on is to ensure that you don't need to rebuild to turn it off, which at least prevents a hard exit. Beyond that, there is quite a long tail of mitigation measures against false positives, including some not mentioned in the paper (and, er, some not implemented). The big one, and one that isn't in the paper, is "trap pointers" -- I outlined this in response to Hal in the TBAA sanitizer thread. In short, it delays warnings from the time of cast to the time of use. I only implemented this later, to support bounds checking rather than cast checking, but it applies to the latter too. (Sneak preview: I'm working on bounds checker I believe will offer a different, possibly better, precision/performance trade-off than ASan, but that's still in progress.) Another possible mitigation is to use "__like_a" by default in some circumstances, perhaps helped by an up-front static analysis pass to find things like structure prefixing. That's pure idea-stuff right now. Doing a build-world of FreeBSD or similar, as you suggest, is exactly the sort of experience that is required, and inevitably, in doing so there will be a lot of handle-turning to fix bugs and the like. I'd rather go through this on an implementation that has a chance of getting adopted... so I think the LLVM port has to happen first, though I understand that the contribution per se may have to wait.> I've certainly seen code at Apple which would fail > libcrunch's "is-a" checks, but which "works fine" according to the > code owners. Would you be open to reducing the number of cases > libcrunch is able to diagnose in order to reduce FP rates?I'm curious -- can you give me a sketch of what this code is doing? If it's just a case of "creating wrong-typed pointers that aren't deref'd", then I think we're good. In general I'd rather fix the FPs precisely, rather than introduce underapproximation, and I've yet to see cases where that really can't be done.> Second, do you see any way to simplify the wrappers required to > invoke libcrunch? E.g is the runtime preload step necessary? Apart > from the runtime shared object, can the other tools required to use > libcrunch be consolidated into the clang binary?Great question. The plan is to consolidate the tools, yes -- but probably mostly into the gold plugin. Only a couple of steps work on the individual compilation units, and they can be deferred until link time anyhow. It does mean that each compilation unit's allocation site information has to get dumped somewhere, to be picked up at link time. This is currently in a .i.allocs text file, but that is a bit untidy... I've thought about a DWARF extension (DW_TAG_allocation_site or similar) instead, which would be cleaner. About preloading -- I happen to like this design, modulo the clunky invocation, because it means you can easily turn the checking on or off. For executables, you could simply link the preload stuff in, but that would lose the on/off switch, and doesn't work for shared libraries. Another idea I've had is to use a customised loader (ld.so) instead of a preload library. So you would do $ ./myprogram # runs normally or $ /path/to/crunchld.so ./myprogram # runs +crunch This would more-or-less replicate the usage model of Valgrind tools. (In general, my feeling is that extending the ld.so is more sane than using LD_PRELOAD, although I realise it will seem a bit off-the-wall.)> Third, how do you plan on keeping the libcrunch integration into > clang in-sync with the runtime, if the runtime is to live outside of > the llvm tree?I think that part is no problem. The interface between instrumented code and the runtime is pretty narrow (basically just the __is_a(), __like_a() and similar functions) and does not embed any LLVM stuff, so it would be stable under LLVM churn. The same would hold for the gold plugin... LLVM/clang's involvement is mostly just to do the instrumentation, which is a pretty well-isolated task. The allocation site metadata I just mentioned is probably the only change-prone thing. Of course, there is a problem of breakage/churn in the libcrunch/liballocs stuff... in particular, things like new DWARF features can break things. I have no good answer to that, except that more hands make light(er) work.> How will the feature be tested?On that, I have no idea, and I'd have to take your lead on that. To make an analogy with another out-of-tree dependency, how do you test LTO against GNU binutils? I'm hoping, perhaps naively, that it could work similarly.> Currently, the > sanitizer runtimes ship with official LLVM downloads: what would the > deployment story for libcrunch be (how would users get it)?Initially they would build the libcrunch tree, which would build for them the gold plugin and runtime. Again, I think it would be rather like the LTO story (a web page with "follow these instructions"). And of course distributors could build binary packages... again, it helps that the interface between compiler and plugin/runtime is fairly simple. (Full disclosure: currently you have to build both the liballocs tree and the libcrunch tree, separately. Eventually the former will be a submodule of the latter. I have too many levels of submodule going on....)> I don't mean any of this as a "do not proceed" message. These are > simply some of the issues which I think are worth discussing early on.Of course. :-) Thanks for the detailed comments! Hoping my answers make sense, and happy to follow up, Stephen.