about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/tst-getdents64.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2019-06-27 15:08:40 +0200
committerFlorian Weimer <fweimer@redhat.com>2019-06-27 15:08:40 +0200
commita620bd7935c4b2dc94e472e62bd9a5c9434ea7b7 (patch)
tree0741388f6c9acdf7621676f6bf4829f0bb7ac44f /sysdeps/unix/sysv/linux/tst-getdents64.c
parentd039da1c00e01f8d3c3d74f439a971eb73e3045e (diff)
downloadglibc-a620bd7935c4b2dc94e472e62bd9a5c9434ea7b7.tar.gz
glibc-a620bd7935c4b2dc94e472e62bd9a5c9434ea7b7.tar.xz
glibc-a620bd7935c4b2dc94e472e62bd9a5c9434ea7b7.zip
Linux: Adjust gedents64 buffer size to int range [BZ #24740]
The kernel interface uses type unsigned int, but there is an
internal conversion to int, so INT_MAX is the correct limit.
Part of the buffer will always be unused, but this is not a
problem.  Such huge buffers do not occur in practice anyway.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'sysdeps/unix/sysv/linux/tst-getdents64.c')
-rw-r--r--sysdeps/unix/sysv/linux/tst-getdents64.c44
1 files changed, 44 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/tst-getdents64.c b/sysdeps/unix/sysv/linux/tst-getdents64.c
index c1f7721221..24e77e04d8 100644
--- a/sysdeps/unix/sysv/linux/tst-getdents64.c
+++ b/sysdeps/unix/sysv/linux/tst-getdents64.c
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -28,6 +29,47 @@
 #include <support/xunistd.h>
 #include <unistd.h>
 
+/* Called by large_buffer_checks below.  */
+static void
+large_buffer_check (int fd, char *large_buffer, size_t large_buffer_size)
+{
+  xlseek (fd, 0, SEEK_SET);
+  ssize_t ret = getdents64 (fd, large_buffer, large_buffer_size);
+  if (ret < 0)
+    FAIL_EXIT1 ("getdents64 for buffer of %zu bytes failed: %m",
+                large_buffer_size);
+  if (ret < offsetof (struct dirent64, d_name))
+    FAIL_EXIT1 ("getdents64 for buffer of %zu returned small value %zd",
+                large_buffer_size, ret);
+}
+
+/* Bug 24740: Make sure that the system call argument is adjusted
+   properly for the int type.  A large value should stay a large
+   value, and not wrap around to something small, causing the system
+   call to fail with EINVAL.  */
+static void
+large_buffer_checks (int fd)
+{
+  size_t large_buffer_size;
+  if (!__builtin_add_overflow (UINT_MAX, 2, &large_buffer_size))
+    {
+      char *large_buffer = malloc (large_buffer_size);
+      if (large_buffer == NULL)
+        printf ("warning: could not allocate %zu bytes of memory,"
+                " subtests skipped\n", large_buffer_size);
+      else
+        {
+          large_buffer_check (fd, large_buffer, INT_MAX);
+          large_buffer_check (fd, large_buffer, (size_t) INT_MAX + 1);
+          large_buffer_check (fd, large_buffer, (size_t) INT_MAX + 2);
+          large_buffer_check (fd, large_buffer, UINT_MAX);
+          large_buffer_check (fd, large_buffer, (size_t) UINT_MAX + 1);
+          large_buffer_check (fd, large_buffer, (size_t) UINT_MAX + 2);
+        }
+      free (large_buffer);
+    }
+}
+
 static int
 do_test (void)
 {
@@ -105,6 +147,8 @@ do_test (void)
       rewinddir (reference);
     }
 
+  large_buffer_checks (fd);
+
   xclose (fd);
   closedir (reference);
   return 0;