This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
(perl #133958) preserve errno on successful malloc/realloc origin/tonyc/133958-realloc-errno-success
authorTony Cook <tony@develop-help.com>
Fri, 3 May 2019 04:49:50 +0000 (14:49 +1000)
committerTony Cook <tony@develop-help.com>
Wed, 8 May 2019 23:56:59 +0000 (09:56 +1000)
commit9f300641fc6fb15749f287d7ac7fd6f312b3edd8
tree477a973eb25d7b9958c60547b5be2bfc6d1a8710
parent1a833e446496ec3cd2bd9d09aac54be5a3bb680d
(perl #133958) preserve errno on successful malloc/realloc

In general perl doesn't try to preserve errno (aka $!) since we're
aiming at the same behaviour as for C code - errno is only meaningful
if a function returned an error.

The exception to that is when perl is working without an explicit
request from the perl programmer.

When code is performing assignments, concatenating strings, pushing on
arrays etc, perl is exercising the memory allocation machinery,
calling malloc() and realloc().

It turns out that at least on one platform, realloc() can modify errno
on success.

It appears to be happening when jemalloc (the malloc() implementation
used on FreeBSD) tries to extend a memory arena and fails, leaving the
error number from that failure in errno, from truss:

mmap(0x80142f000,32768,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_FIXED|MAP_ANON|MAP_EXCL,-1,0x0) ERR#12 'Cannot allocate memory'

This magic call appears to be a FreeBSD specific mechanism to resize
the anonymous mapping.  On Linux the equivalent seems to be calling
mremap().

In each case for the test code mmap() is successfully called
immediately afterwards:

mmap(0x0,69632,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON|MAP_ALIGNED(12),-1,0x0) = 34390323200 (0x801d2b000)

and realloc() succeeds.

glibc() realloc seems to be simpler, AFAICT from reading the code it
only uses mremap() when the memory block is the entire mapping,
ie. for large blocks rather than for memory arenas, and it doesn't
request the same address, so it doesn't fail.

For blocks that are part of arenas, glibc tries to expand in-place
within the current arena (with no extending the arena itself) or falls
back to malloc, so there's no chance for errno to be changed on a
successful realloc().
util.c