]> git.michaelhowe.org Git - packages/o/openafs.git/commitdiff
dir: Protect against circular hash chains
authorSimon Wilkinson <sxw@your-file-system.com>
Fri, 15 Apr 2011 18:40:45 +0000 (19:40 +0100)
committerDerrick Brashear <shadow@dementix.org>
Sun, 14 Aug 2011 02:16:49 +0000 (19:16 -0700)
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 <buildbot@rampaginggeek.com>
Reviewed-by: Derrick Brashear <shadow@dementix.org>
src/dir/dir.c

index 45156b6aaeabf4970f7fb65ee33abd3cb82b5202..b39483157a473a426a63d41b2e5a6084c9277af8 100644 (file)
@@ -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);