Source: gdbm
Version: 1.8.3-14
Severity: important
Tags: lfs patch upstream

Dear Maintainer,

After upgrading to stretch, man-db was crashing randomly during package
upgrades.  Sometimes it would instead report EOVERFLOW errors.  Since I was
running it on i386 with a very large XFS filesystem, I suspected large file
support issues, since I've seen them before in other packages.  It turns out
this was the case.

I built man-db from source and debugged it to confirm the issue was in gdbm.
I then build gdbm from source and was astonished to notice that the fstat()
calls it makes weren't even error-checked, which explained the random crashes
(it tries to allocate, read, etc. a random amount of data because the stat
structure contains garbage from the stack).  This concerns me a bit, since
gdbm programs like man-db run as root with pretty arbitrary input so there
might even be an attack vector here.

I confirmed the code is compiled without _FILE_OFFSET_BITS=64, recompiled it
with that flag, and confirmed that the fstat() calls were working.  Running
the man-db test suite with the fixed gdbm passed all tests (before it failed
on 4 tests on my system).

I've prepared a patch to gdbm that fixes the fstat() calls (this should
probably go upstream).  I hacked in the -D_FILE_OFFSET_BITS=64 (I'm not an
autoconf expert) into Makefile.in; perhaps there's a better way.

I will attach the patch after the report is submitted.

-- System Information:
Debian Release: 9.0
  APT prefers stable
  APT policy: (500, 'stable')
Architecture: i386 (i686)

Kernel: Linux 4.9.0-3-686-pae (SMP w/2 CPU cores)
Locale: LANG=C, LC_CTYPE=en_CA.UTF-8 (charmap=UTF-8), LANGUAGE=C (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash
Init: systemd (via /run/systemd/system)
--- gdbm-1.8.3.orig/Makefile.in 2017-07-08 01:39:16.000000000 -0300
+++ gdbm-1.8.3/Makefile.in      2017-07-08 01:23:35.352509271 -0300
@@ -26,7 +26,7 @@
 # Where the system [n]dbm routines are...
 LIBS = @LIBS@ -lc
 
-CFLAGS = @CFLAGS@
+CFLAGS = @CFLAGS@ -D_FILE_OFFSET_BITS=64
 LDFLAGS = @LDFLAGS@
 
 # Common prefix for installation directories
--- gdbm-1.8.3.orig/gdbmopen.c  2017-07-08 01:39:16.000000000 -0300
+++ gdbm-1.8.3/gdbmopen.c       2017-07-08 01:50:27.809533680 -0300
@@ -152,7 +152,14 @@
     }
 
   /* Get the status of the file. */
-  fstat (dbf->desc, &file_stat);
+  if (fstat (dbf->desc, &file_stat) == -1)
+    {
+      close (dbf->desc);
+      free (dbf->name);
+      free (dbf);
+      gdbm_errno = GDBM_FILE_OPEN_ERROR;
+      return NULL;
+    }
 
   /* Lock the file in the approprate way. */
   if ((flags & GDBM_OPENMASK) == GDBM_READER)
@@ -195,8 +202,14 @@
      now time to truncate the file. */
   if (need_trunc && file_stat.st_size != 0)
     {
-      TRUNCATE (dbf);
-      fstat (dbf->desc, &file_stat);
+      if (TRUNCATE (dbf) == -1 || fstat (dbf->desc, &file_stat) == -1)
+        {
+          close (dbf->desc);
+          free (dbf->name);
+          free (dbf);
+          gdbm_errno = GDBM_FILE_OPEN_ERROR;
+          return NULL;
+        }
     }
 
   /* Decide if this is a new file or an old file. */
--- gdbm-1.8.3.orig/gdbmreorg.c 2002-10-07 15:38:26.000000000 -0300
+++ gdbm-1.8.3/gdbmreorg.c      2017-07-08 01:09:33.791888611 -0300
@@ -111,7 +111,12 @@
   new_name[len] = '#';
 
   /* Get the mode for the old file and open the new database. */
-  fstat (dbf->desc, &fileinfo);
+  if (fstat (dbf->desc, &fileinfo) == -1)
+    {
+      free (new_name);
+      gdbm_errno = GDBM_REORGANIZE_FAILED;
+      return -1;
+    }
   new_dbf = gdbm_open (new_name, dbf->header->block_size, GDBM_WRCREAT,
                       fileinfo.st_mode, dbf->fatal_err);
 
--- gdbm-1.8.3.orig/Makefile.in 2017-07-08 01:39:16.000000000 -0300
+++ gdbm-1.8.3/Makefile.in      2017-07-08 01:23:35.352509271 -0300
@@ -26,7 +26,7 @@
 # Where the system [n]dbm routines are...
 LIBS = @LIBS@ -lc
 
-CFLAGS = @CFLAGS@
+CFLAGS = @CFLAGS@ -D_FILE_OFFSET_BITS=64
 LDFLAGS = @LDFLAGS@
 
 # Common prefix for installation directories
