_REENT_CHECK_VERIFY calls __assert_func even if NDEBUG is defined

Jeff Johnston jjohnstn@redhat.com
Wed Apr 29 19:51:25 GMT 2020


On Tue, Apr 28, 2020 at 8:06 AM R. Diez <rdiezmail-newlib@yahoo.de> wrote:

> > An alternative change would require modifications to all the
> > existing conversion routines using eBalloc() and their callers
> > to do checking of return values and bubble up to the user,
> > setting errno to ENOMEM.
>
> I am not sure what conversion routines you are referring to. I found this
> issue with rand(), because lwIP needs a source of random numbers. It is
> normally not expected that rand() calls malloc(). rand() returns no error
> indication, according to the POSIX standard:
>
> https://pubs.opengroup.org/onlinepubs/9699919799/functions/rand.html
>
> So using errno here is difficult.
>
> rand() ends up calling _REENT_CHECK, which does a malloc(). What do you
> mean with eBalloc()?
>

Sorry, my bad for the confusion.  eBalloc() was one part of the CVE whereby
newlib was allocating memory internally for the conversion routines
and not checking the result (this code originally came from NetBSD
originally and assumed the Balloc() would succeed or a NULL pointer access
would occur).  To make this
return gracefully to the user without an abort would require a number of
changes to intermediate routines.

Regarding the _REENT_CHECK_VERIFY macro, this only affects you if you are
using _REENT_SMALL.  With that set-up, the reentrant structure is minimal
to start with and
storage is allocated at the last minute, if needed.  If you look in
libc/sys/reent.h you will see that for the non _REENT_SMALL configuration,
the storage is allocated up front and
the _REENT_CHECK macros do nothing.

In your case, rand() calls the _REENT_CHECK_RAND48() macro which for
_REENT_SMALL will end up allocating the rand48 storage if not already
allocated.

So, you have a number of choices:

1. don't use _REENT_SMALL and rand() won't allocate storage when used
2. use _REENT_SMALL but call rand() at the start of your application so it
won't try to allocate memory later on
3. use _REENT_SMALL and ensure you have enough extra storage allocated to
handle the apps needs and the minimal storage needed for the rand48 logic
4. allow the abort or null pointer access to occur and tune the application
not to run into this scenario if it does happen


>
> > The memory in question is being allocated by Balloc()
> > which is part of the mprec.c solution used in newlib.
> > The allocated _REENT_MP_FREELIST has an array
> > of storage to reuse for different k values so newlib will reuse
> > [...]
>
> Does that apply in my scenario? I am building Newlib without thread
> support. There is no reent creation or destruction, as far as I can tell.
>

If you aren't directly/indirectly using the mprec routines in stdlib, no,
it does not apply to you.  A reent structure is always allocated (see
impure_data in libc/reent/impure.c).

>
> I have been digging further, and I believe (the code is not easy to
> follow) that these unexpected allocations come from using the "small
> reent". What does --enable-newlib-reent-small actually do? The README file
> at this location:
>
>   https://sourceware.org/newlib/README
>
> has this description:
>
>   `--enable-newlib-reent-small'
>     Enable small reentrant struct support.
>     Disabled by default.
>
> But it does not really say what the difference between the normal and
> "small" versions is.
>

The difference is that _REENT_SMALL omits a bunch of reentrant storage used
by various routines until they are actually used.  Thus, your reentrant
structure
footprint is the minimum it can be for your application (e.g. if you never
call rand(), you don't need the rand48 structure).  If you never do file
I/O, you don't
need the std streams allocated for you and so on.  The cost of this is that
if your application has run out of memory, you might not have enough for
some of
these last second allocations.  The CVE in question noted that newlib
simply attempted to access the reentrant allocated storage without checking
and it could be null.  That said, if you don't have enough storage
for a small reentrant structure need, the application is in serious trouble
to continue because many platforms share storage between the stack and the
heap (you might not have
enough storage for another call).  I wouldn't have a lot of faith that an
application will check after every call it makes to see if ENOMEM is set
and then magically remove some storage it didn't really need.
Such a case usually means either tinkering with the initial storage
available or tuning the application so it isn't wasting storage needlessly.

Regards,

-- Jeff J.


> Thanks in advance,
>   rdiez
>
>


More information about the Newlib mailing list