LibreSSL regression: explicit_bzero failing

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

LibreSSL regression: explicit_bzero failing

Benny Baumann
Dear OpenBSD folks,

I've been following the development of LibReSSL and when testing
LibReSSL (the portable releases) on my Linux box (Lubuntu 14.04, Linux
Kernel 3.16, GCC 4.9) I noticed the following (kinda) odd behaviour:

I'm compiling LibReSSL with the following command lines:
automake
CFLAGS="-O3 -flto -fsanitize=address -Wall -march=native -mtune=native
-g -ggdb3" LDFLAGS="$CFLAGS" ./configure
make clean
make all
make check

On LibReSSL 2.0.2:
Everything works, tests run without problems, everything is fine.

On LibReSSL 2.0.3 AND 2.0.4:
Everything fine EXCEPT the test for explicit_bzero failing with an
address sanitizing error in memmem (when looking for the secret in the
buffer on line 132.

The error report by ASan is as follows:
=================================================================
==27814== ERROR: AddressSanitizer: stack-buffer-underflow on address
0x000000605fe0 at pc 0x401587 bp 0x605fa0 sp 0x605f98
READ of size 1 at 0x000000605fe0 thread T0
    #0 0x401586
(/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401586)
    #1 0x401661
(/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401661)
    #2 0x7fd8678e0fff (/lib/x86_64-linux-gnu/libc-2.19.so+0x36fff)
    #3 0x7fd8678e12e5 (/lib/x86_64-linux-gnu/libc-2.19.so+0x372e5)
    #4 0x401cba
(/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401cba)
    #5 0x400f32
(/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x400f32)
    #6 0x7fd8678cbec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
    #7 0x401044
(/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401044)
0x000000605fe0 is located 6560 bytes inside of global variable 'altstack
(/tmp/ccJge4UH.ltrans0.o)' (0x604640) of size 9216
Shadow bytes around the buggy address:
  0x0000800b8ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0000800b8bf0: 00 00 00 00 00 00 00 00 00 00 00 00[f1]f1 f1 f1
  0x0000800b8c00: 00 00 00 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00
  0x0000800b8c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800b8c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==27814== ABORTING

The most interesting part here is that LibReSSL 2.0.2 used the exact
same code for the testcase but works. Thus I'm more likely suspecting a
GCC or ASan bug here, but you never know ...

There's also another interesting message while linking, but IMHO that
seems unrelated:
---
  CCLD     libcrypto.la
bio/.libs/libcrypto_la-b_dump.o (symbol from plugin): warning: memset
used with constant zero length parameter; this could be due to
transposed parameters
---
No such warning is issued  for BIO_dump (b_dump.c) and following the
code paths there getting the length to zero does not seem possible based
on the source.

Kind regards,
BenBE.

[demime 1.01d removed an attachment of type application/pgp-signature which had a name of signature.asc]

Reply | Threaded
Open this post in threaded view
|

Re: LibreSSL regression: explicit_bzero failing

Brent Cook
On Aug 6, 2014, at 2:57 AM, Benny Baumann <[hidden email]> wrote:

> Dear OpenBSD folks,
>
> I've been following the development of LibReSSL and when testing
> LibReSSL (the portable releases) on my Linux box (Lubuntu 14.04, Linux
> Kernel 3.16, GCC 4.9) I noticed the following (kinda) odd behaviour:
>
> I'm compiling LibReSSL with the following command lines:
> automake
> CFLAGS="-O3 -flto -fsanitize=address -Wall -march=native -mtune=native
> -g -ggdb3" LDFLAGS="$CFLAGS" ./configure
> make clean
> make all
> make check
>
> On LibReSSL 2.0.2:
> Everything works, tests run without problems, everything is fine.
>
> On LibReSSL 2.0.3 AND 2.0.4:
> Everything fine EXCEPT the test for explicit_bzero failing with an
> address sanitizing error in memmem (when looking for the secret in the
> buffer on line 132.

Hi Benny,

gcc is reserving some bytes at the beginning of the stack buffer, and
the test's scan is triggering this warning when the test scans the
signal stack for the secret bytes. Both the test and the gcc check are
working as expected, but obviously butt heads.

We should probably not assume all bytes of the signal stack buffer are
accessible while we are on it.

Matthew, does a change like below preserve the original intent of the
test?

--- a/src/regress/lib/libc/explicit_bzero/explicit_bzero.c
+++ b/src/regress/lib/libc/explicit_bzero/explicit_bzero.c
@@ -129,7 +129,7 @@ test_without_bzero()
        char buf[SECRETBYTES];
        assert_on_stack();
        populate_secret(buf, sizeof(buf));
-       char *res = memmem(altstack, sizeof(altstack), buf, sizeof(buf));
+       char *res = memmem(buf, sizeof(buf), secret, sizeof(secret));
        ASSERT_NE(NULL, res);
        return (res);
 }
@@ -140,7 +140,7 @@ test_with_bzero()
        char buf[SECRETBYTES];
        assert_on_stack();
        populate_secret(buf, sizeof(buf));
-       char *res = memmem(altstack, sizeof(altstack), buf, sizeof(buf));
+       char *res = memmem(buf, sizeof(buf), secret, sizeof(secret));
        ASSERT_NE(NULL, res);
        explicit_bzero(buf, sizeof(buf));
        return (res);

> The error report by ASan is as follows:
> =================================================================
> ==27814== ERROR: AddressSanitizer: stack-buffer-underflow on address
> 0x000000605fe0 at pc 0x401587 bp 0x605fa0 sp 0x605f98
> READ of size 1 at 0x000000605fe0 thread T0
>    #0 0x401586
> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401586)
>    #1 0x401661
> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401661)
>    #2 0x7fd8678e0fff (/lib/x86_64-linux-gnu/libc-2.19.so+0x36fff)
>    #3 0x7fd8678e12e5 (/lib/x86_64-linux-gnu/libc-2.19.so+0x372e5)
>    #4 0x401cba
> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401cba)
>    #5 0x400f32
> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x400f32)
>    #6 0x7fd8678cbec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
>    #7 0x401044
> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401044)
> 0x000000605fe0 is located 6560 bytes inside of global variable 'altstack
> (/tmp/ccJge4UH.ltrans0.o)' (0x604640) of size 9216
> Shadow bytes around the buggy address:
>  0x0000800b8ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  0x0000800b8bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  0x0000800b8bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  0x0000800b8bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  0x0000800b8be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> =>0x0000800b8bf0: 00 00 00 00 00 00 00 00 00 00 00 00[f1]f1 f1 f1
>  0x0000800b8c00: 00 00 00 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00
>  0x0000800b8c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  0x0000800b8c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  0x0000800b8c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>  0x0000800b8c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> Shadow byte legend (one shadow byte represents 8 application bytes):
>  Addressable:           00
>  Partially addressable: 01 02 03 04 05 06 07
>  Heap left redzone:     fa
>  Heap righ redzone:     fb
>  Freed Heap region:     fd
>  Stack left redzone:    f1
>  Stack mid redzone:     f2
>  Stack right redzone:   f3
>  Stack partial redzone: f4
>  Stack after return:    f5
>  Stack use after scope: f8
>  Global redzone:        f9
>  Global init order:     f6
>  Poisoned by user:      f7
>  ASan internal:         fe
> ==27814== ABORTING
>
> The most interesting part here is that LibReSSL 2.0.2 used the exact
> same code for the testcase but works. Thus I'm more likely suspecting a
> GCC or ASan bug here, but you never know ...
>
> There's also another interesting message while linking, but IMHO that
> seems unrelated:
> ---
>  CCLD     libcrypto.la
> bio/.libs/libcrypto_la-b_dump.o (symbol from plugin): warning: memset
> used with constant zero length parameter; this could be due to
> transposed parameters

