From e32e873b89e2d7f73e17fbc7d1f89501ad5afc2c Mon Sep 17 00:00:00 2001 From: Simon Wilkinson Date: Fri, 15 Apr 2011 19:40:45 +0100 Subject: [PATCH] dir: Protect against circular hash chains The dir package didn't protect against circular hash chains when performing directory lookups. A corrupt directory could therefore cause a client or a fileserver to go into an endless loop if that directory contained a loop in its hash chain pointers. Fix this by exiting the lookup if the hash chain has more elements than the total number of entries in a directory. This maximum number of entries is taken as being (number of entries per page) * (max number of pages), which is considerably more than the real maximum value. (cherry picked from commit bb25bdfcb059fc54a57fd4733ce3184e231ca88d) Change-Id: I7290dbaad66bccfe3b03a843184464f0681f9429 Reviewed-on: http://gerrit.openafs.org/5250 Tested-by: BuildBot Reviewed-by: Derrick Brashear --- src/dir/dir.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/dir/dir.c b/src/dir/dir.c index 45156b6aa..b39483157 100644 --- a/src/dir/dir.c +++ b/src/dir/dir.c @@ -403,6 +403,7 @@ EnumerateDir(void *dir, int (*hookproc) (void *dir, char *name, struct DirHeader *dhp; struct DirEntry *ep; int code = 0; + int elements; dhp = (struct DirHeader *)DRead(dir, 0); if (!dhp) @@ -411,7 +412,10 @@ EnumerateDir(void *dir, int (*hookproc) (void *dir, char *name, for (i = 0; i < NHASHENT; i++) { /* For each hash chain, enumerate everyone on the list. */ num = ntohs(dhp->hashTable[i]); - while (num != 0) { + elements = 0; + while (num != 0 && elements < BIGMAXPAGES * EPP) { + elements++; + /* Walk down the hash table list. */ code = GetVerifiedBlob(dir, num, &ep); if (code) @@ -443,6 +447,7 @@ IsEmpty(void *dir) struct DirHeader *dhp; struct DirEntry *ep; int code; + int elements; dhp = (struct DirHeader *)DRead(dir, 0); if (!dhp) @@ -450,7 +455,9 @@ IsEmpty(void *dir) for (i = 0; i < NHASHENT; i++) { /* For each hash chain, enumerate everyone on the list. */ num = ntohs(dhp->hashTable[i]); - while (num != 0) { + elements = 0; + while (num != 0 && elements < BIGMAXPAGES * EPP) { + elements++; /* Walk down the hash table list. */ code = GetVerifiedBlob(dir, num, &ep); if (code) @@ -572,6 +579,7 @@ FindItem(void *dir, char *ename, unsigned short **previtem) struct DirHeader *dhp; unsigned short *lp; struct DirEntry *tp; + int elements; i = DirHash(ename); dhp = (struct DirHeader *)DRead(dir, 0); @@ -590,26 +598,29 @@ FindItem(void *dir, char *ename, unsigned short **previtem) } lp = &(dhp->hashTable[i]); - while (1) { + elements = 0; + /* Detect circular hash chains. Absolute max size of a directory */ + while (elements < BIGMAXPAGES * EPP) { + elements++; + /* Look at each hash conflict entry. */ if (!strcmp(ename, tp->name)) { /* Found our entry. */ *previtem = lp; return tp; } + DRelease(lp, 0); lp = &(tp->next); - if (tp->next == 0) { - /* The end of the line */ - DRelease(lp, 0); /* Release all locks. */ - return 0; - } + if (tp->next == 0) + break; + GetVerifiedBlob(dir, (u_short) ntohs(tp->next), &tp); - if (tp == NULL) { - DRelease(lp, 0); - return NULL; - } + if (tp == NULL) + break; } + DRelease(lp, 0); + return NULL; } static struct DirEntry * @@ -624,6 +635,7 @@ FindFid (void *dir, afs_uint32 vnode, afs_uint32 unique) struct DirHeader *dhp; unsigned short *lp; struct DirEntry *tp; + int elements; dhp = (struct DirHeader *) DRead(dir,0); if (!dhp) return 0; @@ -634,7 +646,9 @@ FindFid (void *dir, afs_uint32 vnode, afs_uint32 unique) DRelease(dhp, 0); return 0; } - while(tp) { + elements = 0; + while(tp && elements < BIGMAXPAGES * EPP) { + elements++; if (vnode == ntohl(tp->fid.vnode) && unique == ntohl(tp->fid.vunique)) { DRelease(dhp, 0); -- 2.39.5