[31589] in CVS-changelog-for-Kerberos-V5

home help back first fref pref prev next nref lref last post

krb5 commit: Improve libdb2 data validation

daemon@ATHENA.MIT.EDU (ghudson@mit.edu)
Fri Oct 30 13:01:43 2026

From: ghudson@mit.edu
To: cvs-krb5@mit.edu
Message-Id: <20261030065121.A5EF210582F@krbdev.mit.edu>
Date: Fri, 30 Oct 2026 02:51:21 -0400 (EDT)
MIME-Version: 1.0
Reply-To: krbdev@mit.edu
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: cvs-krb5-bounces@mit.edu

https://github.com/krb5/krb5/commit/64470b21a544f1619673373c74efd96c99778028
commit 64470b21a544f1619673373c74efd96c99778028
Author: Bogdan Boguslavskij <bogdanb@altlinux.org>
Date:   Mon Oct 5 21:25:42 2026 -0400

    Improve libdb2 data validation
    
    In the btree module, make __bt_pgin() bounds-check h->lower and reset
    the page to empty if it is not within the allowable range.  Change the
    definition of NEXTINDEX to clamp the result lower bound to 0, so that
    it can't return an enormous offset due to unsigned underflow.  In
    __bt_search() and __bt_seqset(), detect when we fill the parent page
    stack (because the database is circular or the tree is unexpectedly
    imbalanced) and return an error.
    
    In the hash module, make __kdb2_hash_open() reject a block size of
    zero to prevent division by zero, and reject an invalid overflow
    pointer to prevent an out-of-bounds memory access.  Prevent unbounded
    recursion in collect_key() and collect_data() by adding a depth
    counter and limiting it to max_bucket.  Prevent out-of-bounds access
    in __pgin_routine() by bounds-checking num_ent for non-bitmap pages.
    
    [ghudson@mit.edu: combined several commits and rewrote commit message;
    simplified __bt_pgin() bounds check and made it leave h->lower == 0
    alone for nroot(); added checks for full parent page stack; made minor
    edits for code style]
    
    ticket: 9249 (new)
    tags: pullup
    target_version: 1.22-next

 src/plugins/kdb/db2/libdb2/btree/bt_conv.c    | 10 +++++++++-
 src/plugins/kdb/db2/libdb2/btree/bt_search.c  |  2 ++
 src/plugins/kdb/db2/libdb2/btree/bt_seq.c     |  8 ++++++++
 src/plugins/kdb/db2/libdb2/btree/btree.h      |  8 ++++++--
 src/plugins/kdb/db2/libdb2/hash/hash.c        |  8 +++++++-
 src/plugins/kdb/db2/libdb2/hash/hash_bigkey.c | 23 ++++++++++++++---------
 src/plugins/kdb/db2/libdb2/hash/hash_page.c   | 13 +++++++++++++
 7 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/src/plugins/kdb/db2/libdb2/btree/bt_conv.c b/src/plugins/kdb/db2/libdb2/btree/bt_conv.c
