[2/4]  ext2: fix rec_len overflow
         - prevent rec_len from overflow with 64KB blocksize


Signed-off-by: Takashi Sato <[EMAIL PROTECTED]>
Signed-off-by: Mingming Cao <[EMAIL PROTECTED]>

---
 fs/ext2/dir.c           |   46 ++++++++++++++++++++++++++++++++++++----------
 include/linux/ext2_fs.h |   13 +++++++++++++
 2 files changed, 49 insertions(+), 10 deletions(-)

Index: linux-2.6.23-rc3/fs/ext2/dir.c
===================================================================
--- linux-2.6.23-rc3.orig/fs/ext2/dir.c 2007-08-12 21:25:24.000000000 -0700
+++ linux-2.6.23-rc3/fs/ext2/dir.c      2007-08-29 15:29:51.000000000 -0700
@@ -94,9 +94,9 @@ static void ext2_check_page(struct page 
                        goto out;
        }
        for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {
+               offs = EXT2_DIR_ADJUST_TAIL_OFFS(offs, chunk_size);
                p = (ext2_dirent *)(kaddr + offs);
                rec_len = le16_to_cpu(p->rec_len);
-
                if (rec_len < EXT2_DIR_REC_LEN(1))
                        goto Eshort;
                if (rec_len & 3)
@@ -108,6 +108,7 @@ static void ext2_check_page(struct page 
                if (le32_to_cpu(p->inode) > max_inumber)
                        goto Einumber;
        }
+       offs = EXT2_DIR_ADJUST_TAIL_OFFS(offs, chunk_size);
        if (offs != limit)
                goto Eend;
 out:
@@ -283,6 +284,7 @@ ext2_readdir (struct file * filp, void *
                de = (ext2_dirent *)(kaddr+offset);
                limit = kaddr + ext2_last_byte(inode, n) - EXT2_DIR_REC_LEN(1);
                for ( ;(char*)de <= limit; de = ext2_next_entry(de)) {
+                       de = EXT2_DIR_ADJUST_TAIL_ADDR(kaddr, de, 
sb->s_blocksize);
                        if (de->rec_len == 0) {
                                ext2_error(sb, __FUNCTION__,
                                        "zero-length directory entry");
@@ -305,8 +307,10 @@ ext2_readdir (struct file * filp, void *
                                        return 0;
                                }
                        }
+                       filp->f_pos = EXT2_DIR_ADJUST_TAIL_OFFS(filp->f_pos, 
sb->s_blocksize);
                        filp->f_pos += le16_to_cpu(de->rec_len);
                }
+               filp->f_pos = EXT2_DIR_ADJUST_TAIL_OFFS(filp->f_pos, 
sb->s_blocksize);
                ext2_put_page(page);
        }
        return 0;
@@ -343,13 +347,14 @@ struct ext2_dir_entry_2 * ext2_find_entr
                start = 0;
        n = start;
        do {
-               char *kaddr;
+               char *kaddr, *page_start;
                page = ext2_get_page(dir, n);
                if (!IS_ERR(page)) {
-                       kaddr = page_address(page);
+                       kaddr = page_start = page_address(page);
                        de = (ext2_dirent *) kaddr;
                        kaddr += ext2_last_byte(dir, n) - reclen;
                        while ((char *) de <= kaddr) {
+                               de = EXT2_DIR_ADJUST_TAIL_ADDR(page_start, de, 
dir->i_sb->s_blocksize);
                                if (de->rec_len == 0) {
                                        ext2_error(dir->i_sb, __FUNCTION__,
                                                "zero-length directory entry");
@@ -416,6 +421,7 @@ void ext2_set_link(struct inode *dir, st
        unsigned to = from + le16_to_cpu(de->rec_len);
        int err;
 
+       to = EXT2_DIR_ADJUST_TAIL_OFFS(to, inode->i_sb->s_blocksize);
        lock_page(page);
        err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
        BUG_ON(err);
@@ -446,6 +452,7 @@ int ext2_add_link (struct dentry *dentry
        char *kaddr;
        unsigned from, to;
        int err;
+       char *page_start = NULL;
 
        /*
         * We take care of directory expansion in the same loop.
@@ -460,16 +467,28 @@ int ext2_add_link (struct dentry *dentry
                if (IS_ERR(page))
                        goto out;
                lock_page(page);
-               kaddr = page_address(page);
+               kaddr = page_start = page_address(page);
                dir_end = kaddr + ext2_last_byte(dir, n);
                de = (ext2_dirent *)kaddr;
-               kaddr += PAGE_CACHE_SIZE - reclen;
+               if (chunk_size < EXT2_DIR_MAX_REC_LEN) {
+                       kaddr += PAGE_CACHE_SIZE - reclen;
+               } else {
+                       kaddr += PAGE_CACHE_SIZE - 
+                               (chunk_size - EXT2_DIR_MAX_REC_LEN) - reclen;
+               }
                while ((char *)de <= kaddr) {
+                       de = EXT2_DIR_ADJUST_TAIL_ADDR(page_start, de, 
chunk_size);     
                        if ((char *)de == dir_end) {
                                /* We hit i_size */
                                name_len = 0;
-                               rec_len = chunk_size;
-                               de->rec_len = cpu_to_le16(chunk_size);
+                               if (chunk_size  < EXT2_DIR_MAX_REC_LEN) {
+                                       rec_len = chunk_size;
+                                       de->rec_len = cpu_to_le16(chunk_size);
+                               } else {
+                                       rec_len = EXT2_DIR_MAX_REC_LEN;
+                                       de->rec_len =
+                                       cpu_to_le16(EXT2_DIR_MAX_REC_LEN);
+                               }
                                de->inode = 0;
                                goto got_it;
                        }
@@ -499,6 +518,7 @@ int ext2_add_link (struct dentry *dentry
 got_it:
        from = (char*)de - (char*)page_address(page);
        to = from + rec_len;
+       to = EXT2_DIR_ADJUST_TAIL_OFFS(to, chunk_size);
        err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
        if (err)
                goto out_unlock;
@@ -541,6 +561,7 @@ int ext2_delete_entry (struct ext2_dir_e
        ext2_dirent * de = (ext2_dirent *) (kaddr + from);
        int err;
 
+       to = EXT2_DIR_ADJUST_TAIL_OFFS(to, inode->i_sb->s_blocksize);
        while ((char*)de < (char*)dir) {
                if (de->rec_len == 0) {
                        ext2_error(inode->i_sb, __FUNCTION__,
@@ -598,7 +619,11 @@ int ext2_make_empty(struct inode *inode,
 
        de = (struct ext2_dir_entry_2 *)(kaddr + EXT2_DIR_REC_LEN(1));
        de->name_len = 2;
-       de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1));
+       if (chunk_size < EXT2_DIR_MAX_REC_LEN) {
+               de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1));
+       } else {
+               de->rec_len = cpu_to_le16(EXT2_DIR_MAX_REC_LEN - 
EXT2_DIR_REC_LEN(1));
+       }
        de->inode = cpu_to_le32(parent->i_ino);
        memcpy (de->name, "..\0", 4);
        ext2_set_de_type (de, inode);
@@ -618,18 +643,19 @@ int ext2_empty_dir (struct inode * inode
        unsigned long i, npages = dir_pages(inode);
 
        for (i = 0; i < npages; i++) {
-               char *kaddr;
+               char *kaddr, *page_start;
                ext2_dirent * de;
                page = ext2_get_page(inode, i);
 
                if (IS_ERR(page))
                        continue;
 
-               kaddr = page_address(page);
+               kaddr = page_start = page_address(page);
                de = (ext2_dirent *)kaddr;
                kaddr += ext2_last_byte(inode, i) - EXT2_DIR_REC_LEN(1);
 
                while ((char *)de <= kaddr) {
+                       de = EXT2_DIR_ADJUST_TAIL_ADDR(page_start, de, 
inode->i_sb->s_blocksize);
                        if (de->rec_len == 0) {
                                ext2_error(inode->i_sb, __FUNCTION__,
                                        "zero-length directory entry");
Index: linux-2.6.23-rc3/include/linux/ext2_fs.h
===================================================================
--- linux-2.6.23-rc3.orig/include/linux/ext2_fs.h       2007-08-29 
15:22:29.000000000 -0700
+++ linux-2.6.23-rc3/include/linux/ext2_fs.h    2007-08-29 15:29:51.000000000 
-0700
@@ -557,5 +557,18 @@ enum {
 #define EXT2_DIR_ROUND                         (EXT2_DIR_PAD - 1)
 #define EXT2_DIR_REC_LEN(name_len)     (((name_len) + 8 + EXT2_DIR_ROUND) & \
                                         ~EXT2_DIR_ROUND)
+#define        EXT2_DIR_MAX_REC_LEN            65532
+
+/*
+ * Align a tail offset(address) to the end of a directory block
+ */
+#define EXT2_DIR_ADJUST_TAIL_OFFS(offs, bsize) \
+       ((((offs) & ((bsize) -1)) == EXT2_DIR_MAX_REC_LEN) ? \
+       ((offs) + (bsize) - EXT2_DIR_MAX_REC_LEN):(offs))
+
+#define EXT2_DIR_ADJUST_TAIL_ADDR(page, de, bsize) \
+       (((((char*)(de) - (page)) & ((bsize) - 1)) == EXT2_DIR_MAX_REC_LEN) ? \
+       ((ext2_dirent*)((char*)(de) + (bsize) - EXT2_DIR_MAX_REC_LEN)):(de))
 
 #endif /* _LINUX_EXT2_FS_H */
+


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to