From patchwork Mon Jul 29 10:43:24 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 1965934 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=cWO04INM; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WXZj81Q5pz1yf4 for ; Mon, 29 Jul 2024 20:43:56 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6B54F3858C52 for ; Mon, 29 Jul 2024 10:43:54 +0000 (GMT) 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 C49173858D37 for ; Mon, 29 Jul 2024 10:43:33 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C49173858D37 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C49173858D37 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722249815; cv=none; b=a7k/pV5Tr3qZMvpPgSPRxBL30IV0GF/aTM8+PZ2WqWWiyo4GcNaweraVJ/wik3YEM4RBo5kf0AQENAxlAxDKOjK38byIe4kDqBmZyJrLV5sJZkH9Mb1NW8+ebJ9cbu1sAbFdOSwL4zIGzL0cNTXPuwthgnNjRCWkP6G/eoWq7d0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1722249815; c=relaxed/simple; bh=9OxWDLcmKYTJJxsSftbFpZ0D6hbsxiEGQot3unVSw7Q=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=VMELr8pXrKMwvDBKEjKjF/1f2fwi2AM6tIqO4EEYUkmyF/dyRidlYpqqAXBHKTZMqgtkbJ+b/utwL4WwGvWBzUJU3pbHbtx4jjrTMzH1nO07G2253vESE3VYPjCcU2jhil8i8QGTbR0Zrf1pQwVOjRpE+mRJMe+zWfmkAQz41BU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1722249813; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type; bh=hpbwITQFB8nxUpuH6dmWLGLej/IUQIlM6n3GYA+ShKI=; b=cWO04INM3/gv8ym8J3HTQc+h4erORpHsQebarzJfzIG39iSAxI0JFeAcpnhVtIrA29bNAY awutbHIK3mUr3diHsHkWWchCzuDNdderB+ZcpXWXuLZKH/Ti9dR+B5Hw6I4QnmZXCR5Irt ngC0oycd+ereQE3LwGh4SZdWLRMQ8Es= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-306-1C16IRIiO1u5AW-sc7JDBg-1; Mon, 29 Jul 2024 06:43:30 -0400 X-MC-Unique: 1C16IRIiO1u5AW-sc7JDBg-1 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4F7E71955D4E; Mon, 29 Jul 2024 10:43:29 +0000 (UTC) Received: from oldenburg.str.redhat.com (unknown [10.45.224.31]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id EB03E19560AE; Mon, 29 Jul 2024 10:43:27 +0000 (UTC) From: Florian Weimer To: libc-alpha@sourceware.org Cc: Andreas Schwab Subject: [PATCH] libio: asprintf should write NULL upon failure Date: Mon, 29 Jul 2024 12:43:24 +0200 Message-ID: <871q3cqy0j.fsf@oldenburg.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 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_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org And update the manual to mention that function is now part of POSIX. This was suggested most recently by Solar Designer, noting that code replacing vsprintf with vasprintf in a security fix was subtly wrong: Re: GStreamer Security Advisory 2024-0003: Orc compiler stack-based buffer overflow Reviewed-by: Sam James --- v2: Commit message updated as suggested by Andreas Schwab. libio/Makefile | 1 + libio/tst-asprintf-null.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ libio/vasprintf.c | 17 ++++++++-------- manual/stdio.texi | 9 +++++++-- 4 files changed, 67 insertions(+), 11 deletions(-) base-commit: 32328a5a1461ff88c0b1e04954e9c68b3fa7f56d diff --git a/libio/Makefile b/libio/Makefile index 6a507b67ea..a2d1cde955 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -86,6 +86,7 @@ tests = \ bug-wmemstream1 \ bug-wsetpos \ test-fmemopen \ + tst-asprintf-null \ tst-atime \ tst-bz22415 \ tst-bz24051 \ diff --git a/libio/tst-asprintf-null.c b/libio/tst-asprintf-null.c new file mode 100644 index 0000000000..1eebeb200f --- /dev/null +++ b/libio/tst-asprintf-null.c @@ -0,0 +1,51 @@ +/* Test that asprintf sets the buffer pointer to NULL on failure. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static int +do_test (void) +{ + static const char sentinel[] = "sentinel"; + char *buf = (char *) sentinel; + { + /* Avoid -Wformat-overflow warning. */ + const char *volatile format = "%2000000000d %2000000000d"; + TEST_COMPARE (asprintf (&buf, format, 1, 2), -1); + } + if (errno != ENOMEM) + TEST_COMPARE (errno, EOVERFLOW); + TEST_VERIFY (buf == NULL); + + /* Force ENOMEM in the test below. */ + struct rlimit rl; + TEST_COMPARE (getrlimit (RLIMIT_AS, &rl), 0); + rl.rlim_cur = 10 * 1024 * 1024; + TEST_COMPARE (setrlimit (RLIMIT_AS, &rl), 0); + + buf = (char *) sentinel; + TEST_COMPARE (asprintf (&buf, "%20000000d", 1), -1); + TEST_COMPARE (errno, ENOMEM); + TEST_VERIFY (buf == NULL); + return 0; +} + +#include diff --git a/libio/vasprintf.c b/libio/vasprintf.c index 999ae526f4..24f2a2e175 100644 --- a/libio/vasprintf.c +++ b/libio/vasprintf.c @@ -92,7 +92,7 @@ __printf_buffer_flush_asprintf (struct __printf_buffer_asprintf *buf) int -__vasprintf_internal (char **result_ptr, const char *format, va_list args, +__vasprintf_internal (char **result, const char *format, va_list args, unsigned int mode_flags) { struct __printf_buffer_asprintf buf; @@ -105,23 +105,23 @@ __vasprintf_internal (char **result_ptr, const char *format, va_list args, { if (buf.base.write_base != buf.direct) free (buf.base.write_base); + *result = NULL; return done; } /* Transfer to the final buffer. */ - char *result; size_t size = buf.base.write_ptr - buf.base.write_base; if (buf.base.write_base == buf.direct) { - result = malloc (size + 1); - if (result == NULL) + *result = malloc (size + 1); + if (*result == NULL) return -1; - memcpy (result, buf.direct, size); + memcpy (*result, buf.direct, size); } else { - result = realloc (buf.base.write_base, size + 1); - if (result == NULL) + *result = realloc (buf.base.write_base, size + 1); + if (*result == NULL) { free (buf.base.write_base); return -1; @@ -129,8 +129,7 @@ __vasprintf_internal (char **result_ptr, const char *format, va_list args, } /* Add NUL termination. */ - result[size] = '\0'; - *result_ptr = result; + (*result)[size] = '\0'; return done; } diff --git a/manual/stdio.texi b/manual/stdio.texi index f5e289d58a..0d63897b74 100644 --- a/manual/stdio.texi +++ b/manual/stdio.texi @@ -2516,7 +2516,7 @@ The functions in this section do formatted output and place the results in dynamically allocated memory. @deftypefun int asprintf (char **@var{ptr}, const char *@var{template}, @dots{}) -@standards{GNU, stdio.h} +@standards{POSIX, stdio.h} @safety{@prelim{}@mtsafe{@mtslocale{}}@asunsafe{@ascuheap{}}@acunsafe{@acsmem{}}} This function is similar to @code{sprintf}, except that it dynamically allocates a string (as with @code{malloc}; @pxref{Unconstrained @@ -2524,7 +2524,9 @@ Allocation}) to hold the output, instead of putting the output in a buffer you allocate in advance. The @var{ptr} argument should be the address of a @code{char *} object, and a successful call to @code{asprintf} stores a pointer to the newly allocated string at that -location. +location. Current versions of @theglibc{} write a null pointer to +@samp{*@var{ptr}} upon failure, but this is not required by the +standard, and previous versions did not modify the pointer value. The return value is the number of characters allocated for the buffer, or less than zero if an error occurred. Usually this means that the buffer @@ -2545,6 +2547,9 @@ make_message (char *name, char *value) return result; @} @end smallexample + +The @code{asprintf} function was a GNU extension initially, but is now +part of POSIX. @end deftypefun @deftypefun int obstack_printf (struct obstack *@var{obstack}, const char *@var{template}, @dots{})