From: Christoph Lameter <clameter@sgi.com>
To: Mingming Cao <cmm@us.ibm.com>
Cc: Takashi Sato <sho@tnes.nec.co.jp>, linux-fsdevel@vger.kernel.org
Subject: [patch 3/4] ext3: fix rec_len overflow with 64KB block size
Date: Tue, 25 Sep 2007 16:30:52 -0700 [thread overview]
Message-ID: <20070925233500.932740805@sgi.com> (raw)
In-Reply-To: 20070925233049.656803267@sgi.com
[-- Attachment #1: lbs_ext3_fix_64kb --]
[-- Type: text/plain, Size: 10385 bytes --]
Prevent rec_len from overflow with 64KB blocksize
Signed-off-by: Takashi Sato <sho@tnes.nec.co.jp>
Signed-off-by: Mingming Cao <cmm@us.ibm.com>
Signed-off-by: Christoph Lameter <clameter@sgi.com>
---
fs/ext3/dir.c | 13 ++++---
fs/ext3/namei.c | 88 +++++++++++++++++++++++++++++++++++++++---------
include/linux/ext3_fs.h | 9 ++++
3 files changed, 91 insertions(+), 19 deletions(-)
Index: linux-2.6.23-rc8-mm1/fs/ext3/dir.c
===================================================================
--- linux-2.6.23-rc8-mm1.orig/fs/ext3/dir.c 2007-09-25 14:53:57.000000000 -0700
+++ linux-2.6.23-rc8-mm1/fs/ext3/dir.c 2007-09-25 15:41:45.000000000 -0700
@@ -100,10 +100,11 @@ static int ext3_readdir(struct file * fi
unsigned long offset;
int i, stored;
struct ext3_dir_entry_2 *de;
- struct super_block *sb;
int err;
struct inode *inode = filp->f_path.dentry->d_inode;
int ret = 0;
+ struct super_block *sb = inode->i_sb;
+ unsigned tail = sb->s_blocksize;
sb = inode->i_sb;
@@ -167,8 +168,11 @@ revalidate:
* readdir(2), then we might be pointing to an invalid
* dirent right now. Scan from the start of the block
* to make sure. */
- if (filp->f_version != inode->i_version) {
- for (i = 0; i < sb->s_blocksize && i < offset; ) {
+ if (tail > EXT3_DIR_MAX_REC_LEN)
+ tail = EXT3_DIR_MAX_REC_LEN;
+
+ if (filp->f_version != inode->i_version) {
+ for (i = 0; i < tail && i < offset; ) {
de = (struct ext3_dir_entry_2 *)
(bh->b_data + i);
/* It's too expensive to do a full
@@ -189,7 +193,7 @@ revalidate:
}
while (!error && filp->f_pos < inode->i_size
- && offset < sb->s_blocksize) {
+ && offset < tail) {
de = (struct ext3_dir_entry_2 *) (bh->b_data + offset);
if (!ext3_check_dir_entry ("ext3_readdir", inode, de,
bh, offset)) {
@@ -225,6 +229,7 @@ revalidate:
}
filp->f_pos += le16_to_cpu(de->rec_len);
}
+ filp->f_pos = EXT3_DIR_ADJUST_TAIL_OFFS(filp->f_pos, sb->s_blocksize);
offset = 0;
brelse (bh);
}
Index: linux-2.6.23-rc8-mm1/fs/ext3/namei.c
===================================================================
--- linux-2.6.23-rc8-mm1.orig/fs/ext3/namei.c 2007-09-24 17:33:10.000000000 -0700
+++ linux-2.6.23-rc8-mm1/fs/ext3/namei.c 2007-09-25 15:41:45.000000000 -0700
@@ -263,9 +263,13 @@ static struct stats dx_show_leaf(struct
unsigned names = 0, space = 0;
char *base = (char *) de;
struct dx_hash_info h = *hinfo;
+ unsigned tail = size;
printk("names: ");
- while ((char *) de < base + size)
+ if (tail > EXT3_DIR_MAX_REC_LEN)
+ tail = EXT3_DIR_MAX_REC_LEN;
+
+ while ((char *) de < base + tail)
{
if (de->inode)
{
@@ -708,8 +712,12 @@ static int dx_make_map (struct ext3_dir_
int count = 0;
char *base = (char *) de;
struct dx_hash_info h = *hinfo;
+ unsigned tail = size;
+
+ if (tail > EXT3_DIR_MAX_REC_LEN)
+ tail = EXT3_DIR_MAX_REC_LEN;
- while ((char *) de < base + size)
+ while ((char *) de < base + tail)
{
if (de->name_len && de->inode) {
ext3fs_dirhash(de->name, de->name_len, &h);
@@ -808,9 +816,13 @@ static inline int search_dirblock(struct
int de_len;
const char *name = dentry->d_name.name;
int namelen = dentry->d_name.len;
+ unsigned tail = dir->i_sb->s_blocksize;
de = (struct ext3_dir_entry_2 *) bh->b_data;
- dlimit = bh->b_data + dir->i_sb->s_blocksize;
+ if (tail > EXT3_DIR_MAX_REC_LEN)
+ tail = EXT3_DIR_MAX_REC_LEN;
+
+ dlimit = bh->b_data + tail;
while ((char *) de < dlimit) {
/* this code is executed quadratically often */
/* do minimal checking `by hand' */
@@ -1156,6 +1168,9 @@ static struct ext3_dir_entry_2* dx_pack_
unsigned rec_len = 0;
prev = to = de;
+ if (size > EXT3_DIR_MAX_REC_LEN)
+ size = EXT3_DIR_MAX_REC_LEN;
+
while ((char*)de < base + size) {
next = (struct ext3_dir_entry_2 *) ((char *) de +
le16_to_cpu(de->rec_len));
@@ -1237,8 +1252,15 @@ static struct ext3_dir_entry_2 *do_split
/* Fancy dance to stay within two buffers */
de2 = dx_move_dirents(data1, data2, map + split, count - split);
de = dx_pack_dirents(data1,blocksize);
- de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
- de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2);
+ if (blocksize < EXT3_DIR_MAX_REC_LEN) {
+ de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
+ de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2);
+ } else {
+ de->rec_len = cpu_to_le16(data1 + EXT3_DIR_MAX_REC_LEN -
+ (char *) de);
+ de2->rec_len = cpu_to_le16(data2 + EXT3_DIR_MAX_REC_LEN -
+ (char *) de2);
+ }
dxtrace(dx_show_leaf (hinfo, (struct ext3_dir_entry_2 *) data1, blocksize, 1));
dxtrace(dx_show_leaf (hinfo, (struct ext3_dir_entry_2 *) data2, blocksize, 1));
@@ -1293,11 +1315,15 @@ static int add_dirent_to_buf(handle_t *h
unsigned short reclen;
int nlen, rlen, err;
char *top;
+ unsigned tail = dir->i_sb->s_blocksize;
+
+ if (tail > EXT3_DIR_MAX_REC_LEN)
+ tail = EXT3_DIR_MAX_REC_LEN;
reclen = EXT3_DIR_REC_LEN(namelen);
if (!de) {
de = (struct ext3_dir_entry_2 *)bh->b_data;
- top = bh->b_data + dir->i_sb->s_blocksize - reclen;
+ top = bh->b_data + tail - reclen;
while ((char *) de <= top) {
if (!ext3_check_dir_entry("ext3_add_entry", dir, de,
bh, offset)) {
@@ -1411,13 +1437,21 @@ static int make_indexed_dir(handle_t *ha
/* The 0th block becomes the root, move the dirents out */
fde = &root->dotdot;
de = (struct ext3_dir_entry_2 *)((char *)fde + le16_to_cpu(fde->rec_len));
- len = ((char *) root) + blocksize - (char *) de;
+ if (blocksize < EXT3_DIR_MAX_REC_LEN)
+ len = ((char *) root) + blocksize - (char *) de;
+ else
+ len = ((char *) root) + EXT3_DIR_MAX_REC_LEN - (char *) de;
+
memcpy (data1, de, len);
de = (struct ext3_dir_entry_2 *) data1;
top = data1 + len;
while ((char *)(de2=(void*)de+le16_to_cpu(de->rec_len)) < top)
de = de2;
- de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
+ if (blocksize < EXT3_DIR_MAX_REC_LEN)
+ de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
+ else
+ de->rec_len = cpu_to_le16(data1 + EXT3_DIR_MAX_REC_LEN - (char *) de);
+
/* Initialize the root; the dot dirents already exist */
de = (struct ext3_dir_entry_2 *) (&root->dotdot);
de->rec_len = cpu_to_le16(blocksize - EXT3_DIR_REC_LEN(2));
@@ -1507,7 +1541,11 @@ static int ext3_add_entry (handle_t *han
return retval;
de = (struct ext3_dir_entry_2 *) bh->b_data;
de->inode = 0;
- de->rec_len = cpu_to_le16(blocksize);
+ if (blocksize < EXT3_DIR_MAX_REC_LEN)
+ de->rec_len = cpu_to_le16(blocksize);
+ else
+ de->rec_len = cpu_to_le16(EXT3_DIR_MAX_REC_LEN);
+
return add_dirent_to_buf(handle, dentry, inode, de, bh);
}
@@ -1571,7 +1609,12 @@ static int ext3_dx_add_entry(handle_t *h
goto cleanup;
node2 = (struct dx_node *)(bh2->b_data);
entries2 = node2->entries;
- node2->fake.rec_len = cpu_to_le16(sb->s_blocksize);
+ if (sb->s_blocksize < EXT3_DIR_MAX_REC_LEN)
+ node2->fake.rec_len = cpu_to_le16(sb->s_blocksize);
+ else
+ node2->fake.rec_len =
+ cpu_to_le16(EXT3_DIR_MAX_REC_LEN);
+
node2->fake.inode = 0;
BUFFER_TRACE(frame->bh, "get_write_access");
err = ext3_journal_get_write_access(handle, frame->bh);
@@ -1659,11 +1702,15 @@ static int ext3_delete_entry (handle_t *
{
struct ext3_dir_entry_2 * de, * pde;
int i;
+ unsigned tail = bh->b_size;
i = 0;
pde = NULL;
de = (struct ext3_dir_entry_2 *) bh->b_data;
- while (i < bh->b_size) {
+ if (tail > EXT3_DIR_MAX_REC_LEN)
+ tail = EXT3_DIR_MAX_REC_LEN;
+
+ while (i < tail) {
if (!ext3_check_dir_entry("ext3_delete_entry", dir, de, bh, i))
return -EIO;
if (de == de_del) {
@@ -1823,7 +1870,11 @@ retry:
de = (struct ext3_dir_entry_2 *)
((char *) de + le16_to_cpu(de->rec_len));
de->inode = cpu_to_le32(dir->i_ino);
- de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize-EXT3_DIR_REC_LEN(1));
+ if (inode->i_sb->s_blocksize < EXT3_DIR_MAX_REC_LEN)
+ de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize-EXT3_DIR_REC_LEN(1));
+ else
+ de->rec_len = cpu_to_le16(EXT3_DIR_MAX_REC_LEN-EXT3_DIR_REC_LEN(1));
+
de->name_len = 2;
strcpy (de->name, "..");
ext3_set_de_type(dir->i_sb, de, S_IFDIR);
@@ -1858,10 +1909,10 @@ static int empty_dir (struct inode * ino
unsigned long offset;
struct buffer_head * bh;
struct ext3_dir_entry_2 * de, * de1;
- struct super_block * sb;
+ struct super_block * sb = inode->i_sb;
int err = 0;
+ unsigned tail = sb->s_blocksize;
- sb = inode->i_sb;
if (inode->i_size < EXT3_DIR_REC_LEN(1) + EXT3_DIR_REC_LEN(2) ||
!(bh = ext3_bread (NULL, inode, 0, 0, &err))) {
if (err)
@@ -1888,11 +1939,17 @@ static int empty_dir (struct inode * ino
return 1;
}
offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len);
+ if (offset == EXT3_DIR_MAX_REC_LEN)
+ offset += sb->s_blocksize - EXT3_DIR_MAX_REC_LEN;
+
de = (struct ext3_dir_entry_2 *)
((char *) de1 + le16_to_cpu(de1->rec_len));
+ if (tail > EXT3_DIR_MAX_REC_LEN)
+ tail = EXT3_DIR_MAX_REC_LEN;
+
while (offset < inode->i_size ) {
if (!bh ||
- (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
+ (void *) de >= (void *) (bh->b_data + tail)) {
err = 0;
brelse (bh);
bh = ext3_bread (NULL, inode,
@@ -1919,6 +1976,7 @@ static int empty_dir (struct inode * ino
return 0;
}
offset += le16_to_cpu(de->rec_len);
+ offset = EXT3_DIR_ADJUST_TAIL_OFFS(offset, sb->s_blocksize);
de = (struct ext3_dir_entry_2 *)
((char *) de + le16_to_cpu(de->rec_len));
}
Index: linux-2.6.23-rc8-mm1/include/linux/ext3_fs.h
===================================================================
--- linux-2.6.23-rc8-mm1.orig/include/linux/ext3_fs.h 2007-09-25 15:37:34.000000000 -0700
+++ linux-2.6.23-rc8-mm1/include/linux/ext3_fs.h 2007-09-25 15:41:45.000000000 -0700
@@ -660,6 +660,15 @@ struct ext3_dir_entry_2 {
#define EXT3_DIR_ROUND (EXT3_DIR_PAD - 1)
#define EXT3_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT3_DIR_ROUND) & \
~EXT3_DIR_ROUND)
+#define EXT3_DIR_MAX_REC_LEN 65532
+
+/*
+ * Align a tail offset to the end of a directory block
+ */
+#define EXT3_DIR_ADJUST_TAIL_OFFS(offs, bsize) \
+ ((((offs) & ((bsize) -1)) == EXT3_DIR_MAX_REC_LEN) ? \
+ ((offs) + (bsize) - EXT3_DIR_MAX_REC_LEN):(offs))
+
/*
* Hash Tree Directory indexing
* (c) Daniel Phillips, 2001
--
next prev parent reply other threads:[~2007-09-25 23:35 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-09-25 23:30 [patch 0/4] 64k pagesize/blocksize fixes Christoph Lameter
2007-09-25 23:30 ` [patch 1/4] Increase limits for 64k page size support for Ext2/3/4 Christoph Lameter
2007-09-25 23:30 ` [patch 2/4] ext2: fix rec_len overflow for 64KB block size Christoph Lameter
2007-09-26 0:42 ` Andreas Dilger
2007-09-26 17:27 ` Christoph Lameter
2007-09-25 23:30 ` Christoph Lameter [this message]
2007-09-25 23:30 ` [patch 4/4] ext4: " Christoph Lameter
2007-09-26 17:58 ` [patch 0/4] 64k pagesize/blocksize fixes Mingming Cao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20070925233500.932740805@sgi.com \
--to=clameter@sgi.com \
--cc=cmm@us.ibm.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=sho@tnes.nec.co.jp \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.