gcc and lto allow for constant propagation to create specialized versions
of functions with constant arguments. In this case, it is the call to:

BIO_dump_indent_cb(cb, u, s, len, 0);

that is the culprit. This is not a bug necessarily, but gcc could create
slightly more optimal code if we guarded the memset with an 'if (indent)'

> ---
> No such warning is issued  for BIO_dump (b_dump.c) and following the
> code paths there getting the length to zero does not seem possible based
> on the source.
>
> Kind regards,
> BenBE.
>
> [demime 1.01d removed an attachment of type application/pgp-signature which had a name of signature.asc]

Reply | Threaded
Open this post in threaded view
|

Re: LibreSSL regression: explicit_bzero failing

Matthew Dempsky-3
On Wed, Aug 6, 2014 at 6:00 AM, Brent Cook <[hidden email]> wrote:
> Matthew, does a change like below preserve the original intent of the
> test?

No, this change causes undefined behavior using a pointer to an object
past when its lifetime ended, which is specifically what
explicit_bzero.c is written to avoid by using signal handlers and the
altstack.

I'm pretty sure this is just a false positive from address sanitizer
due to it not understanding alternate stacks.

> --- a/src/regress/lib/libc/explicit_bzero/explicit_bzero.c
> +++ b/src/regress/lib/libc/explicit_bzero/explicit_bzero.c
> @@ -129,7 +129,7 @@ test_without_bzero()
>         char buf[SECRETBYTES];
>         assert_on_stack();
>         populate_secret(buf, sizeof(buf));
> -       char *res = memmem(altstack, sizeof(altstack), buf, sizeof(buf));
> +       char *res = memmem(buf, sizeof(buf), secret, sizeof(secret));
>         ASSERT_NE(NULL, res);
>         return (res);
>  }
> @@ -140,7 +140,7 @@ test_with_bzero()
>         char buf[SECRETBYTES];
>         assert_on_stack();
>         populate_secret(buf, sizeof(buf));
> -       char *res = memmem(altstack, sizeof(altstack), buf, sizeof(buf));
> +       char *res = memmem(buf, sizeof(buf), secret, sizeof(secret));
>         ASSERT_NE(NULL, res);
>         explicit_bzero(buf, sizeof(buf));
>         return (res);
>
>> The error report by ASan is as follows:
>> =================================================================
>> ==27814== ERROR: AddressSanitizer: stack-buffer-underflow on address
>> 0x000000605fe0 at pc 0x401587 bp 0x605fa0 sp 0x605f98
>> READ of size 1 at 0x000000605fe0 thread T0
>>    #0 0x401586
>> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401586)
>>    #1 0x401661
>> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401661)
>>    #2 0x7fd8678e0fff (/lib/x86_64-linux-gnu/libc-2.19.so+0x36fff)
>>    #3 0x7fd8678e12e5 (/lib/x86_64-linux-gnu/libc-2.19.so+0x372e5)
>>    #4 0x401cba
>> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401cba)
>>    #5 0x400f32
>> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x400f32)
>>    #6 0x7fd8678cbec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
>>    #7 0x401044
>> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401044)
>> 0x000000605fe0 is located 6560 bytes inside of global variable 'altstack
>> (/tmp/ccJge4UH.ltrans0.o)' (0x604640) of size 9216
>> Shadow bytes around the buggy address:
>>  0x0000800b8ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>  0x0000800b8bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>  0x0000800b8bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>  0x0000800b8bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>  0x0000800b8be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> =>0x0000800b8bf0: 00 00 00 00 00 00 00 00 00 00 00 00[f1]f1 f1 f1
>>  0x0000800b8c00: 00 00 00 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00
>>  0x0000800b8c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>  0x0000800b8c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>  0x0000800b8c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>  0x0000800b8c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>> Shadow byte legend (one shadow byte represents 8 application bytes):
>>  Addressable:           00
>>  Partially addressable: 01 02 03 04 05 06 07
>>  Heap left redzone:     fa
>>  Heap righ redzone:     fb
>>  Freed Heap region:     fd
>>  Stack left redzone:    f1
>>  Stack mid redzone:     f2
>>  Stack right redzone:   f3
>>  Stack partial redzone: f4
>>  Stack after return:    f5
>>  Stack use after scope: f8
>>  Global redzone:        f9
>>  Global init order:     f6
>>  Poisoned by user:      f7
>>  ASan internal:         fe
>> ==27814== ABORTING
>>
>> The most interesting part here is that LibReSSL 2.0.2 used the exact
>> same code for the testcase but works. Thus I'm more likely suspecting a
>> GCC or ASan bug here, but you never know ...

