[libc-commits] [libc] [llvm] [libc] Release stream buffers with delete[] and stop reallocating them (PR #237127)
Mike Lothian via libc-commits
libc-commits at lists.llvm.org
Fri Oct 30 00:46:33 PDT 2026
https://github.com/FireBurn updated https://github.com/llvm/llvm-project/pull/237127
>From 693e03a648c8c051f12c73e3d65b4c8339dc4bc1 Mon Sep 17 00:00:00 2001
From: Mike Lothian <mike at fireburn.co.uk>
Date: Fri, 30 Oct 2026 07:36:38 +0000
Subject: [PATCH] [libc] Release stream buffers with delete[] and stop
reallocating them
The buffers a stream owns are all allocated with new[], but File::close
and File::set_buffer released them with delete, and set_buffer resized
one with realloc. Allocate the replacement with new[] and release the
old buffer with delete[] in both places.
Assisted-by: Claude
---
libc/src/__support/File/CMakeLists.txt | 1 -
libc/src/__support/File/file.cpp | 28 ++++++++-----------
libc/test/src/stdio/setvbuf_test.cpp | 26 +++++++++++++++++
.../llvm-project-overlay/libc/BUILD.bazel | 1 -
4 files changed, 37 insertions(+), 19 deletions(-)
diff --git a/libc/src/__support/File/CMakeLists.txt b/libc/src/__support/File/CMakeLists.txt
index ca2670281a92d7..f3b76961e2cf45 100644
--- a/libc/src/__support/File/CMakeLists.txt
+++ b/libc/src/__support/File/CMakeLists.txt
@@ -20,7 +20,6 @@ add_object_library(
DEPENDS
.file_io_result
libc.hdr.errno_macros
- libc.hdr.func.realloc
libc.hdr.stdint_proxy
libc.hdr.stdio_macros
libc.hdr.types.char32_t
diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index f8ff4a6e52f2ca..21badb27a2ea57 100644
--- a/libc/src/__support/File/file.cpp
+++ b/libc/src/__support/File/file.cpp
@@ -16,7 +16,6 @@
#include "file_io_result.h"
#include "file_mode.h"
#include "hdr/errno_macros.h"
-#include "hdr/func/realloc.h"
#include "hdr/stdint_proxy.h"
#include "hdr/stdio_macros.h"
#include "hdr/types/char32_t.h"
@@ -522,7 +521,7 @@ int File::close() {
// implementation. The platform close should not need to access the buffer
// and we need to clean it up before the entire structure is removed.
if (own_buf)
- delete buf;
+ delete[] buf;
// Platform close is expected to cleanup the file data structure which
// includes the file mutex. Hence, we call platform_close after releasing
@@ -549,24 +548,19 @@ int File::set_buffer(void *buffer, size_t size, int buffer_mode) {
if (buffer == nullptr && size != 0 && buffer_mode != _IONBF) {
// We exclude the case of buffer_mode == _IONBF in this branch
// because we don't need to allocate buffer in such a case.
- if (own_buf) {
- // This is one of the places where a C allocation function is used
- // as C++ does not have an equivalent of realloc.
- buf = reinterpret_cast<uint8_t *>(realloc(buf, size));
- if (buf == nullptr)
- return ENOMEM;
- } else {
- AllocChecker ac;
- buf = new (ac) uint8_t[size];
- if (!ac)
- return ENOMEM;
- own_buf = true;
- }
+ AllocChecker ac;
+ uint8_t *new_buf = new (ac) uint8_t[size];
+ if (!ac)
+ return ENOMEM;
+ // The buffer is allocated with new[], so it is released with delete[].
+ if (own_buf)
+ delete[] buf;
+ buf = new_buf;
+ own_buf = true;
bufsize = size;
- // TODO: Handle allocation failures.
} else {
if (own_buf)
- delete buf;
+ delete[] buf;
if (buffer_mode != _IONBF) {
buf = static_cast<uint8_t *>(buffer);
bufsize = size;
diff --git a/libc/test/src/stdio/setvbuf_test.cpp b/libc/test/src/stdio/setvbuf_test.cpp
index a0936ba79ef73d..e999db7822fab3 100644
--- a/libc/test/src/stdio/setvbuf_test.cpp
+++ b/libc/test/src/stdio/setvbuf_test.cpp
@@ -106,3 +106,29 @@ TEST(LlvmLibcSetbufTest, InvalidBufferMode) {
ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f));
}
+
+TEST_F(LlvmLibcSetvbufTest, ReplaceTheBufferTheStreamAllocated) {
+ // The stream allocates its own buffer. It is resized, which allocates a new
+ // one, and then replaced by one of ours, which releases the allocated one.
+ constexpr char FILENAME[] = "testdata/setvbuf_replace.test";
+ constexpr char CONTENT[] = "abcdef";
+
+ ::FILE *f = LIBC_NAMESPACE::fopen(FILENAME, "w");
+ ASSERT_FALSE(f == nullptr);
+ ASSERT_EQ(LIBC_NAMESPACE::setvbuf(f, nullptr, _IOFBF, 4096), 0);
+ char buffer[BUFSIZ];
+ ASSERT_EQ(LIBC_NAMESPACE::setvbuf(f, buffer, _IOFBF, BUFSIZ), 0);
+ ASSERT_EQ(sizeof(CONTENT),
+ LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), f));
+ ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f));
+
+ // A stream that keeps the buffer it allocated releases it when it is closed.
+ f = LIBC_NAMESPACE::fopen(FILENAME, "r");
+ ASSERT_FALSE(f == nullptr);
+ ASSERT_EQ(LIBC_NAMESPACE::setvbuf(f, nullptr, _IOFBF, 4096), 0);
+ char data[sizeof(CONTENT)];
+ ASSERT_EQ(sizeof(CONTENT),
+ LIBC_NAMESPACE::fread(data, 1, sizeof(CONTENT), f));
+ ASSERT_STREQ(CONTENT, data);
+ ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f));
+}
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 5f8fa7e58778af..171a804cf4eeca 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -2395,7 +2395,6 @@ libc_support_library(
":func_aligned_alloc",
":func_free",
":func_malloc",
- ":func_realloc",
":hdr_errno_macros",
":hdr_stdint_proxy",
":hdr_stdio_macros",
More information about the libc-commits
mailing list