aboutsummaryrefslogtreecommitdiff
path: root/sysdeps
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2021-06-30 17:41:38 +0200
committerFlorian Weimer <fweimer@redhat.com>2021-06-30 17:41:47 +0200
commiteb68d7d23cc411acdf68a60f194343a6774d6194 (patch)
treef50e4c81f3c3d880a12627c9af27df44b8b3f372 /sysdeps
parent38f58041baff897e595a957ddb04ecb4c83ab322 (diff)
downloadglibc-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.c83
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)