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);