From patchwork Fri Nov 8 17:14:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 2008647 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; secure) header.d=sourceware.org header.i=@sourceware.org header.a=rsa-sha256 header.s=default header.b=YVATGstZ; 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 4XlQZK1Dtmz1xy0 for ; Sat, 9 Nov 2024 04:15:53 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5C8653858429 for ; Fri, 8 Nov 2024 17:15:51 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 5C8653858429 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1731086151; bh=rwj0VzpQtQOLSPPa7QV2QcLG0Bpm4sr60OVq8I/doNM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=YVATGstZ0kmvWvTYkeTxmGTeYQT/77OubQL+i2HSdMC9eIoIaKQR5r6mOl3MSPTSb xCDpFbVIEPU7iDt0hc9esymwNrahDFpF+lkJAN3huwEbWNbuFMGi+qqEriaMxbHBHw cmjq+mpQgzUacOEsiptLtMUZUBTZYE/ELdWZo3c8= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from giraffe.ash.relay.mailchannels.net (giraffe.ash.relay.mailchannels.net [23.83.222.69]) by sourceware.org (Postfix) with ESMTPS id 6D5053858CDA for ; Fri, 8 Nov 2024 17:15:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6D5053858CDA Authentication-Results: sourceware.org; dmarc=fail (p=none dis=none) header.from=sourceware.org Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=sourceware.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 6D5053858CDA Authentication-Results: server2.sourceware.org; arc=pass smtp.remote-ip=23.83.222.69 ARC-Seal: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1731086113; cv=pass; b=QhFlz7UGyXaebDC7galoKp6DDoBByFBeYKuXRc61pC5ofnSmjAzkfayqyHWMBM9yFqmFZTmw1yJrbts6j3eIVOLtKmFIFcZxoniauQLcYCoGAXrFOhTWwKxXT80tCvjZ9g14ONYFVz9FqLpK99vlWfcqqO7dTXEdEzWmxrGnaDw= ARC-Message-Signature: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1731086113; c=relaxed/simple; bh=agvZzTXeZI5f3+BhT1of8u9/rwP7On7OTvEEPyCm4Ks=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=hj3Rpr2mt9aJZOmXB+NvlgW24djoRqSKOTstwrUVJoyT5ICR+EvP/uWVz/wnr+7tiCwpDZNyH1cZycJ7hRt/Ff6CkgidNBfdF/QcyZHYyJJFPtZptYQldXIqYTUMQFb4LGYpzbPueqegr3PXNVLuyjJTutm9Em7ITOzwKFd17tc= ARC-Authentication-Results: i=2; server2.sourceware.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 648FD1816DF; Fri, 8 Nov 2024 17:15:00 +0000 (UTC) Received: from pdx1-sub0-mail-a258.dreamhost.com (trex-7.trex.outbound.svc.cluster.local [100.112.47.183]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id 146EA182D0A; Fri, 8 Nov 2024 17:15:00 +0000 (UTC) ARC-Seal: i=1; s=arc-2022; d=mailchannels.net; t=1731086100; a=rsa-sha256; cv=none; b=caXogDwp5C7DxQ9JHELGUCUEBBjzVxS3AwYngNAo7Z0KaB6Oy6/iG9JFw8JOkpFTRZzjhH kixremV8eFaS49I2ga3qCXmCJlwtKdeehYt/55u7+xRRuvc3Kq+wpR7jfDSkGAbGfQ9eP/ LjukoJ1P64ORJtTIEdHwMKtt4OH37n2Af8yxTZ4kc4rqYwiEo3DmUD45kWTSmL87EiuqCi 5vWFt10RthXjB8F1a2N+0ksWpA73jwsFbdYAJITnB85MIfQZdxxwPdMsahJvw8vPcw4p5w uzQqJr0KFJJ0dl3qyeJBBZrYQq+Yn/lxcnQKzYkgA2X5vxaH/211kugkLDgYgA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=mailchannels.net; s=arc-2022; t=1731086100; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=rwj0VzpQtQOLSPPa7QV2QcLG0Bpm4sr60OVq8I/doNM=; b=ty2jkBlRjM6MtTkPKzg0QnDYU6vkAKOXQMNbhE/uow25j0UEL1mG549K//QVsy13CEaLje Sat9+qo2oh4veDL7ZxblmgWKwTHPHOiD1jekvPq/d333sI/fwfucFZ4m9MDjTQz2+giK9W P1v9vc85eoUNiCMk6Tz5Up0nux70vkTodcCa7qczcVXx0sx4eT9PF8IG1OcOTF87KACh6q mQaDu7syVfPUcMWMZIRMOPnnkGL36mGAHSomTieY2d5ImCmPBaMiI49tMI662z+onV2b6o JmUIH+zgpaB/kSwwNysPIn5dGYDtSCiSjxGaDO8UGOz460oBIyvgPy1u0QmmQQ== ARC-Authentication-Results: i=1; rspamd-7ffc46dcc8-f9qmn; auth=pass smtp.auth=dreamhost smtp.mailfrom=siddhesh@sourceware.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Cure-Madly: 6f04599e1868663f_1731086100308_2280818261 X-MC-Loop-Signature: 1731086100308:1615313549 X-MC-Ingress-Time: 1731086100308 Received: from pdx1-sub0-mail-a258.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384) by 100.112.47.183 (trex/7.0.2); Fri, 08 Nov 2024 17:15:00 +0000 Received: from fedora.. (bras-base-toroon4859w-grc-89-184-146-156-41.dsl.bell.ca [184.146.156.41]) (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) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a258.dreamhost.com (Postfix) with ESMTPSA id 4XlQYH4gGwzLG; Fri, 8 Nov 2024 09:14:59 -0800 (PST) From: Siddhesh Poyarekar To: libc-alpha@sourceware.org Cc: carlos@redhat.com, fweimer@redhat.com Subject: [PATCH 2/3] ungetc: Guarantee single char pushback Date: Fri, 8 Nov 2024 12:14:49 -0500 Message-ID: <20241108171450.411932-3-siddhesh@sourceware.org> X-Mailer: git-send-email 2.46.0 In-Reply-To: <20241108171450.411932-1-siddhesh@sourceware.org> References: <20241108171450.411932-1-siddhesh@sourceware.org> MIME-Version: 1.0 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 The C standard requires that ungetc guarantees at least one pushback, so put a single byte pushback buffer in the FILE struct to enable that. Signed-off-by: Siddhesh Poyarekar --- libio/bits/types/struct_FILE.h | 3 +- libio/fileops.c | 21 +++----- libio/genops.c | 27 +++++----- stdio-common/Makefile | 1 + stdio-common/tst-ungetc-nomem.c | 94 +++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 28 deletions(-) create mode 100644 stdio-common/tst-ungetc-nomem.c diff --git a/libio/bits/types/struct_FILE.h b/libio/bits/types/struct_FILE.h index d8d26639d1..6b5295fce5 100644 --- a/libio/bits/types/struct_FILE.h +++ b/libio/bits/types/struct_FILE.h @@ -94,8 +94,9 @@ struct _IO_FILE_complete void *_freeres_buf; struct _IO_FILE **_prevchain; int _mode; + char _short_backupbuf[1]; /* Make sure we don't get into trouble again. */ - char _unused2[15 * sizeof (int) - 5 * sizeof (void *)]; + char _unused2[15 * sizeof (int) - 5 * sizeof (void *) - sizeof (char)]; }; /* These macros are used by bits/stdio.h and internal headers. */ diff --git a/libio/fileops.c b/libio/fileops.c index 4db4a76f75..67e57e0d8c 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -478,11 +478,8 @@ _IO_new_file_underflow (FILE *fp) if (fp->_IO_buf_base == NULL) { /* Maybe we already have a push back pointer. */ - if (fp->_IO_save_base != NULL) - { - free (fp->_IO_save_base); - fp->_flags &= ~_IO_IN_BACKUP; - } + if (_IO_have_backup (fp)) + _IO_free_backup_area (fp); _IO_doallocbuf (fp); } @@ -930,11 +927,8 @@ _IO_new_file_seekoff (FILE *fp, off64_t offset, int dir, int mode) if (fp->_IO_buf_base == NULL) { /* It could be that we already have a pushback buffer. */ - if (fp->_IO_read_base != NULL) - { - free (fp->_IO_read_base); - fp->_flags &= ~_IO_IN_BACKUP; - } + if (_IO_have_backup (fp)) + _IO_free_backup_area (fp); _IO_doallocbuf (fp); _IO_setp (fp, fp->_IO_buf_base, fp->_IO_buf_base); _IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base); @@ -1280,11 +1274,8 @@ _IO_file_xsgetn (FILE *fp, void *data, size_t n) if (fp->_IO_buf_base == NULL) { /* Maybe we already have a push back pointer. */ - if (fp->_IO_save_base != NULL) - { - free (fp->_IO_save_base); - fp->_flags &= ~_IO_IN_BACKUP; - } + if (_IO_have_backup (fp)) + _IO_free_backup_area (fp); _IO_doallocbuf (fp); } diff --git a/libio/genops.c b/libio/genops.c index 6545a78ad5..02b38fbc9a 100644 --- a/libio/genops.c +++ b/libio/genops.c @@ -48,6 +48,13 @@ flush_cleanup (void *not_used) } #endif +static void +free_backup_buf (FILE *fp, char *ptr) +{ + if (fp->_short_backupbuf != ptr) + free (ptr); +} + /* Fields in struct _IO_FILE after the _lock field are internal to glibc and opaque to applications. We can change them as long as the size of struct _IO_FILE is unchanged, which is checked as the @@ -212,7 +219,7 @@ _IO_free_backup_area (FILE *fp) { if (_IO_in_backup (fp)) _IO_switch_to_main_get_area (fp); /* Just in case. */ - free (fp->_IO_save_base); + free_backup_buf (fp, fp->_IO_save_base); fp->_IO_save_base = NULL; fp->_IO_save_end = NULL; fp->_IO_backup_base = NULL; @@ -260,7 +267,7 @@ save_for_backup (FILE *fp, char *end_p) memcpy (new_buffer + avail, fp->_IO_read_base + least_mark, needed_size); - free (fp->_IO_save_base); + free_backup_buf (fp, fp->_IO_save_base); fp->_IO_save_base = new_buffer; fp->_IO_save_end = new_buffer + avail + needed_size; } @@ -634,7 +641,7 @@ _IO_default_finish (FILE *fp, int dummy) for (mark = fp->_markers; mark != NULL; mark = mark->_next) mark->_sbuf = NULL; - if (fp->_IO_save_base) + if (fp->_IO_save_base && fp->_IO_save_base != fp->_short_backupbuf) { free (fp->_IO_save_base); fp->_IO_save_base = NULL; @@ -997,14 +1004,10 @@ _IO_default_pbackfail (FILE *fp, int c) } else if (!_IO_have_backup (fp)) { - /* No backup buffer: allocate one. */ - /* Use nshort buffer, if unused? (probably not) FIXME */ - int backup_size = 128; - char *bbuf = (char *) malloc (backup_size); - if (bbuf == NULL) - return EOF; - fp->_IO_save_base = bbuf; - fp->_IO_save_end = fp->_IO_save_base + backup_size; + /* We need to guarantee one pushback, so start with the built-in + 1-char buffer. */ + fp->_IO_save_base = fp->_short_backupbuf; + fp->_IO_save_end = fp->_IO_save_base + 1; fp->_IO_backup_base = fp->_IO_save_end; } fp->_IO_read_base = fp->_IO_read_ptr; @@ -1022,7 +1025,7 @@ _IO_default_pbackfail (FILE *fp, int c) return EOF; memcpy (new_buf + (new_size - old_size), fp->_IO_read_base, old_size); - free (fp->_IO_read_base); + free_backup_buf (fp, fp->_IO_read_base); _IO_setg (fp, new_buf, new_buf + (new_size - old_size), new_buf + new_size); fp->_IO_backup_base = fp->_IO_read_ptr; diff --git a/stdio-common/Makefile b/stdio-common/Makefile index a166eb7cf8..5e0ec763ff 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -275,6 +275,7 @@ tests := \ tst-tmpnam \ tst-ungetc \ tst-ungetc-leak \ + tst-ungetc-nomem \ tst-unlockedio \ tst-vfprintf-mbs-prec \ tst-vfprintf-user-type \ diff --git a/stdio-common/tst-ungetc-nomem.c b/stdio-common/tst-ungetc-nomem.c new file mode 100644 index 0000000000..bf36229a9c --- /dev/null +++ b/stdio-common/tst-ungetc-nomem.c @@ -0,0 +1,94 @@ +/* Test ungetc behavior with malloc failures. + Copyright The GNU Toolchain Authors. + 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 +#include + +extern void *__libc_malloc (size_t) + __attribute__ ((malloc)) __attribute__ ((alloc_size (1))); + +static bool fail = false; + +void * +malloc (size_t sz) +{ + if (fail) + return NULL; + + return __libc_malloc (sz); +} + +static int +do_test (void) +{ + char *filename = NULL; + struct stat props = {}; + size_t bufsz = 0; + + create_temp_file ("tst-ungetc-nomem.", &filename); + if (stat (filename, &props) != 0) + FAIL_EXIT1 ("Could not get file status: %m\n"); + + FILE *fp = fopen (filename, "w"); + + /* The libio buffer sizes are the same as block size. */ + bufsz = props.st_blksize + 2; + + char *buf = xmalloc (bufsz); + memset (buf, 'a', bufsz); + + size_t remaining = bufsz; + while (remaining > 0) + { + size_t done = fwrite (buf, sizeof (char), remaining, fp); + if (done == 0) + break; + remaining -= done; + } + fclose (fp); + + /* Begin test. */ + fp = fopen (filename, "r"); + + + /* The standard requires the first ungetc to always work. */ + fail = true; + TEST_COMPARE (ungetc('y', fp), 'y'); + + /* Now let the buffers get allocated to allow for subsequent tests. */ + fail = false; + TEST_COMPARE (fgetc (fp), 'y'); + TEST_COMPARE (ungetc('y', fp), 'y'); + TEST_COMPARE (fgetc (fp), 'y'); + + while (!feof (fp)) + { + fail = true; + TEST_COMPARE (ungetc('y', fp), 'y'); + fail = false; + TEST_COMPARE (fgetc (fp), 'y'); + if (fgetc (fp) != 'a') + TEST_COMPARE (ferror (fp), 0); + } + return 0; +} + +#include