Reply | Threaded
Open this post in threaded view
|

Re: LibreSSL regression: explicit_bzero failing

Brent Cook
On Wed, Aug 6, 2014 at 10:37 AM, Matthew Dempsky <[hidden email]>
wrote:

> On Wed, Aug 6, 2014 at 6:00 AM, Brent Cook <[hidden email]> wrote:
> > Matthew, does a change like below preserve the original intent of the
> > test?
>
> No, this change causes undefined behavior using a pointer to an object
> past when its lifetime ended, which is specifically what
> explicit_bzero.c is written to avoid by using signal handlers and the
> altstack.
>
> I'm pretty sure this is just a false positive from address sanitizer
> due to it not understanding alternate stacks.
>
>
Which object is used past its lifetime? buf is local to the function, and
secret is a static global.

Enabling the stack protector code really does shift buf by 112 bytes on the
signal stack, so I think its more likely working and inserting some guards
at the head and tail of the stack signal stack.

Sorry in advance if I'm being dense here and not seeing the problem. I do
wonder why folks are so hell bent on enabling LTO when there are quite a
few lower-hanging fruits for -portable optimization :)


> > --- a/src/regress/lib/libc/explicit_bzero/explicit_bzero.c
> > +++ b/src/regress/lib/libc/explicit_bzero/explicit_bzero.c
> > @@ -129,7 +129,7 @@ test_without_bzero()
> >         char buf[SECRETBYTES];
> >         assert_on_stack();
> >         populate_secret(buf, sizeof(buf));
> > -       char *res = memmem(altstack, sizeof(altstack), buf, sizeof(buf));
> > +       char *res = memmem(buf, sizeof(buf), secret, sizeof(secret));
> >         ASSERT_NE(NULL, res);
> >         return (res);
> >  }
> > @@ -140,7 +140,7 @@ test_with_bzero()
> >         char buf[SECRETBYTES];
> >         assert_on_stack();
> >         populate_secret(buf, sizeof(buf));
> > -       char *res = memmem(altstack, sizeof(altstack), buf, sizeof(buf));
> > +       char *res = memmem(buf, sizeof(buf), secret, sizeof(secret));
> >         ASSERT_NE(NULL, res);
> >         explicit_bzero(buf, sizeof(buf));
> >         return (res);
> >
> >> The error report by ASan is as follows:
> >> =================================================================
> >> ==27814== ERROR: AddressSanitizer: stack-buffer-underflow on address
> >> 0x000000605fe0 at pc 0x401587 bp 0x605fa0 sp 0x605f98
> >> READ of size 1 at 0x000000605fe0 thread T0
> >>    #0 0x401586
> >> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401586)
> >>    #1 0x401661
> >> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401661)
> >>    #2 0x7fd8678e0fff (/lib/x86_64-linux-gnu/libc-2.19.so+0x36fff)
> >>    #3 0x7fd8678e12e5 (/lib/x86_64-linux-gnu/libc-2.19.so+0x372e5)
> >>    #4 0x401cba
> >> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401cba)
> >>    #5 0x400f32
> >> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x400f32)
> >>    #6 0x7fd8678cbec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
> >>    #7 0x401044
> >> (/usr/src/libressl-2.0.4/tests/.libs/lt-explicit_bzero+0x401044)
> >> 0x000000605fe0 is located 6560 bytes inside of global variable 'altstack
> >> (/tmp/ccJge4UH.ltrans0.o)' (0x604640) of size 9216
> >> Shadow bytes around the buggy address:
> >>  0x0000800b8ba0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >>  0x0000800b8bb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >>  0x0000800b8bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >>  0x0000800b8bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >>  0x0000800b8be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >> =>0x0000800b8bf0: 00 00 00 00 00 00 00 00 00 00 00 00[f1]f1 f1 f1
> >>  0x0000800b8c00: 00 00 00 f4 f2 f2 f2 f2 00 00 00 00 00 00 00 00
> >>  0x0000800b8c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >>  0x0000800b8c20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >>  0x0000800b8c30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >>  0x0000800b8c40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >> Shadow byte legend (one shadow byte represents 8 application bytes):
> >>  Addressable:           00
> >>  Partially addressable: 01 02 03 04 05 06 07
> >>  Heap left redzone:     fa
> >>  Heap righ redzone:     fb
> >>  Freed Heap region:     fd
> >>  Stack left redzone:    f1
> >>  Stack mid redzone:     f2
> >>  Stack right redzone:   f3
> >>  Stack partial redzone: f4
> >>  Stack after return:    f5
> >>  Stack use after scope: f8
> >>  Global redzone:        f9
> >>  Global init order:     f6
> >>  Poisoned by user:      f7
> >>  ASan internal:         fe
> >> ==27814== ABORTING
> >>
> >> The most interesting part here is that LibReSSL 2.0.2 used the exact
> >> same code for the testcase but works. Thus I'm more likely suspecting a
> >> GCC or ASan bug here, but you never know ...