index 99c4af56c..cf3077b8e 100644
--- a/src/plugins/kdb/db2/libdb2/btree/bt_conv.c
+++ b/src/plugins/kdb/db2/libdb2/btree/bt_conv.c
@@ -61,13 +61,14 @@ static void mswap __P((PAGE *));
 void
 __bt_pgin(void *t, db_pgno_t pg, void *pp)
 {
+	BTREE *bt = t;
 	PAGE *h;
 	indx_t i, top;
 	u_char flags;
 	char *p;
 	u_int32_t ksize;
 
-	if (!F_ISSET(((BTREE *)t), B_NEEDSWAP))
+	if (!F_ISSET(bt, B_NEEDSWAP))
 		return;
 	if (pg == P_META) {
 		mswap(pp);
@@ -82,6 +83,13 @@ __bt_pgin(void *t, db_pgno_t pg, void *pp)
 	M_16_SWAP(h->lower);
 	M_16_SWAP(h->upper);
 
+	/* Do nothing if the page appears uninitialized (h->lower == 0).  Treat
+	 * the page as having no entries if h->lower is invalid. */
+	if (h->lower < BTDATAOFF || h->lower > bt->bt_psize) {
+		h->lower = (h->lower == 0) ? 0 : BTDATAOFF;
+		return;
+	}
+
 	top = NEXTINDEX(h);
 	if ((h->flags & P_TYPE) == P_BINTERNAL)
 		for (i = 0; i < top; i++) {
diff --git a/src/plugins/kdb/db2/libdb2/btree/bt_search.c b/src/plugins/kdb/db2/libdb2/btree/bt_search.c
index ed512ccb6..87883d24a 100644
--- a/src/plugins/kdb/db2/libdb2/btree/bt_search.c
+++ b/src/plugins/kdb/db2/libdb2/btree/bt_search.c
@@ -128,6 +128,8 @@ __bt_search(BTREE *t, const DBT *key, int *exactp)
 next:		BT_PUSH(t, h->pgno, idx);
 		pg = GETBINTERNAL(h, idx)->pgno;
 		mpool_put(t->bt_mp, h, 0);
+		if (BT_STACKFULL(t))
+			return (NULL);
 	}
 }
 
diff --git a/src/plugins/kdb/db2/libdb2/btree/bt_seq.c b/src/plugins/kdb/db2/libdb2/btree/bt_seq.c
index 97db44abc..c6d26ca99 100644
--- a/src/plugins/kdb/db2/libdb2/btree/bt_seq.c
+++ b/src/plugins/kdb/db2/libdb2/btree/bt_seq.c
@@ -218,6 +218,10 @@ __bt_seqset(BTREE *t, EPG *ep, DBT *key, int flags)
 			pg = GETBINTERNAL(h, 0)->pgno;
 			BT_PUSH(t, h->pgno, 0);
 			mpool_put(t->bt_mp, h, 0);
+			if (BT_STACKFULL(t)) {
+				errno = E2BIG;
+				return (RET_ERROR);
+			}
 		}
 		ep->page = h;
 		ep->index = 0;
@@ -242,6 +246,10 @@ __bt_seqset(BTREE *t, EPG *ep, DBT *key, int flags)
 			pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
 			BT_PUSH(t, h->pgno, NEXTINDEX(h) - 1);
 			mpool_put(t->bt_mp, h, 0);
+			if (BT_STACKFULL(t)) {
+				errno = E2BIG;
+				return (RET_ERROR);
+			}
 		}
 
 		ep->page = h;
