diff mbox series

[committed,v2] libstdc++: Add workaround for read(2) EINVAL on macOS and FreeBSD [PR102259]

Message ID 20241209160504.1850443-1-jwakely@redhat.com
State New
Headers show
Series [committed,v2] libstdc++: Add workaround for read(2) EINVAL on macOS and FreeBSD [PR102259] | expand

Commit Message

Jonathan Wakely Dec. 9, 2024, 4:01 p.m. UTC
On macOS and FreeBSD the read(2) system call can return EINVAL for large
sizes, so limit the maximum that we try to read. The calling code in
basic_filebuf::xsgetn will loop until it gets the size it wants, so we don't
need to loop in basic_file::xsgetn, just limit the maximum size.

libstdc++-v3/ChangeLog:

	PR libstdc++/102259
	* config/io/basic_file_stdio.cc (basic_file::xsgetn): Limit n to
	_GLIBCXX_MAX_READ_SIZE if that macro is defined.
	* config/os/bsd/darwin/os_defines.h (_GLIBCXX_MAX_READ_SIZE):
	Define to INT_MAX-1.
	* config/os/bsd/freebsd/os_defines.h (_GLIBCXX_MAX_READ_SIZE):
	Likewise.
---

Pushed v2 patch that defines _GLIBCXX_MAX_READ_SIZE and limits the read
if the size exceeds that value.

Tested on FreeBSD. It looks like the current stable FreeBSD release only
gives EINVAL for values greater than SSIZE_MAX not INT_MAX, so this
might only be needed for older releases. But it's only a minor
pessimization to add this limit and loop for the unusual cases of
reading 2GB or more in one go. If somebody knows when the SSIZE_MAX
limit came into being for FreeBSD, we could make the value of
_GLIBCXX_MAX_READ_SIZE depend on the FreeBSD release.

 libstdc++-v3/config/io/basic_file_stdio.cc      | 6 ++++++
 libstdc++-v3/config/os/bsd/darwin/os_defines.h  | 3 +++
 libstdc++-v3/config/os/bsd/freebsd/os_defines.h | 3 +++
 3 files changed, 12 insertions(+)
diff mbox series

Patch

diff --git a/libstdc++-v3/config/io/basic_file_stdio.cc b/libstdc++-v3/config/io/basic_file_stdio.cc
index 9b529490f08..95e6905b3ad 100644
--- a/libstdc++-v3/config/io/basic_file_stdio.cc
+++ b/libstdc++-v3/config/io/basic_file_stdio.cc
@@ -338,6 +338,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     if (__ret == 0 && ferror(this->file()))
       __ret = -1;
 #else
+
+#ifdef _GLIBCXX_MAX_READ_SIZE
+    if (__builtin_expect(__n > _GLIBCXX_MAX_READ_SIZE, 0))
+      __n = _GLIBCXX_MAX_READ_SIZE;
+#endif
+
     do
       __ret = read(this->fd(), __s, __n);
     while (__ret == -1L && errno == EINTR);
diff --git a/libstdc++-v3/config/os/bsd/darwin/os_defines.h b/libstdc++-v3/config/os/bsd/darwin/os_defines.h
index 6bc7930bdba..a9cbdbb557b 100644
--- a/libstdc++-v3/config/os/bsd/darwin/os_defines.h
+++ b/libstdc++-v3/config/os/bsd/darwin/os_defines.h
@@ -54,4 +54,7 @@ 
 // No support for referencing weak symbols without a definition.
 #define _GLIBCXX_USE_WEAK_REF 0
 
+// read(2) can return EINVAL for n >= INT_MAX.
+#define _GLIBCXX_MAX_READ_SIZE (__INT_MAX__ - 1)
+
 #endif
diff --git a/libstdc++-v3/config/os/bsd/freebsd/os_defines.h b/libstdc++-v3/config/os/bsd/freebsd/os_defines.h
index 125dfdc1888..bb4a8442dcc 100644
--- a/libstdc++-v3/config/os/bsd/freebsd/os_defines.h
+++ b/libstdc++-v3/config/os/bsd/freebsd/os_defines.h
@@ -50,4 +50,7 @@ 
 #define _GLIBCXX_USE_C99_FLOAT_TRANSCENDENTALS_DYNAMIC 0
 #endif
 
+// read(2) can return EINVAL for n >= INT_MAX.
+#define _GLIBCXX_MAX_READ_SIZE (__INT_MAX__ - 1)
+
 #endif