diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-05-16 22:02:53 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-05-16 22:02:53 +0300 |
commit | 48733c9aca3d12fc07a791d24fb3e6890de9c01f (patch) | |
tree | 95d483c9738e83ea30cedd8b75ea93cdd7876bff /src | |
parent | bbfd14e24d0c288bff351a9ed4cdac217e6cbe4d (diff) | |
download | gdbm-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.c | 14 | ||||
-rw-r--r-- | src/gdbm.h.in | 6 | ||||
-rw-r--r-- | src/gdbmerrno.c | 4 | ||||
-rw-r--r-- | src/gdbmopen.c | 76 |
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 | ||
137 | const char * | 139 | const 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 | ||
52 | static int | ||
53 | validate_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 | |||
52 | GDBM_FILE | 98 | GDBM_FILE |
53 | gdbm_fd_open (int fd, const char *file_name, int block_size, | 99 | gdbm_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))) |