summary refs log tree commit diff
path: root/catgets
diff options
context:
space:
mode:
Diffstat (limited to 'catgets')
-rw-r--r--catgets/open_catalog.c39
1 files changed, 38 insertions, 1 deletions
diff --git a/catgets/open_catalog.c b/catgets/open_catalog.c
index 9d54124cd0..deaa8718dd 100644
--- a/catgets/open_catalog.c
+++ b/catgets/open_catalog.c
@@ -19,6 +19,7 @@
 
 #include <byteswap.h>
 #include <endian.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <string.h>
 #include <stdlib.h>
@@ -38,6 +39,10 @@ __open_catalog (__nl_catd catalog)
   int fd = -1;
   struct stat st;
   int swapping;
+  size_t cnt;
+  size_t max_offset;
+  size_t tab_size;
+  const char *lastp;
 
   /* Make sure we are alone.  */
   __libc_lock_lock (catalog->lock);
@@ -175,11 +180,19 @@ __open_catalog (__nl_catd catalog)
     }
 
   /* Avoid dealing with directories and block devices */
-  if (fd < 0 || __fstat (fd, &st) < 0 || !S_ISREG (st.st_mode))
+  if (fd < 0 || __fstat (fd, &st) < 0)
     {
       catalog->status = nonexisting;
       goto unlock_return;
     }
+  if (!S_ISREG (st.st_mode) || st.st_size < sizeof (struct catalog_obj))
+    {
+      /* `errno' is not set correctly but the file is not usable.
+	 Use an reasonable error value.  */
+      __set_errno (EINVAL);
+      catalog->status = nonexisting;
+      goto unlock_return;
+    }
 
 #ifndef MAP_COPY
     /* Linux seems to lack read-only copy-on-write.  */
@@ -242,6 +255,7 @@ __open_catalog (__nl_catd catalog)
     swapping = 1;
   else
     {
+    invalid_file:
       /* Invalid file.  Free the resources and mark catalog as not
 	 usable.  */
       if (catalog->status == mmapped)
@@ -276,6 +290,29 @@ __open_catalog (__nl_catd catalog)
     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
 					       * catalog->plane_depth * 3 * 2];
 
+  /* Determine the largest string offset mentioned in the table.  */
+  max_offset = 0;
+  tab_size = 3 * catalog->plane_size * catalog->plane_depth;
+  for (cnt = 2; cnt < tab_size; cnt += 3)
+    if (catalog->name_ptr[cnt] > max_offset)
+      max_offset = catalog->name_ptr[cnt];
+
+  /* Now we can check whether the file is large enough to contain the
+     tables it says it contains.  */
+  if (st.st_size <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
+    /* The last string is not contained in the file.  */
+    goto invalid_file;
+
+  lastp = catalog->strings + max_offset;
+  max_offset = (st.st_size
+		- sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
+  while (*lastp != '\0')
+    {
+      if (--max_offset == 0)
+	goto invalid_file;
+      ++lastp;
+    }
+
   /* Release the lock again.  */
  unlock_return:
   if (fd != -1)