OSDN Git Service

CVE-2008-3275 Linux kernel local filesystem DoS
authorEugene Teo <eugeneteo@kernel.sg>
Tue, 14 Oct 2008 07:04:08 +0000 (15:04 +0800)
committerWilly Tarreau <w@1wt.eu>
Sun, 19 Oct 2008 19:57:43 +0000 (21:57 +0200)
This is a backport for CVE-2008-3275.

"Lookup can install a child dentry for a deleted directory.  This keeps
the directory dentry alive, and the inode pinned in the cache and on
disk, even after all external references have gone away.

This isn't a big problem normally, since memory pressure or umount
will clear out the directory dentry and its children, releasing the
inode.  But for UBIFS this causes problems because its orphan area can
overflow.

Fix this by returning ENOENT for all lookups on a S_DEAD directory
before creating a child dentry."

Signed-off-by: Eugene Teo <eugeneteo@kernel.sg>
[ WT: problem and fix confirmed on ramfs using method described
  at http://lkml.org/lkml/2008/7/2/83 ]
Signed-off-by: Willy Tarreau <w@1wt.eu>
fs/namei.c

index 374b767..5433162 100644 (file)
@@ -296,7 +296,14 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, i
         */
        result = d_lookup(parent, name);
        if (!result) {
         */
        result = d_lookup(parent, name);
        if (!result) {
-               struct dentry * dentry = d_alloc(parent, name);
+               struct dentry *dentry;
+
+               /* Don't create child dentry for a dead directory. */
+               result = ERR_PTR(-ENOENT);
+               if (IS_DEADDIR(dir))
+                       goto out_unlock;
+
+               dentry = d_alloc(parent, name);
                result = ERR_PTR(-ENOMEM);
                if (dentry) {
                        lock_kernel();
                result = ERR_PTR(-ENOMEM);
                if (dentry) {
                        lock_kernel();
@@ -307,6 +314,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, i
                        else
                                result = dentry;
                }
                        else
                                result = dentry;
                }
+       out_unlock:
                up(&dir->i_sem);
                return result;
        }
                up(&dir->i_sem);
                return result;
        }
@@ -798,7 +806,14 @@ struct dentry * lookup_hash(struct qstr *name, struct dentry * base)
 
        dentry = cached_lookup(base, name, 0);
        if (!dentry) {
 
        dentry = cached_lookup(base, name, 0);
        if (!dentry) {
-               struct dentry *new = d_alloc(base, name);
+               struct dentry *new;
+
+               /* Don't create child dentry for a dead directory. */
+               dentry = ERR_PTR(-ENOENT);
+               if (IS_DEADDIR(inode))
+                       goto out;
+
+               new = d_alloc(base, name);
                dentry = ERR_PTR(-ENOMEM);
                if (!new)
                        goto out;
                dentry = ERR_PTR(-ENOMEM);
                if (!new)
                        goto out;