This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: [RFC] Knobs to detect undefined behaviour.
- From: OndÅej BÃlka <neleai at seznam dot cz>
- To: Carlos O'Donell <carlos at redhat dot com>
- Cc: Jeff Law <law at redhat dot com>, Rich Felker <dalias at aerifal dot cx>, libc-alpha at sourceware dot org
- Date: Wed, 9 Oct 2013 20:58:09 +0200
- Subject: Re: [RFC] Knobs to detect undefined behaviour.
- Authentication-results: sourceware.org; auth=none
- References: <20131003122009 dot GA8891 at domone dot podge> <524DCA52 dot 2030609 at redhat dot com> <20131007141928 dot GV20515 at brightrain dot aerifal dot cx> <52542C63 dot 10305 at redhat dot com> <20131008162738 dot GG20515 at brightrain dot aerifal dot cx> <52545389 dot 6000901 at redhat dot com> <52545730 dot 6090306 at redhat dot com> <20131009072830 dot GA19974 at domone dot podge> <5255A175 dot 2000607 at redhat dot com>
On Wed, Oct 09, 2013 at 02:33:25PM -0400, Carlos O'Donell wrote:
> On 10/09/2013 03:28 AM, OndÅej BÃlka wrote:
> > On Tue, Oct 08, 2013 at 01:04:16PM -0600, Jeff Law wrote:
> >> When I first proposed the idea for these sanity checking dl-preload
> >> libraries for Fedora I envisioned that we could go beyond just
> >> checking for overlapping memory areas in the mem* and str*
> >> functions. There could be a set of pthread wrapper functions that
> >> check for whatever invariants we can in the pthread* functions
> >> without a huge performance hit.
> >>
> > It is possible but we could use a environment variable in libc as
> > alternative. Then we could choose detecting implementation by ifunc.
> >
> > Or add assert-like macro that will be compiled into libc_sanitized.so
> >
> > Main advantage is visibility. If user needs to find ten various libraries
> > and preload them few of users will do that. It should be moved to one place
> >
> > As we could add to manpages something like:
> >
> > A glibc could detect various undefined behaviours and abort when it is
> > detected. But it could break third party binaries so this needs to be
> > enabled manually. For checking use:
> >
> > GLIBC_SANITIZE=true program
> >
> >
> > There are various areas that could be covered:
> >
> > str/mem routines - could we merge memstomp?
> >
> > malloc - A efence got this almost right but tried to detect all
> > overruns.
> >
> > If we detect these only statisticaly it could be done only with using
> > twice more memory, for requests upto 4096 bytes we will use arena that
> > alternates between protected and usable pages like:
> > prot use prot use prot use prot
> > and for more than 4096 bytes we could use mmap.
> >
> > If we placed a 64-bit canary before/after each alloc we could also add
> > bounds checking by looking for that canary.
> >
> > What else?
>
> There is a lot we could talk about here.
>
> The linker already does process the environment variables so it could
> be done via env vars I think.
>
> We need to make forward progress on tunnables per our discussions
> at Cauldron 2013, which is to unify internally all the existing
> tunnables.
>
> We also need to make forward progress on env vars, which is to flesh
> out the policy and check to see if existing env vars match the
> policy and if not then talk about how we might clean things up.
>
> https://sourceware.org/glibc/wiki/TuningLibraryRuntimeBehavior
> https://sourceware.org/glibc/wiki/EnvVarGuide
>
> Cheers,
> Carlos.
I wrote a quick alternative for LD_PRELOAD. I added two performance
related warnings. First one is when user tries to concatenate strings in
strcat that leads to quadratic behavior.
A second is to warn if there are many large misaligned copies. I do not
have qualification of what means many yet.
Comments?
#include <stdlib.h>
#include <string.h>
#undef memcpy
void *
memcpy (void *dest, const void *src, size_t n)
{
static int misaligned = 0;
size_t i;
if (n > 4096 && (dest - src) % 16 && !misaligned)
{
log ("memcpy : Misaligned copy of size %i\n", n);
if (getenv ("ABORT_MISALIGNED"))
abort ();
misaligned = 1;
}
if (src - n < dest && dest < src + n)
{
fprintf (stderr, "memcpy - overlapping areas\n");
abort ();
}
__add_counter ();
memmove (dest, src, n); /* memmove is not overloaded */
return dest;
}
void *
mempcpy (void *dest, const void *src, size_t n)
{
return memcpy (dest, src, n) + n;
}
char *
strcpy (char *dest, const char *src)
{
size_t n = strlen (src) + 1;
return memcpy (dest, src, n);
}
char *
stpcpy (char *dest, const char *src)
{
size_t n = strlen (src) + 1;
return memcpy (dest, src, n) + n - 1;
}
char *
strncpy (char *dest, const char *src, size_t n)
{
if (src - n < dest && dest < src + n)
{
fprintf (stderr, "memcpy - overlapping areas");
abort ();
}
size_t len = strnlen (src, n);
memcpy (dest, src, len);
memset (dest + len, 0, n - len);
return dest;
}
char *
stpncpy (char *dest, const char *src, size_t n)
{
if (src - n < dest && dest < src + n)
{
fprintf (stderr, "memcpy - overlapping areas");
abort ();
}
size_t len = strnlen (src, n);
memcpy (dest, src, len);
memset (dest + len, 0, n - len);
return dest + len;
}
char *
strncat (char *dest, const char *src, size_t n)
{
static char *last; static int cnt; static int lastsize;
size_t len = strnlen (src, n);
char *p = dest + strlen (dest);
if (dest == last && len >= lastsize)
{
cnt++;
}
else
{
cnt = 0;
}
lastsize = len;
static int quad = 0;
if (cnt > 10 && !quad)
{
log ("str(n)cat : Detected likely quadratic loop.\n");
if (getenv ("ABORT_LOOP"))
abort ();
quad = 1;
}
if (src - len < dest && dest < p)
{
fprintf (stderr, "memcpy - overlapping areas");
abort ();
}
memcpy (p, src, len);
p[len] = 0;
return dest;
}
char *
strcat (char *dest, const char *src)
{
return strncat (dest, src, strlen (src));
}
void *
memccpy (void *dest, const void *src, int c, size_t n)
{
if (src - n < dest && dest < src + n)
{
fprintf (stderr, "memccpy - overlapping areas");
abort ();
}
void *p = memchr (src, c, n);
if (p != NULL)
return mempcpy (dest, src, p - src + 1);
memcpy (dest, src, n);
return NULL;
}