[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]>
Signed-off-by: Christoph Lameter <[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-rc8-mm1/fs/ext2/dir.c
===================================================================
--- linux-2.6.23-rc8-mm1.orig/fs/ext2/dir.c     2007-09-25 15:59:34.000000000 
-0700
+++ linux-2.6.23-rc8-mm1/fs/ext2/dir.c  2007-09-25 16:02:51.000000000 -0700
@@ -105,9 +105,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)
@@ -119,6 +119,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:
@@ -294,6 +295,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");
@@ -316,8 +318,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;
@@ -354,13 +358,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");
@@ -428,6 +433,7 @@ void ext2_set_link(struct inode *dir, st
        unsigned len = le16_to_cpu(de->rec_len);
        int err;
 
+       len = EXT2_DIR_ADJUST_TAIL_OFFS(pos, len);
        lock_page(page);
        err = __ext2_write_begin(NULL, page->mapping, pos, len,
                                AOP_FLAG_UNINTERRUPTIBLE, &page, NULL);
@@ -459,6 +465,7 @@ int ext2_add_link (struct dentry *dentry
        char *kaddr;
        loff_t pos;
        int err;
+       char *page_start = NULL;
 
        /*
         * We take care of directory expansion in the same loop.
@@ -473,16 +480,29 @@ 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;
                        }
@@ -512,6 +532,7 @@ int ext2_add_link (struct dentry *dentry
 got_it:
        pos = page_offset(page) +
                (char*)de - (char*)page_address(page);
+       reclen = EXT2_DIR_ADJUST_TAIL_OFFS(pos + rec_len, chunk_size) - pos;
        err = __ext2_write_begin(NULL, page->mapping, pos, rec_len, 0,
                                                        &page, NULL);
        if (err)
@@ -556,6 +577,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__,
@@ -617,7 +639,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);
@@ -637,18 +663,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-rc8-mm1/include/linux/ext2_fs.h
===================================================================
--- linux-2.6.23-rc8-mm1.orig/include/linux/ext2_fs.h   2007-09-25 
15:59:34.000000000 -0700
+++ linux-2.6.23-rc8-mm1/include/linux/ext2_fs.h        2007-09-25 
16:01:38.000000000 -0700
@@ -561,6 +561,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))
 
 static inline ext2_fsblk_t
 ext2_group_first_block_no(struct super_block *sb, unsigned long group_no)

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

Reply via email to