aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-05-16 22:02:53 +0300
committerSergey Poznyakoff <gray@gnu.org>2018-05-16 22:02:53 +0300
commit48733c9aca3d12fc07a791d24fb3e6890de9c01f (patch)
tree95d483c9738e83ea30cedd8b75ea93cdd7876bff /src
parentbbfd14e24d0c288bff351a9ed4cdac217e6cbe4d (diff)
downloadgdbm-48733c9aca3d12fc07a791d24fb3e6890de9c01f.tar.gz
gdbm-48733c9aca3d12fc07a791d24fb3e6890de9c01f.tar.bz2
Improve database file safety checks.
* src/bucket.c (_gdbm_get_bucket): Verify bucket consistency. * src/gdbm.h.in (GDBM_BAD_BUCKET, GDBM_WRONG_ARCH): New error codes. * src/gdbmerrno.c (gdbm_errlist): New error codes. * src/gdbmopen.c (gdbm_fd_open): Improve header consistency checks.
Diffstat (limited to 'src')
-rw-r--r--src/bucket.c14
-rw-r--r--src/gdbm.h.in6
-rw-r--r--src/gdbmerrno.c4
-rw-r--r--src/gdbmopen.c76
4 files changed, 75 insertions, 25 deletions
diff --git a/src/bucket.c b/src/bucket.c
index 6d12589..6def504 100644
--- a/src/bucket.c
+++ b/src/bucket.c
@@ -72,6 +72,8 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
72 /* Is that one is not already current, we must find it. */ 72 /* Is that one is not already current, we must find it. */
73 if (dbf->cache_entry->ca_adr != bucket_adr) 73 if (dbf->cache_entry->ca_adr != bucket_adr)
74 { 74 {
75 size_t max_bucket_elts;
76
75 /* Look in the cache. */ 77 /* Look in the cache. */
76 for (index = 0; index < dbf->cache_size; index++) 78 for (index = 0; index < dbf->cache_size; index++)
77 { 79 {
@@ -117,6 +119,18 @@ _gdbm_get_bucket (GDBM_FILE dbf, int dir_index)
117 _gdbm_fatal (dbf, gdbm_db_strerror (dbf)); 119 _gdbm_fatal (dbf, gdbm_db_strerror (dbf));
118 return -1; 120 return -1;
119 } 121 }
122 /* Validate the bucket */
123 max_bucket_elts =
124 (dbf->header->bucket_size - sizeof(hash_bucket))
125 / sizeof(bucket_element) + 1;
126 if (!(dbf->bucket->count >= 0
127 && dbf->bucket->av_count >= 0
128 && dbf->bucket->count <= max_bucket_elts
129 && dbf->bucket->av_count <= max_bucket_elts))
130 {
131 GDBM_SET_ERRNO (dbf, GDBM_BAD_BUCKET, TRUE);
132 return -1;
133 }
120 } 134 }
121 135
122 return 0; 136 return 0;
diff --git a/src/gdbm.h.in b/src/gdbm.h.in
index 8dd61b0..d6a8306 100644
--- a/src/gdbm.h.in
+++ b/src/gdbm.h.in
@@ -219,9 +219,11 @@ extern int gdbm_copy_meta (GDBM_FILE dst, GDBM_FILE src);
219# define GDBM_NEED_RECOVERY 29 219# define GDBM_NEED_RECOVERY 29
220# define GDBM_BACKUP_FAILED 30 220# define GDBM_BACKUP_FAILED 30
221# define GDBM_DIR_OVERFLOW 31 221# define GDBM_DIR_OVERFLOW 31
222 222# define GDBM_BAD_BUCKET 32
223# define GDBM_WRONG_ARCH 33
224
223# define _GDBM_MIN_ERRNO 0 225# define _GDBM_MIN_ERRNO 0
224# define _GDBM_MAX_ERRNO GDBM_DIR_OVERFLOW 226# define _GDBM_MAX_ERRNO GDBM_WRONG_ARCH
225 227
226/* This one was never used and will be removed in the future */ 228/* This one was never used and will be removed in the future */
227# define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR 229# define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR
diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c
index 1962d1a..0c8c8e5 100644
--- a/src/gdbmerrno.c
+++ b/src/gdbmerrno.c
@@ -131,7 +131,9 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = {
131 [GDBM_ERR_FILE_MODE] = N_("Failed to restore file mode"), 131 [GDBM_ERR_FILE_MODE] = N_("Failed to restore file mode"),
132 [GDBM_NEED_RECOVERY] = N_("Database needs recovery"), 132 [GDBM_NEED_RECOVERY] = N_("Database needs recovery"),
133 [GDBM_BACKUP_FAILED] = N_("Failed to create backup copy"), 133 [GDBM_BACKUP_FAILED] = N_("Failed to create backup copy"),
134 [GDBM_DIR_OVERFLOW] = N_("Bucket directory overflow") 134 [GDBM_DIR_OVERFLOW] = N_("Bucket directory overflow"),
135 [GDBM_BAD_BUCKET] = N_("Malformed bucket header"),
136 [GDBM_WRONG_OFF_T] = N_("File header assumes wrong off_t size")
135}; 137};
136 138
137const char * 139const char *
diff --git a/src/gdbmopen.c b/src/gdbmopen.c
index db56440..669f8fb 100644
--- a/src/gdbmopen.c
+++ b/src/gdbmopen.c
@@ -49,6 +49,52 @@ compute_directory_size (GDBM_FILE dbf, blksize_t block_size,
49 *ret_dir_bits = dir_bits; 49 *ret_dir_bits = dir_bits;
50} 50}
51 51
52static int
53validate_header (gdbm_file_header const *hdr, struct stat const *st)
54{
55 /* Is the magic number good? */
56 if (hdr->header_magic != GDBM_MAGIC)
57 {
58 switch (hdr->header_magic)
59 {
60 case GDBM_OMAGIC:
61 /* OK */
62 break;
63
64 case GDBM_OMAGIC_SWAP:
65 case GDBM_MAGIC32_SWAP:
66 case GDBM_MAGIC64_SWAP:
67 return GDBM_BYTE_SWAPPED;
68
69 case GDBM_MAGIC32:
70 case GDBM_MAGIC64:
71 return GDBM_WRONG_OFF_T;
72
73 default:
74 return GDBM_BAD_MAGIC_NUMBER;
75 }
76 }
77
78 if (!(hdr->block_size > sizeof (gdbm_file_header)
79 && hdr->block_size - sizeof (gdbm_file_header) >=
80 sizeof(hdr->avail.av_table[0])))
81 {
82 return GDBM_BLOCK_SIZE_ERROR;
83 }
84
85 /* Make sure dir and dir + dir_size fall within the file boundary */
86 if (!(hdr->dir > 0
87 && hdr->dir < st->st_size
88 && hdr->dir_size > 0
89 && hdr->dir + hdr->dir_size < st->st_size))
90 return GDBM_BAD_FILE_OFFSET;
91
92 if (!(hdr->bucket_size > sizeof(hash_bucket)))
93 return GDBM_BAD_MAGIC_NUMBER;
94
95 return 0;
96}
97
52GDBM_FILE 98GDBM_FILE
53gdbm_fd_open (int fd, const char *file_name, int block_size, 99gdbm_fd_open (int fd, const char *file_name, int block_size,
54 int flags, void (*fatal_func) (const char *)) 100 int flags, void (*fatal_func) (const char *))
@@ -323,7 +369,8 @@ gdbm_fd_open (int fd, const char *file_name, int block_size,
323 header and initialize the hash directory. */ 369 header and initialize the hash directory. */
324 370
325 gdbm_file_header partial_header; /* For the first part of it. */ 371 gdbm_file_header partial_header; /* For the first part of it. */
326 372 int rc;
373
327 /* Read the partial file header. */ 374 /* Read the partial file header. */
328 if (_gdbm_full_read (dbf, &partial_header, sizeof (gdbm_file_header))) 375 if (_gdbm_full_read (dbf, &partial_header, sizeof (gdbm_file_header)))
329 { 376 {
@@ -336,33 +383,17 @@ gdbm_fd_open (int fd, const char *file_name, int block_size,
336 return NULL; 383 return NULL;
337 } 384 }
338 385
339 /* Is the magic number good? */ 386 /* Is the header valid? */
340 if (partial_header.header_magic != GDBM_MAGIC 387 rc = validate_header (&partial_header, &file_stat);
341 && partial_header.header_magic != GDBM_OMAGIC) 388 if (rc != GDBM_NO_ERROR)
342 { 389 {
343 if (!(flags & GDBM_CLOERROR)) 390 if (!(flags & GDBM_CLOERROR))
344 dbf->desc = -1; 391 dbf->desc = -1;
345 gdbm_close (dbf); 392 gdbm_close (dbf);
346 switch (partial_header.header_magic) 393 GDBM_SET_ERRNO2 (NULL, rc, FALSE, GDBM_DEBUG_OPEN);
347 {
348 case GDBM_OMAGIC_SWAP:
349 case GDBM_MAGIC32_SWAP:
350 case GDBM_MAGIC64_SWAP:
351 GDBM_SET_ERRNO2 (NULL, GDBM_BYTE_SWAPPED, FALSE,
352 GDBM_DEBUG_OPEN);
353 break;
354 case GDBM_MAGIC32:
355 case GDBM_MAGIC64:
356 GDBM_SET_ERRNO2 (NULL, GDBM_BAD_FILE_OFFSET, FALSE,
357 GDBM_DEBUG_OPEN);
358 break;
359 default:
360 GDBM_SET_ERRNO2 (NULL, GDBM_BAD_MAGIC_NUMBER, FALSE,
361 GDBM_DEBUG_OPEN);
362 }
363 return NULL; 394 return NULL;
364 } 395 }
365 396
366 /* It is a good database, read the entire header. */ 397 /* It is a good database, read the entire header. */
367 dbf->header = malloc (partial_header.block_size); 398 dbf->header = malloc (partial_header.block_size);
368 if (dbf->header == NULL) 399 if (dbf->header == NULL)
@@ -373,6 +404,7 @@ gdbm_fd_open (int fd, const char *file_name, int block_size,
373 GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN); 404 GDBM_SET_ERRNO2 (NULL, GDBM_MALLOC_ERROR, FALSE, GDBM_DEBUG_OPEN);
374 return NULL; 405 return NULL;
375 } 406 }
407
376 memcpy (dbf->header, &partial_header, sizeof (gdbm_file_header)); 408 memcpy (dbf->header, &partial_header, sizeof (gdbm_file_header));
377 if (_gdbm_full_read (dbf, &dbf->header->avail.av_table[1], 409 if (_gdbm_full_read (dbf, &dbf->header->avail.av_table[1],
378 dbf->header->block_size - sizeof (gdbm_file_header))) 410 dbf->header->block_size - sizeof (gdbm_file_header)))

Return to:

Send suggestions and report system problems to the System administrator.