tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Auxiliary header and macros for sanitizers in userland programs

On 05.06.2018 20:47, Valery Ushakov wrote:
> Kamil Rytarowski <> wrote:
>> On 05.06.2018 18:14, Valery Ushakov wrote:
>>> Kamil Rytarowski <> wrote:
>>>> We've faced a problem with sanitizing part of the NetBSD userland, as we
>>>> need to use helper functions to make sanitization possible in some
>>>> narrow cases that aren't clear for sanitizers.
>>>> The current problem is the usage of callback functions defined in
>>>> programs and executed from the internals of libc.
>>> [...] 
>>>> Once a callback function is executed from the internals of libc, a
>>>> sanitized program does not know whether the arguments passed to it are
>>>> properly initialized.
>>> Why?  What makes calling from libc special?  It's probably obvious to
>>> you since you've been workign on this for a while, but most of us have
>>> no clue.
> [...]
>> In the fts_open(3) case, there is performed allocation of FTSENT
>> entries inside libc and this buffer is passed to the callback
>> function without prior notifying the user of fts_open(3) about these
>> elements (their address and size of initialized buffer).  MSan does
>> not know whether the passed arguments to the arguments of the
>> callback are initialized or not.
> So the issue is that libc is compiled without sanitizer and
> allocations done inside libc are not known to a sanitizer?  For libc
> functions that return allocated memory I guess you mark it in the
> sanitizer's interposed wrapper ("interceptor"?), but in the case of
> callbacks there is no interceptor between libc and the callback to do
> that.  Is that about right?
> -uwe

libc is compiled without sanitizer. This is the design of sanitizers
that libc, libm, libpthread and librt (+ libdl for Linux) are treated
mostly as blackbox libraries. Sanitizers mostly want to look at them
from high level API and install interceptors for their public symbols -
unless a symbol is out of interest (most of them aren't interesting, the
interesting ones are mostly the ones that pass or receive anything over
a pointer).

If someone intends to find bugs in libc, the proper tool is Valgrind.
This means that sanitizers are not a replacement for Valgrind, but a
different tool, however both can detect similar bugs.

Actually sanitizers might know about part of allocations inside libc
(malloc(3) interceptor, mmap(2) interceptor), and can know about some
initialization inside libc.. but in general the sanitizers have no
information what happens inside libc, treating it as a blackbox.

Every sanitizer behaves differently and is optimized for its purpose to
detect leaks, unitialized memory, out of bounds access, threading issues
etc - some of them might disable e.g. recursive interceptors, others
not. The most senstive one is Memory Sanitizer and we are now using it
to detect integration bugs, we can already chroot into a functional
memory sanitized userland.. however the set of programs is still restricted.

As a part of sanitization of userland there is a process of building all
other libraries with a dedicated sanitizer, as e.g. editline(3) can call
a sigaction(3) internally, with initialized arguments using inlined code.

Interceptos mostly have rules of type PreRead/PostRead and
PreWrite/PostWrite arguments passed to functions in libc (pthread, ..).
In the MSan case during PreWrite there is a check whether arguments
passed to a function are properly initialized, and in PostRead phase
mark the buffers as initialized.

In the fts_open(3) case there is no stage between the time of being
aware about initialzed (not just allocated) FTSENT buffers and executing
callback function that already needs this information. In this case,
there is need to help to Memory Sanitizer with explicit __msan_unpoison().

There are similar cases when someone is using syscall(2) directly. There
are helper macros and functions to make usage of syscall(2)-like API
easier, e.g. for write(2):

__sanitizer_syscall_pre_write(fd, buf, nbyte)
res = write(fd, buf, nbyte)
__sanitizer_syscall_post_write(res, fd, buf, nbyte)

There are also other helper functions for other sanitizers, but so far
nothing else was detected to be required.

I don't want to keep redefining preprocessor ifdefs in every base
userland program (unless in external/), so I've exported the ifdefing
switches to a dedicated header.

Attachment: signature.asc
Description: OpenPGP digital signature

Home | Main Index | Thread Index | Old Index