aboutsummaryrefslogtreecommitdiff
path: root/src/gdbmstore.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-05-26 22:17:10 +0200
committerSergey Poznyakoff <gray@gnu.org>2018-05-27 17:16:03 +0200
commit4011b4ce9a4c4fa505843381d43a1157cf0c782e (patch)
tree3b12f036a9dabd412a0ada3d6e0c07f44a2dfc91 /src/gdbmstore.c
parentd100aa7a304913fa1692abaa2818418409f3668a (diff)
downloadgdbm-4011b4ce9a4c4fa505843381d43a1157cf0c782e.tar.gz
gdbm-4011b4ce9a4c4fa505843381d43a1157cf0c782e.tar.bz2
Additional validation of avail_table.
Verify that avail_table is sorted by size and that each element's size falls within allowed range. * src/bucket.c (gdbm_get_bucket): Fix bucket validation. Validate bucket_avail. (_gdbm_split_bucket): Check return from _gdbm_free. * src/falloc.c (adjust_bucket_avail,_gdbm_free): Return error code. All uses updated. (pop_avail_block): Fix eventual memory leak. Use gdbm_avail_block_validate. * src/gdbmdefs.h (gdbm_avail_table_valid_p): Change signature. * src/gdbmopen.c (gdbm_avail_table_valid_p): Traverse the array verifying address and size of each element. (gdbm_avail_block_validate) (gdbm_bucket_avail_table_validate): New functions. (validate_header): Remove call to gdbm_avail_block_valid_p. Avail_block is validated later, after it's been loaded. Bail out if header->next_block does not equal the file size. (gdbm_fd_open): Validate avail_block. * src/gdbmstore.c (_gdbm_store): Check return from _gdbm_free. Avoid endless loop in case of inconsistent h_table. * src/gdbmtool.c (_gdbm_print_avail_list): Use gdbm_avail_block_validate. * src/proto.h: Update. * tests/gtload.c: Improve error diagnostics.
Diffstat (limited to 'src/gdbmstore.c')
-rw-r--r--src/gdbmstore.c31
1 files changed, 21 insertions, 10 deletions
diff --git a/src/gdbmstore.c b/src/gdbmstore.c
index 7b2ed04..2ed1616 100644
--- a/src/gdbmstore.c
+++ b/src/gdbmstore.c
@@ -25,18 +25,19 @@
/* Add a new element to the database. CONTENT is keyed by KEY. The
file on disk is updated to reflect the structure of the new database
before returning from this procedure. The FLAGS define the action to
take when the KEY is already in the database. The value GDBM_REPLACE
asks that the old data be replaced by the new CONTENT. The value
- GDBM_INSERT asks that an error be returned and no action taken. A
- return value of 0 means no errors. A return value of -1 means that
- the item was not stored in the data base because the caller was not an
- official writer. A return value of 0 means that the item was not stored
- because the argument FLAGS was GDBM_INSERT and the KEY was already in
- the database. */
+ GDBM_INSERT asks that an error be returned and no action taken.
+
+ On success (the item was stored), 0 is returned. If the item could
+ not be stored because a matching key already exists and GDBM_REPLACE
+ was not given, 1 is returned and gdbm_errno (as well as the database
+ errno value) is set to GDBM_CANNOT_REPLACE. Otherwise, if another
+ error occurred, -1 is returned. */
int
gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags)
{
int new_hash_val; /* The new hash value. */
int elem_loc; /* The location in hash bucket. */
@@ -88,13 +89,14 @@ gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags)
/* Just replace the data. */
free_adr = dbf->bucket->h_table[elem_loc].data_pointer;
free_size = dbf->bucket->h_table[elem_loc].key_size
+ dbf->bucket->h_table[elem_loc].data_size;
if (free_size != new_size)
{
- _gdbm_free (dbf, free_adr, free_size);
+ if (_gdbm_free (dbf, free_adr, free_size))
+ return -1;
}
else
{
/* Just reuse the same address! */
file_adr = free_adr;
}
@@ -120,24 +122,33 @@ gdbm_store (GDBM_FILE dbf, datum key, datum content, int flags)
return -1;
}
/* If this is a new entry in the bucket, we need to do special things. */
if (elem_loc == -1)
{
+ int start_loc;
+
if (dbf->bucket->count == dbf->header->bucket_elems)
{
/* Split the current bucket. */
if (_gdbm_split_bucket (dbf, new_hash_val))
return -1;
}
/* Find space to insert into bucket and set elem_loc to that place. */
- elem_loc = new_hash_val % dbf->header->bucket_elems;
+ elem_loc = start_loc = new_hash_val % dbf->header->bucket_elems;
while (dbf->bucket->h_table[elem_loc].hash_value != -1)
- elem_loc = (elem_loc + 1) % dbf->header->bucket_elems;
-
+ {
+ elem_loc = (elem_loc + 1) % dbf->header->bucket_elems;
+ if (elem_loc == start_loc)
+ {
+ GDBM_SET_ERRNO (dbf, GDBM_BAD_HASH_TABLE, TRUE);
+ return -1;
+ }
+ }
+
/* We now have another element in the bucket. Add the new information.*/
dbf->bucket->count++;
dbf->bucket->h_table[elem_loc].hash_value = new_hash_val;
memcpy (dbf->bucket->h_table[elem_loc].key_start, key.dptr,
(SMALL < key.dsize ? SMALL : key.dsize));
}

Return to:

Send suggestions and report system problems to the System administrator.