From 14d706b796c379e2347aed6a5103e2b0af6c839d Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 25 Nov 2007 10:36:51 +0100 Subject: [PATCH] Add upper limits to hash chain and freelist lengths This prevents completely endless loops, but it also might render some valid tdb's as invalid. --- source/lib/tdb/common/freelist.c | 14 ++++++++++++++ source/lib/tdb/common/tdb.c | 7 +++++++ source/lib/tdb/common/tdb_private.h | 2 ++ source/lib/tdb/common/traverse.c | 7 +++++++ 4 files changed, 30 insertions(+), 0 deletions(-) diff --git a/source/lib/tdb/common/freelist.c b/source/lib/tdb/common/freelist.c index b109643..078f5e9 100644 --- a/source/lib/tdb/common/freelist.c +++ b/source/lib/tdb/common/freelist.c @@ -61,6 +61,7 @@ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next) { tdb_off_t last_ptr, i; + int freelist_length = 0; /* read in the freelist top */ last_ptr = FREELIST_TOP; @@ -71,6 +72,12 @@ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_ } /* Follow chain (next offset is at start of record) */ last_ptr = i; + + if (freelist_length++ > TDB_MAX_FREELIST_LENGTH) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "remove_from_freelist: " + "cyclic freelist")); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } } TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); @@ -261,6 +268,7 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st tdb_off_t rec_ptr, last_ptr; tdb_len_t rec_len; } bestfit; + int freelist_length = 0; if (tdb_lock(tdb, -1, F_WRLCK) == -1) return 0; @@ -307,6 +315,12 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st /* move to the next record */ last_ptr = rec_ptr; rec_ptr = rec->next; + + if (freelist_length++ > TDB_MAX_FREELIST_LENGTH) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_allocate: " + "cyclic freelist")); + goto fail; + } } if (bestfit.rec_ptr != 0) { diff --git a/source/lib/tdb/common/tdb.c b/source/lib/tdb/common/tdb.c index 0e9d1db..dc4a82e 100644 --- a/source/lib/tdb/common/tdb.c +++ b/source/lib/tdb/common/tdb.c @@ -79,6 +79,7 @@ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, struct list_struct *r) { tdb_off_t rec_ptr; + int chain_length = 0; /* read in the hash top */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) @@ -97,6 +98,12 @@ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, return rec_ptr; } rec_ptr = r->next; + + if (chain_length++ > TDB_MAX_HASHCHAIN_LENGTH) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_find: " + "cyclic chain")); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } } return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); } diff --git a/source/lib/tdb/common/tdb_private.h b/source/lib/tdb/common/tdb_private.h index 58c30c1..bd04c95 100644 --- a/source/lib/tdb/common/tdb_private.h +++ b/source/lib/tdb/common/tdb_private.h @@ -59,6 +59,8 @@ typedef uint32_t tdb_off_t; #define TDB_SEQNUM_OFS offsetof(struct tdb_header, sequence_number) #define TDB_PAD_BYTE 0x42 #define TDB_PAD_U32 0x42424242 +#define TDB_MAX_HASHCHAIN_LENGTH (1000000) +#define TDB_MAX_FREELIST_LENGTH (10000000) /* NB assumes there is a local variable called "tdb" that is the * current context, also takes doubly-parenthesized print-style diff --git a/source/lib/tdb/common/traverse.c b/source/lib/tdb/common/traverse.c index 6fc576a..2b9a4a2 100644 --- a/source/lib/tdb/common/traverse.c +++ b/source/lib/tdb/common/traverse.c @@ -35,6 +35,7 @@ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tloc /* Lock each chain from the start one. */ for (; tlock->hash < tdb->header.hash_size; tlock->hash++) { + int chain_length = 0; if (!tlock->off && tlock->hash != 0) { /* this is an optimisation for the common case where the hash chain is empty, which is particularly @@ -116,6 +117,12 @@ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tloc if (!(tdb->read_only || tdb->traverse_read) && tdb_do_delete(tdb, current, rec) != 0) goto fail; + + if (chain_length++ > TDB_MAX_HASHCHAIN_LENGTH) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, + "tdb_next_lock: cyclic chain")); + goto fail; + } } tdb_unlock(tdb, tlock->hash, tlock->lock_rw); want_next = 0; -- 1.5.3.4