Reply | Threaded
Open this post in threaded view
|

Re: LibreSSL regression: explicit_bzero failing

Matthew Dempsky-3
On Sat, Aug 9, 2014 at 4:12 PM, Brent Cook <[hidden email]> wrote:
> Which object is used past its lifetime? buf is local to the function, and
> secret is a static global.

That's exactly the issue: buf is local.

Conceptually, to make sure explicit_bzero() is working, we want to
write a test like this:

    int *test() {
        int buf = 42;
        explicit_bzero(&buf, sizeof(buf));
        return &buf;
    }

    void check() {
         int *p = test();
         assert(*p == 0);
    }

But this is explicitly undefined behavior in C, because the value of
&buf becomes indeterminate once test() returns and buf goes out of
scope.  Returning &buf might also affect the compiler's decision of
whether it can optimize away explicit_bzero().

As a workaround, explicit_bzero.c arranges for test() to run using
"altstack"'s memory as the stack, and we change the test() function to
be conceptually like:

    int *test() {
        int buf = 42;
        int *p = memmem(altstack, sizeof(altstack), &buf, sizeof(buf));
        explicit_bzero(&buf, sizeof(buf));
        return p;
    }

The memmem() call searches through altstack looking for a sequence of
memory that has the same contents as buf.  In practice because we've
arranged to use altstack as our stack and buf is a large unique value,
the matching address p is actually going to be &buf itself; but as far
as the compiler's concerned, it's actually an offset in altstack
instead.

Changing "memmem(altstack, sizeof(altstack), buf, sizeof(buf))" to
"memmem(buf, sizeof(buf), secret, sizeof(secret))" changes the code
back to the original, because now memmem()'s returning a pointer into
buf instead of into altstack.

> Enabling the stack protector code really does shift buf by 112 bytes on the
> signal stack, so I think its more likely working and inserting some guards
> at the head and tail of the stack signal stack.

That should be fine: explicit_bzero.c isn't assuming buf is going to
be at any particular offset within altstack, it just expects it to be
*somewhere* within it.

The issue with asan's stack checking logic is it's not aware of POSIX
signal stacks.  We've explicitly allocated the stack memory ourselves
and we're accessing it as an array of unsigned chars.  We might get
unspecified values, but we can't trigger undefined behavior.  However,
asan can't easily distinguish this case from any other
use-after-return issue, and it's a pretty rare case anyway so they're
not likely to invest resources into fixing it.

I'd say we're better off trying to mark the test functions
__attribute__((no_asan)) or something.