From patchwork Wed Jun 30 14:12:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 44052 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1C454383D028 for ; Wed, 30 Jun 2021 14:12:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1C454383D028 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1625062364; bh=iWSeC2izTzgmD8kwDzrXST4ELKsvMfYyD4v9O5KSBsQ=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=S9aSy4fuDbHnCPYghG+sgI4+Iuw30m3W6im1NqkjWufxfAvoU4OQVtm0HjGl+RyhM eq+3KRH+fSWVxNM98BYJzgBsrFNGRjkJ6raB3uuya+bwmWovNSvZAf93PKZEYi0+0P JnBDua82jKs+NvzC5IMzCRXs90DxqQUcM02wzBuI= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 4F144385781D for ; Wed, 30 Jun 2021 14:12:19 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 4F144385781D Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-4-Mbs0DxB6O4Kcq6xuYkHSTA-1; Wed, 30 Jun 2021 10:12:16 -0400 X-MC-Unique: Mbs0DxB6O4Kcq6xuYkHSTA-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B378F80430E; Wed, 30 Jun 2021 14:12:14 +0000 (UTC) Received: from oldenburg.str.redhat.com (ovpn-115-5.ams2.redhat.com [10.36.115.5]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 9A8B7100164A; Wed, 30 Jun 2021 14:12:06 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH] Linux: Avoid calling malloc indirectly from __get_nprocs Date: Wed, 30 Jun 2021 16:12:05 +0200 Message-ID: <87a6n7jvbe.fsf@oldenburg.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.2 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Florian Weimer via Libc-alpha From: Florian Weimer Reply-To: Florian Weimer Cc: Siddhesh Poyarekar Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" malloc initialization depends on __get_nprocs, so using scratch buffers in __get_nprocs may result in infinite recursion. Tested on i686-linux-gnu, x86_64-linux-gnu. build-many-glibcs.py is still running, but looks good so far. (Exposure of this bug may have been a side effect of the malloc initialization rework, not sure.) Thanks, Florian Reviewed-by: Siddhesh Poyarekar Reviewed-by: Carlos O'Donell --- sysdeps/unix/sysv/linux/getsysstats.c | 83 ++++++++++++++++++++++++----------- 1 file 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 . */ +#include #include +#include +#include +#include #include -#include #include #include +#include #include #include +/* Compute the population count of the entire array. */ +static int +__get_nprocs_count (const unsigned long int *array, size_t length) +{ + 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; +} + +/* __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; +} + int __get_nprocs (void) { - 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); - - scratch_buffer_free (&set); - - return r; + /* 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)