--- gdbm-1.8.3.orig/gdbmopen.c  2017-07-08 01:39:16.000000000 -0300
+++ gdbm-1.8.3/gdbmopen.c       2017-07-08 01:50:27.809533680 -0300
@@ -152,7 +152,14 @@
     }
 
   /* Get the status of the file. */
-  fstat (dbf->desc, &file_stat);
+  if (fstat (dbf->desc, &file_stat) == -1)
+    {
+      close (dbf->desc);
+      free (dbf->name);
+      free (dbf);
+      gdbm_errno = GDBM_FILE_OPEN_ERROR;
+      return NULL;
+    }
 
   /* Lock the file in the approprate way. */
   if ((flags & GDBM_OPENMASK) == GDBM_READER)
@@ -195,8 +202,14 @@
      now time to truncate the file. */
   if (need_trunc && file_stat.st_size != 0)
     {
-      TRUNCATE (dbf);
-      fstat (dbf->desc, &file_stat);
+      if (TRUNCATE (dbf) == -1 || fstat (dbf->desc, &file_stat) == -1)
+        {
+          close (dbf->desc);
+          free (dbf->name);
+          free (dbf);
+          gdbm_errno = GDBM_FILE_OPEN_ERROR;
+          return NULL;
+        }
     }
 
   /* Decide if this is a new file or an old file. */
--- gdbm-1.8.3.orig/gdbmreorg.c 2002-10-07 15:38:26.000000000 -0300
+++ gdbm-1.8.3/gdbmreorg.c      2017-07-08 01:09:33.791888611 -0300
@@ -111,7 +111,12 @@
   new_name[len] = '#';
 
   /* Get the mode for the old file and open the new database. */
-  fstat (dbf->desc, &fileinfo);
+  if (fstat (dbf->desc, &fileinfo) == -1)
+    {
+      free (new_name);
+      gdbm_errno = GDBM_REORGANIZE_FAILED;
+      return -1;
+    }
   new_dbf = gdbm_open (new_name, dbf->header->block_size, GDBM_WRCREAT,
                       fileinfo.st_mode, dbf->fatal_err);
 
--- gdbm-1.8.3.orig/Makefile.in 2017-07-08 01:39:16.000000000 -0300
+++ gdbm-1.8.3/Makefile.in      2017-07-08 01:23:35.352509271 -0300
@@ -26,7 +26,7 @@
 # Where the system [n]dbm routines are...
 LIBS = @LIBS@ -lc
 
-CFLAGS = @CFLAGS@
+CFLAGS = @CFLAGS@ -D_FILE_OFFSET_BITS=64
 LDFLAGS = @LDFLAGS@
 
 # Common prefix for installation directories
--- gdbm-1.8.3.orig/gdbmopen.c  2017-07-08 01:39:16.000000000 -0300
+++ gdbm-1.8.3/gdbmopen.c       2017-07-08 01:50:27.809533680 -0300
@@ -152,7 +152,14 @@
     }
 
   /* Get the status of the file. */
-  fstat (dbf->desc, &file_stat);
+  if (fstat (dbf->desc, &file_stat) == -1)
+    {
+      close (dbf->desc);
+      free (dbf->name);
+      free (dbf);
+      gdbm_errno = GDBM_FILE_OPEN_ERROR;
+      return NULL;
+    }
 
   /* Lock the file in the approprate way. */
   if ((flags & GDBM_OPENMASK) == GDBM_READER)
@@ -195,8 +202,14 @@
      now time to truncate the file. */
   if (need_trunc && file_stat.st_size != 0)
     {
-      TRUNCATE (dbf);
-      fstat (dbf->desc, &file_stat);
+      if (TRUNCATE (dbf) == -1 || fstat (dbf->desc, &file_stat) == -1)
+        {
+          close (dbf->desc);
+          free (dbf->name);
+          free (dbf);
+          gdbm_errno = GDBM_FILE_OPEN_ERROR;
+          return NULL;
+        }
     }
 
   /* Decide if this is a new file or an old file. */
--- gdbm-1.8.3.orig/gdbmreorg.c 2002-10-07 15:38:26.000000000 -0300
+++ gdbm-1.8.3/gdbmreorg.c      2017-07-08 01:09:33.791888611 -0300
@@ -111,7 +111,12 @@
   new_name[len] = '#';
 
   /* Get the mode for the old file and open the new database. */
-  fstat (dbf->desc, &fileinfo);
+  if (fstat (dbf->desc, &fileinfo) == -1)
+    {
+      free (new_name);
+      gdbm_errno = GDBM_REORGANIZE_FAILED;
+      return -1;
+    }
   new_dbf = gdbm_open (new_name, dbf->header->block_size, GDBM_WRCREAT,
                       fileinfo.st_mode, dbf->fatal_err);
 

Reply via email to