diff --git a/src/plugins/kdb/db2/libdb2/btree/btree.h b/src/plugins/kdb/db2/libdb2/btree/btree.h
index 37dc4fda1..ab31cb092 100644
--- a/src/plugins/kdb/db2/libdb2/btree/btree.h
+++ b/src/plugins/kdb/db2/libdb2/btree/btree.h
@@ -87,7 +87,9 @@ typedef struct _page {
 #define	BTDATAOFF							\
 	(sizeof(db_pgno_t) + sizeof(db_pgno_t) + sizeof(db_pgno_t) +		\
 	    sizeof(u_int32_t) + sizeof(indx_t) + sizeof(indx_t))
-#define	NEXTINDEX(p)	(((p)->lower - BTDATAOFF) / sizeof(indx_t))
+#define NEXTINDEX(p)						\
+	((p)->lower < BTDATAOFF ? 0 :				\
+	 (((p)->lower - BTDATAOFF) / sizeof(indx_t)))
 
 /*
  * For pages other than overflow pages, there is an array of offsets into the
@@ -313,6 +315,7 @@ typedef struct _btree {
 
 	CURSOR	  bt_cursor;		/* cursor */
 
+#define	BT_STACKSIZE 50
 #define	BT_PUSH(t, p, i) {						\
 	t->bt_sp->pgno = p; 						\
 	t->bt_sp->index = i; 						\
@@ -320,7 +323,8 @@ typedef struct _btree {
 }
 #define	BT_POP(t)	(t->bt_sp == t->bt_stack ? NULL : --t->bt_sp)
 #define	BT_CLR(t)	(t->bt_sp = t->bt_stack)
-	EPGNO	  bt_stack[50];		/* stack of parent pages */
+#define	BT_STACKFULL(t)	(t->bt_sp == t->bt_stack + BT_STACKSIZE)
+	EPGNO	  bt_stack[BT_STACKSIZE]; /* stack of parent pages */
 	EPGNO	 *bt_sp;		/* current stack pointer */
 
 	DBT	  bt_rkey;		/* returned key */
diff --git a/src/plugins/kdb/db2/libdb2/hash/hash.c b/src/plugins/kdb/db2/libdb2/hash/hash.c
index f90aae0f9..1bef40cc5 100644
--- a/src/plugins/kdb/db2/libdb2/hash/hash.c
+++ b/src/plugins/kdb/db2/libdb2/hash/hash.c
@@ -149,7 +149,7 @@ __kdb2_hash_open(const char *file, int flags, int mode, const HASHINFO *info,
 		    sizeof(HASHHDR))
 			RETURN_ERROR(EFTYPE, error1);
 
-		/* Verify file type, versions and hash function */
+		/* Verify file type, versions, hash function, block size */
 		if (hashp->hdr.magic != HASHMAGIC)
 			RETURN_ERROR(EFTYPE, error1);
 #define	OLDHASHVERSION	1
@@ -159,12 +159,18 @@ __kdb2_hash_open(const char *file, int flags, int mode, const HASHINFO *info,
 		if (hashp->hash(CHARKEY, sizeof(CHARKEY))
 		    != hashp->hdr.h_charkey)
 			RETURN_ERROR(EFTYPE, error1);
+		if (hashp->hdr.bsize == 0)
+			RETURN_ERROR(EFTYPE, error1);
 		/*
 		 * Figure out how many segments we need.  Max_Bucket is the
 		 * maximum bucket number, so the number of buckets is
 		 * max_bucket + 1.
 		 */
 
+		if (hashp->hdr.ovfl_point < 0 ||
+		    hashp->hdr.ovfl_point >= NCACHED)
+			RETURN_ERROR(EFTYPE, error1);
+
 		/* Read in bitmaps */
 		bpages = (hashp->hdr.spares[hashp->hdr.ovfl_point] +
 		    (hashp->hdr.bsize << BYTE_SHIFT) - 1) >>
diff --git a/src/plugins/kdb/db2/libdb2/hash/hash_bigkey.c b/src/plugins/kdb/db2/libdb2/hash/hash_bigkey.c
index 6befb7a57..30b2ca947 100644
--- a/src/plugins/kdb/db2/libdb2/hash/hash_bigkey.c
+++ b/src/plugins/kdb/db2/libdb2/hash/hash_bigkey.c
@@ -69,8 +69,9 @@ static char sccsid[] = "@(#)hash_bigkey.c	8.5 (Berkeley) 11/2/95";
 #include "page.h"
 #include "extern.h"
 
-static int32_t collect_key __P((HTAB *, PAGE16 *, int32_t, db_pgno_t *));
-static int32_t collect_data __P((HTAB *, PAGE16 *, int32_t));
+static int32_t collect_key __P((HTAB *, PAGE16 *, int32_t, db_pgno_t *,
+				u_int32_t));
+static int32_t collect_data __P((HTAB *, PAGE16 *, int32_t, u_int32_t));
 
 /*
  * Big_insert
@@ -260,7 +261,7 @@ __big_keydata(HTAB *hashp, PAGE16 *pagep, DBT *key, DBT *val, int32_t ndx)
 	    __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW);
 	if (!key_pagep)
 		return (-1);
-	key->size = collect_key(hashp, key_pagep, 0, &last_page);
+	key->size = collect_key(hashp, key_pagep, 0, &last_page, 0);
 	key->data = hashp->bigkey_buf;
 	__put_page(hashp, key_pagep, A_RAW, 0);
 
@@ -292,7 +293,7 @@ __get_bigkey(hashp, pagep, ndx, key)
 	    __get_page(hashp, OADDR_TO_PAGE(DATA_OFF(pagep, ndx)), A_RAW);
 	if (!key_pagep)
 		return (-1);
-	key->size = collect_key(hashp, key_pagep, 0, NULL);
+	key->size = collect_key(hashp, key_pagep, 0, NULL, 0);
 	key->data = hashp->bigkey_buf;
 
 	__put_page(hashp, key_pagep, A_RAW, 0);
@@ -331,7 +332,7 @@ __big_return(HTAB *hashp, ITEM_INFO *item_info, DBT *val,
 			return (-1);
 	}
 
-	val->size = collect_data(hashp, pagep, 0);
+	val->size = collect_data(hashp, pagep, 0, 0);
 	if (val->size < 1)
 		return (-1);
 	val->data = (void *)hashp->bigdata_buf;
@@ -352,7 +353,8 @@ __big_return(HTAB *hashp, ITEM_INFO *item_info, DBT *val,
  * Return total length of data; -1 if error.
  */
 static int32_t
-collect_key(HTAB *hashp, PAGE16 *pagep, int32_t len, db_pgno_t *last_page)
+collect_key(HTAB *hashp, PAGE16 *pagep, int32_t len, db_pgno_t *last_page,
+	    u_int32_t depth)
 {
 	PAGE16 *next_pagep;
 	int32_t totlen, retval;
@@ -361,6 +363,9 @@ collect_key(HTAB *hashp, PAGE16 *pagep, int32_t len, db_pgno_t *last_page)
 	db_pgno_t save_addr;
 #endif
 
+	if (depth > hashp->hdr.max_bucket)
+		return (-1);
+
 	/* If this is the last page with key. */
 	if (BIGDATALEN(pagep)) {
 		totlen = len + BIGKEYLEN(pagep);
@@ -395,7 +400,7 @@ collect_key(HTAB *hashp, PAGE16 *pagep, int32_t len, db_pgno_t *last_page)
 #ifdef DEBUG
 	save_addr = ADDR(pagep);
 #endif
-	retval = collect_key(hashp, next_pagep, totlen, last_page);
+	retval = collect_key(hashp, next_pagep, totlen, last_page, depth + 1);
 
 #ifdef DEBUG
 	assert(save_addr == ADDR(pagep));
@@ -416,7 +421,7 @@ collect_key(HTAB *hashp, PAGE16 *pagep, int32_t len, db_pgno_t *last_page)
  * Return total length of data; -1 if error.
  */
 static int32_t
-collect_data(HTAB *hashp, PAGE16 *pagep, int32_t len)
+collect_data(HTAB *hashp, PAGE16 *pagep, int32_t len, u_int32_t depth)
 {
 	PAGE16 *next_pagep;
 	int32_t totlen, retval;
@@ -448,7 +453,7 @@ collect_data(HTAB *hashp, PAGE16 *pagep, int32_t len)
 #ifdef DEBUG
 	save_addr = ADDR(pagep);
 #endif
-	retval = collect_data(hashp, next_pagep, totlen);
+	retval = collect_data(hashp, next_pagep, totlen, depth + 1);
 #ifdef DEBUG
 	assert(save_addr == ADDR(pagep));
 #endif
diff --git a/src/plugins/kdb/db2/libdb2/hash/hash_page.c b/src/plugins/kdb/db2/libdb2/hash/hash_page.c
index dba29e0cb..c8b3f95b0 100644
--- a/src/plugins/kdb/db2/libdb2/hash/hash_page.c
+++ b/src/plugins/kdb/db2/libdb2/hash/hash_page.c
@@ -803,6 +803,7 @@ __pgin_routine(void *pg_cookie, db_pgno_t pgno, void *page)
 	HTAB *hashp;
 	PAGE16 *pagep;
 	int32_t max, i;
+	u_int16_t num_ent; /* byte-swapped NUM_ENT, used to validate before swap */
 
 	pagep = (PAGE16 *)page;
 	hashp = (HTAB *)pg_cookie;
@@ -824,6 +825,18 @@ __pgin_routine(void *pg_cookie, db_pgno_t pgno, void *page)
 		return;
 	}
 
+	if (!is_bitmap_pgno(hashp, pgno)) {
+		if (hashp->hdr.lorder == DB_BYTE_ORDER)
+			num_ent = NUM_ENT(pagep);
+		else
+			P_16_COPY(NUM_ENT(pagep), num_ent);
+		if (num_ent >
+		    (hashp->hdr.bsize - PAGE_OVERHEAD) / PAIR_OVERHEAD) {
+			page_init(hashp, pagep, pgno, HASH_PAGE);
+			return;
+		}
+	}
+
 	if (hashp->hdr.lorder == DB_BYTE_ORDER)
 		return;
 	if (is_bitmap_pgno(hashp, pgno)) {
_______________________________________________
cvs-krb5 mailing list
cvs-krb5@mit.edu
https://mailman.mit.edu/mailman/listinfo/cvs-krb5

home help back first fref pref prev next nref lref last post