diff options
author | Florian Weimer <fweimer@redhat.com> | 2021-06-30 17:41:38 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2021-06-30 17:41:47 +0200 |
commit | eb68d7d23cc411acdf68a60f194343a6774d6194 (patch) | |
tree | f50e4c81f3c3d880a12627c9af27df44b8b3f372 /sysdeps | |
parent | 38f58041baff897e595a957ddb04ecb4c83ab322 (diff) | |
download | glibc-eb68d7d23cc411acdf68a60f194343a6774d6194.tar glibc-eb68d7d23cc411acdf68a60f194343a6774d6194.tar.gz glibc-eb68d7d23cc411acdf68a60f194343a6774d6194.tar.bz2 glibc-eb68d7d23cc411acdf68a60f194343a6774d6194.zip |
Linux: Avoid calling malloc indirectly from __get_nprocs
malloc initialization depends on __get_nprocs, so using
scratch buffers in __get_nprocs may result in infinite recursion.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/unix/sysv/linux/getsysstats.c | 83 |
1 files changed, 57 insertions, 26 deletions
diff --git a/sysdeps/unix/sysv/linux/getsysstats.c b/sysdeps/unix/sysv/linux/getsysstats.c index 2e15f0039e..1391e360b8 100644 --- a/sysdeps/unix/sysv/linux/getsysstats.c +++ b/sysdeps/unix/sysv/linux/getsysstats.c @@ -17,42 +17,73 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ +#include <array_length.h> #include <dirent.h> +#include <errno.h> +#include <ldsodefs.h> +#include <limits.h> #include <not-cancel.h> -#include <scratch_buffer.h> #include <stdio.h> #include <stdio_ext.h> +#include <sys/mman.h> #include <sys/sysinfo.h> #include <sysdep.h> -int -__get_nprocs (void) +/* Compute the population count of the entire array. */ +static int +__get_nprocs_count (const unsigned long int *array, size_t length) { - struct scratch_buffer set; - scratch_buffer_init (&set); - - int r; - while (true) - { - /* The possible error are EFAULT for an invalid buffer or ESRCH for - invalid pid, none could happen. */ - r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, set.length, - set.data); - if (r > 0) - break; - - if (!scratch_buffer_grow (&set)) - /* Default to an SMP system in case we cannot obtain an accurate - number. */ - return 2; - } - - /* The scratch_buffer is aligned to max_align_t. */ - r = __sched_cpucount (r, (const cpu_set_t *) set.data); + int count = 0; + for (size_t i = 0; i < length; ++i) + if (__builtin_add_overflow (count, __builtin_popcountl (array[i]), + &count)) + return INT_MAX; + return count; +} - scratch_buffer_free (&set); +/* __get_nprocs with a large buffer. */ +static int +__get_nprocs_large (void) +{ + /* This code cannot use scratch_buffer because it is used during + malloc initialization. */ + size_t pagesize = GLRO (dl_pagesize); + unsigned long int *page = __mmap (0, pagesize, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (page == MAP_FAILED) + return 2; + int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, pagesize, page); + int count; + if (r > 0) + count = __get_nprocs_count (page, pagesize / sizeof (unsigned long int)); + else if (r == -EINVAL) + /* One page is still not enough to store the bits. A more-or-less + arbitrary value. This assumes t hat such large systems never + happen in practice. */ + count = GLRO (dl_pagesize) * CHAR_BIT; + else + count = 2; + __munmap (page, GLRO (dl_pagesize)); + return count; +} - return r; +int +__get_nprocs (void) +{ + /* Fast path for most systems. The kernel expects a buffer size + that is a multiple of 8. */ + unsigned long int small_buffer[1024 / CHAR_BIT / sizeof (unsigned long int)]; + int r = INTERNAL_SYSCALL_CALL (sched_getaffinity, 0, + sizeof (small_buffer), small_buffer); + if (r > 0) + return __get_nprocs_count (small_buffer, r / sizeof (unsigned long int)); + else if (r == -EINVAL) + /* The kernel requests a larger buffer to store the data. */ + return __get_nprocs_large (); + else + /* Some other error. 2 is conservative (not a uniprocessor + system, so atomics are needed). */ + return 2; } libc_hidden_def (__get_nprocs) weak_alias (__get_nprocs, get_nprocs) |