[patch] Fix BZ 19165 -- overflow in fread / fwrite
Paul Pluzhnikov
ppluzhnikov@google.com
Tue Oct 27 02:05:00 GMT 2015
On Mon, Oct 26, 2015 at 1:06 PM, Rich Felker <dalias@libc.org> wrote:
> This highly pessimizes short reads/writes, e.g. fwrite(&c,1,1,f), by
> introducing a div operation. The obvious intent of the original code
> was to avoid this.
Thanks for comments, everyone.
Revised patch addresses all of them (I believe). Not sure whether the
name or location of the saturating multiplication is acceptable
though. Suggestions welcome.
2015-10-26 Paul Pluzhnikov <ppluzhnikov@google.com>
[BZ #19165]
* libio/libioP.h (_IO_saturating_umull): New.
* libio/iofread.c (_IO_fread): Use it.
* ibio/iofread_u.c (__fread_unlocked): Likewise.
* libio/iofwrite.c (_IO_fwrite): Error on overflow.
* libio/iofwrite_u.c (fwrite_unlocked): Likewise.
--
Paul Pluzhnikov
-------------- next part --------------
diff --git a/libio/iofread.c b/libio/iofread.c
index eb69b05..d2a42bc 100644
--- a/libio/iofread.c
+++ b/libio/iofread.c
@@ -29,7 +29,7 @@
_IO_size_t
_IO_fread (void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
- _IO_size_t bytes_requested = size * count;
+ _IO_size_t bytes_requested = _IO_saturating_umull (size, count);
_IO_size_t bytes_read;
CHECK_FILE (fp, 0);
if (bytes_requested == 0)
diff --git a/libio/iofread_u.c b/libio/iofread_u.c
index 997b714..8c3a821 100644
--- a/libio/iofread_u.c
+++ b/libio/iofread_u.c
@@ -32,7 +32,7 @@
_IO_size_t
__fread_unlocked (void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
- _IO_size_t bytes_requested = size * count;
+ _IO_size_t bytes_requested = _IO_saturating_umull (size, count);
_IO_size_t bytes_read;
CHECK_FILE (fp, 0);
if (bytes_requested == 0)
diff --git a/libio/iofwrite.c b/libio/iofwrite.c
index 48ad4bc..9bce404 100644
--- a/libio/iofwrite.c
+++ b/libio/iofwrite.c
@@ -29,7 +29,7 @@
_IO_size_t
_IO_fwrite (const void *buf, _IO_size_t size, _IO_size_t count, _IO_FILE *fp)
{
- _IO_size_t request = size * count;
+ _IO_size_t request = _IO_saturating_umull (size, count);
_IO_size_t written = 0;
CHECK_FILE (fp, 0);
if (request == 0)
diff --git a/libio/iofwrite_u.c b/libio/iofwrite_u.c
index 2b1c47a..081a7b1 100644
--- a/libio/iofwrite_u.c
+++ b/libio/iofwrite_u.c
@@ -33,7 +33,7 @@ _IO_size_t
fwrite_unlocked (const void *buf, _IO_size_t size, _IO_size_t count,
_IO_FILE *fp)
{
- _IO_size_t request = size * count;
+ _IO_size_t request = _IO_saturating_umull (size, count);
_IO_size_t written = 0;
CHECK_FILE (fp, 0);
if (request == 0)
diff --git a/libio/libioP.h b/libio/libioP.h
index b1ca774..40dd9b8 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -890,3 +890,24 @@ _IO_acquire_lock_clear_flags2_fct (_IO_FILE **p)
| _IO_FLAGS2_SCANF_STD); \
} while (0)
#endif
+
+/* Returns a*b if the result doesn't overflow, else SIZE_MAX. */
+static inline size_t
+__attribute__ ((__always_inline__))
+_IO_saturating_umull (size_t a, size_t b)
+{
+#if __GNUC_PREREQ(5, 0)
+ size_t result;
+
+ if (__builtin_umull_overflow (a, b, &result)) {
+ return SIZE_MAX;
+ }
+ return result;
+#else
+ const size_t mul_no_overflow = (size_t) 1 << 4 * sizeof (size_t);
+ if ((a >= mul_no_overflow || b >= mul_no_overflow)
+ && b > 1 && a > SIZE_MAX / b)
+ return SIZE_MAX;
+ return a * b;
+#endif
+}
More information about the Libc-alpha
mailing list