aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Sandeen <sandeen@redhat.com>2008-11-09 15:26:51 +0100
committerWilly Tarreau <w@1wt.eu>2008-11-09 19:34:12 +0100
commit36c7eb6add8cdc47f59c645a56412f399e7a0cb5 (patch)
treef43cd076714f065e072900ed3bc067b6e9a6695e
parentaea1bc311b9c71236caeddff3cd580dc2a069294 (diff)
downloadlinux-2.4-36c7eb6add8cdc47f59c645a56412f399e7a0cb5.tar.gz
ext: Avoid printk floods in the face of directory
This is a trivial backport of the following upstream commits: - bd39597cbd42a784105a04010100e27267481c67 (ext2) - cdbf6dba28e8e6268c8420857696309470009fd9 (ext3) This addresses CVE-2008-3528 ext[23]: Avoid printk floods in the face of directory corruption Note: some people thinks this represents a security bug, since it might make the system go away while it is printing a large number of console messages, especially if a serial console is involved. Hence, it has been assigned CVE-2008-3528, but it requires that the attacker either has physical access to your machine to insert a USB disk with a corrupted filesystem image (at which point why not just hit the power button), or is otherwise able to convince the system administrator to mount an arbitrary filesystem image (at which point why not just include a setuid shell or world-writable hard disk device file or some such). Me, I think they're just being silly. --tytso Signed-off-by: Eric Sandeen <sandeen@redhat.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: linux-ext4@vger.kernel.org Cc: Eugene Teo <eugeneteo@kernel.sg> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> [w@1wt.eu: backport from 2.6-stable to 2.4. Removed all ext4 references] Signed-off-by: Willy Tarreau <w@1wt.eu> (cherry picked from commit b85ad79e46390c2774a7dc2056a2a719ae97a5ac)
-rw-r--r--fs/ext2/dir.c60
-rw-r--r--fs/ext3/dir.c10
2 files changed, 42 insertions, 28 deletions
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index 593b660361b5e0..1ef185cadd3876 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -63,7 +63,7 @@ static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to)
return err;
}
-static void ext2_check_page(struct page *page)
+static void ext2_check_page(struct page *page, int quiet)
{
struct inode *dir = page->mapping->host;
struct super_block *sb = dir->i_sb;
@@ -110,10 +110,10 @@ out:
/* Too bad, we had an error */
Ebadsize:
- ext2_error(sb, "ext2_check_page",
- "size of directory #%lu is not a multiple of chunk size",
- dir->i_ino
- );
+ if (!quiet)
+ ext2_error(sb, __func__,
+ "size of directory #%lu is not a multiple "
+ "of chunk size", dir->i_ino);
goto fail;
Eshort:
error = "rec_len is smaller than minimal";
@@ -130,25 +130,29 @@ Espan:
Einumber:
error = "inode out of bounds";
bad_entry:
- ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - "
- "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
- dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs,
- (unsigned long) le32_to_cpu(p->inode),
- rec_len, p->name_len);
+ if (!quiet)
+ ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - "
+ "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
+ dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs,
+ (unsigned long) le32_to_cpu(p->inode),
+ rec_len, p->name_len);
goto fail;
Eend:
- p = (ext2_dirent *)(kaddr + offs);
- ext2_error (sb, "ext2_check_page",
- "entry in directory #%lu spans the page boundary"
- "offset=%lu, inode=%lu",
- dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs,
- (unsigned long) le32_to_cpu(p->inode));
+ if (!quiet) {
+ p = (ext2_dirent *)(kaddr + offs);
+ ext2_error(sb, "ext2_check_page",
+ "entry in directory #%lu spans the page boundary"
+ "offset=%lu, inode=%lu",
+ dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs,
+ (unsigned long) le32_to_cpu(p->inode));
+ }
fail:
SetPageChecked(page);
SetPageError(page);
}
-static struct page * ext2_get_page(struct inode *dir, unsigned long n)
+static struct page * ext2_get_page(struct inode *dir, unsigned long n,
+ int quiet)
{
struct address_space *mapping = dir->i_mapping;
struct page *page = read_cache_page(mapping, n,
@@ -159,7 +163,7 @@ static struct page * ext2_get_page(struct inode *dir, unsigned long n)
if (!Page_Uptodate(page))
goto fail;
if (!PageChecked(page))
- ext2_check_page(page);
+ ext2_check_page(page, quiet);
if (PageError(page))
goto fail;
}
@@ -257,7 +261,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
for ( ; n < npages; n++, offset = 0) {
char *kaddr, *limit;
ext2_dirent *de;
- struct page *page = ext2_get_page(inode, n);
+ struct page *page = ext2_get_page(inode, n, 0);
if (IS_ERR(page)) {
ext2_error(sb, __FUNCTION__,
@@ -320,6 +324,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
unsigned long npages = dir_pages(dir);
struct page *page = NULL;
ext2_dirent * de;
+ int dir_has_error = 0;
/* OFFSET_CACHE */
*res_page = NULL;
@@ -330,7 +335,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
n = start;
do {
char *kaddr;
- page = ext2_get_page(dir, n);
+ page = ext2_get_page(dir, n, dir_has_error);
if (!IS_ERR(page)) {
kaddr = page_address(page);
de = (ext2_dirent *) kaddr;
@@ -341,7 +346,9 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
de = ext2_next_entry(de);
}
ext2_put_page(page);
- }
+ } else
+ dir_has_error = 1;
+
if (++n >= npages)
n = 0;
/* next page is past the blocks we've got */
@@ -364,7 +371,7 @@ found:
struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p)
{
- struct page *page = ext2_get_page(dir, 0);
+ struct page *page = ext2_get_page(dir, 0, 0);
ext2_dirent *de = NULL;
if (!IS_ERR(page)) {
@@ -431,7 +438,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode)
/* We take care of directory expansion in the same loop */
for (n = 0; n <= npages; n++) {
- page = ext2_get_page(dir, n);
+ page = ext2_get_page(dir, n, 0);
err = PTR_ERR(page);
if (IS_ERR(page))
goto out;
@@ -571,14 +578,17 @@ int ext2_empty_dir (struct inode * inode)
{
struct page *page = NULL;
unsigned long i, npages = dir_pages(inode);
+ int dir_has_error = 0;
for (i = 0; i < npages; i++) {
char *kaddr;
ext2_dirent * de;
- page = ext2_get_page(inode, i);
+ page = ext2_get_page(inode, i, dir_has_error);
- if (IS_ERR(page))
+ if (IS_ERR(page)) {
+ dir_has_error = 1;
continue;
+ }
kaddr = page_address(page);
de = (ext2_dirent *)kaddr;
diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c
index 20841818f819c9..90d4bd0b15cc46 100644
--- a/fs/ext3/dir.c
+++ b/fs/ext3/dir.c
@@ -76,6 +76,7 @@ static int ext3_readdir(struct file * filp,
struct super_block * sb;
int err;
struct inode *inode = filp->f_dentry->d_inode;
+ int dir_has_error = 0;
sb = inode->i_sb;
@@ -87,9 +88,12 @@ static int ext3_readdir(struct file * filp,
blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb);
bh = ext3_bread (0, inode, blk, 0, &err);
if (!bh) {
- ext3_error (sb, "ext3_readdir",
- "directory #%lu contains a hole at offset %lu",
- inode->i_ino, (unsigned long)filp->f_pos);
+ if (!dir_has_error) {
+ ext3_error (sb, __func__, "directory #%lu "
+ "contains a hole at offset %lld",
+ inode->i_ino, filp->f_pos);
+ dir_has_error = 1;
+ }
filp->f_pos += sb->s_blocksize - offset;
continue;
}