diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2018-05-24 08:09:48 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2018-05-24 08:09:48 +0300 |
commit | 0665bcf0c9cdf756f9d5edc6a638c56602c42065 (patch) | |
tree | 590d81a45721edca6b3530e7681d8f0db32af94b | |
parent | ce702a1ca5ed9240cd0a70583c4234c34ce81b73 (diff) | |
download | gdbm-0665bcf0c9cdf756f9d5edc6a638c56602c42065.tar.gz |
error checking: check for avail_block consistency before using it
* src/gdbmdefs.h (gdbm_avail_block_valid_p): New function.
* src/gdbm.h.in (GDBM_BAD_AVAIL): New error code.
* src/gdbmerrno.c: Support new error code.
* src/falloc.c (pop_avail_block): Validate the avail_block
* src/gdbmopen.c (validate_header): Validate the avail_block.
* src/gdbmtool.c (_gdbm_avail_list_size)
(_gdbm_print_avail_list): Validate the avail_block.
-rw-r--r-- | src/falloc.c | 13 | ||||
-rw-r--r-- | src/gdbm.h.in | 3 | ||||
-rw-r--r-- | src/gdbmdefs.h | 7 | ||||
-rw-r--r-- | src/gdbmerrno.c | 3 | ||||
-rw-r--r-- | src/gdbmopen.c | 4 | ||||
-rw-r--r-- | src/gdbmtool.c | 37 |
6 files changed, 48 insertions, 19 deletions
diff --git a/src/falloc.c b/src/falloc.c index 3f437a6..33a238a 100644 --- a/src/falloc.c +++ b/src/falloc.c @@ -152,7 +152,7 @@ _gdbm_free (GDBM_FILE dbf, off_t file_adr, int num_bytes) /* Gets the avail block at the top of the stack and loads it into the - active avail block. It does a "free" for itself! This can (and is) + active avail block. It does a "free" for itself! This can be (and is) now called even when the avail block is not empty, so we must be smart about things. */ @@ -206,12 +206,19 @@ pop_avail_block (GDBM_FILE dbf) return -1; } + if (!gdbm_avail_block_valid_p (new_blk)) + { + gdbm_set_errno (dbf, GDBM_BAD_AVAIL, TRUE); + _gdbm_fatal (dbf, gdbm_db_strerror (dbf)); + return -1; + } + /* Add the elements from the new block to the header. */ index = 0; while (index < new_blk->count) { - while(index < new_blk->count - && dbf->header->avail.count < dbf->header->avail.size) + while (index < new_blk->count + && dbf->header->avail.count < dbf->header->avail.size) { /* With luck, this will merge a lot of blocks! */ _gdbm_put_av_elem (new_blk->av_table[index], diff --git a/src/gdbm.h.in b/src/gdbm.h.in index 3d19de7..61d5707 100644 --- a/src/gdbm.h.in +++ b/src/gdbm.h.in @@ -221,9 +221,10 @@ extern int gdbm_copy_meta (GDBM_FILE dst, GDBM_FILE src); # define GDBM_DIR_OVERFLOW 31 # define GDBM_BAD_BUCKET 32 # define GDBM_BAD_HEADER 33 +# define GDBM_BAD_AVAIL 34 # define _GDBM_MIN_ERRNO 0 -# define _GDBM_MAX_ERRNO GDBM_BAD_HEADER +# define _GDBM_MAX_ERRNO GDBM_BAD_AVAIL /* This one was never used and will be removed in the future */ # define GDBM_UNKNOWN_UPDATE GDBM_UNKNOWN_ERROR diff --git a/src/gdbmdefs.h b/src/gdbmdefs.h index bd104e2..5305b0d 100644 --- a/src/gdbmdefs.h +++ b/src/gdbmdefs.h @@ -51,6 +51,13 @@ typedef struct avail_elem av_table[1]; /* The table. Make it look like an array. */ } avail_block; +/* Return true if avail_block is valid */ +static int inline +gdbm_avail_block_valid_p (avail_block const *av) +{ + return (av->size >= 0 && av->count >= 0 && av->count <= av->size); +} + /* The dbm file header keeps track of the current location of the hash directory and the free space in the file. */ diff --git a/src/gdbmerrno.c b/src/gdbmerrno.c index 7124b3a..896bf70 100644 --- a/src/gdbmerrno.c +++ b/src/gdbmerrno.c @@ -133,7 +133,8 @@ const char * const gdbm_errlist[_GDBM_MAX_ERRNO+1] = { [GDBM_BACKUP_FAILED] = N_("Failed to create backup copy"), [GDBM_DIR_OVERFLOW] = N_("Bucket directory overflow"), [GDBM_BAD_BUCKET] = N_("Malformed bucket header"), - [GDBM_BAD_HEADER] = N_("Malformed database file header") + [GDBM_BAD_HEADER] = N_("Malformed database file header"), + [GDBM_BAD_AVAIL] = N_("Malforemd avail_block") }; const char * diff --git a/src/gdbmopen.c b/src/gdbmopen.c index 5d59f65..b84ad63 100644 --- a/src/gdbmopen.c +++ b/src/gdbmopen.c @@ -108,6 +108,10 @@ validate_header (gdbm_file_header const *hdr, struct stat const *st) if (hdr->bucket_elems != bucket_element_count (hdr)) return GDBM_BAD_HEADER; + + /* Validate the avail block */ + if (!gdbm_avail_block_valid_p (&hdr->avail)) + return GDBM_BAD_HEADER; return 0; } diff --git a/src/gdbmtool.c b/src/gdbmtool.c index c522ad0..18d0e10 100644 --- a/src/gdbmtool.c +++ b/src/gdbmtool.c @@ -225,9 +225,12 @@ _gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size) break; } - lines += av_stk->count; - if (lines > min_size) - break; + if (gdbm_avail_block_valid_p (av_stk)) + { + lines += av_stk->count; + if (lines > min_size) + break; + } temp = av_stk->next_block; } free (av_stk); @@ -235,6 +238,18 @@ _gdbm_avail_list_size (GDBM_FILE dbf, size_t min_size) return lines; } +static void +av_table_display (avail_elem *av_table, int count, FILE *fp) +{ + int i; + + for (i = 0; i < count; i++) + { + fprintf (fp, " %15d %10lu \n", + av_table[i].av_size, (unsigned long) av_table[i].av_adr); + } +} + void _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) { @@ -246,12 +261,7 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) /* Print the the header avail block. */ fprintf (fp, _("\nheader block\nsize = %d\ncount = %d\n"), dbf->header->avail.size, dbf->header->avail.count); - for (temp = 0; temp < dbf->header->avail.count; temp++) - { - fprintf (fp, " %15d %10lu \n", - dbf->header->avail.av_table[temp].av_size, - (unsigned long) dbf->header->avail.av_table[temp].av_adr); - } + av_table_display (dbf->header->avail.av_table, dbf->header->avail.count, fp); /* Initialize the variables for a pass throught the avail stack. */ temp = dbf->header->avail.next_block; @@ -280,11 +290,10 @@ _gdbm_print_avail_list (FILE *fp, GDBM_FILE dbf) /* Print the block! */ fprintf (fp, _("\nblock = %d\nsize = %d\ncount = %d\n"), temp, av_stk->size, av_stk->count); - for (temp = 0; temp < av_stk->count; temp++) - { - fprintf (fp, " %15d %10lu \n", av_stk->av_table[temp].av_size, - (unsigned long) av_stk->av_table[temp].av_adr); - } + if (gdbm_avail_block_valid_p (av_stk)) + av_table_display (av_stk->av_table, av_stk->count, fp); + else + terror (_("invalid avail_block")); temp = av_stk->next_block; } free (av_stk); |