2.4.36-stable kernel tree
Revisão | b85ad79e46390c2774a7dc2056a2a719ae97a5ac (tree) |
---|---|
Hora | 2008-11-09 23:26:51 |
Autor | Eric Sandeen <sandeen@redh...> |
Commiter | Willy Tarreau |
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>
@@ -63,7 +63,7 @@ static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to) | ||
63 | 63 | return err; |
64 | 64 | } |
65 | 65 | |
66 | -static void ext2_check_page(struct page *page) | |
66 | +static void ext2_check_page(struct page *page, int quiet) | |
67 | 67 | { |
68 | 68 | struct inode *dir = page->mapping->host; |
69 | 69 | struct super_block *sb = dir->i_sb; |
@@ -110,10 +110,10 @@ out: | ||
110 | 110 | /* Too bad, we had an error */ |
111 | 111 | |
112 | 112 | Ebadsize: |
113 | - ext2_error(sb, "ext2_check_page", | |
114 | - "size of directory #%lu is not a multiple of chunk size", | |
115 | - dir->i_ino | |
116 | - ); | |
113 | + if (!quiet) | |
114 | + ext2_error(sb, __func__, | |
115 | + "size of directory #%lu is not a multiple " | |
116 | + "of chunk size", dir->i_ino); | |
117 | 117 | goto fail; |
118 | 118 | Eshort: |
119 | 119 | error = "rec_len is smaller than minimal"; |
@@ -130,25 +130,29 @@ Espan: | ||
130 | 130 | Einumber: |
131 | 131 | error = "inode out of bounds"; |
132 | 132 | bad_entry: |
133 | - ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - " | |
134 | - "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", | |
135 | - dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, | |
136 | - (unsigned long) le32_to_cpu(p->inode), | |
137 | - rec_len, p->name_len); | |
133 | + if (!quiet) | |
134 | + ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - " | |
135 | + "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", | |
136 | + dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, | |
137 | + (unsigned long) le32_to_cpu(p->inode), | |
138 | + rec_len, p->name_len); | |
138 | 139 | goto fail; |
139 | 140 | Eend: |
140 | - p = (ext2_dirent *)(kaddr + offs); | |
141 | - ext2_error (sb, "ext2_check_page", | |
142 | - "entry in directory #%lu spans the page boundary" | |
143 | - "offset=%lu, inode=%lu", | |
144 | - dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, | |
145 | - (unsigned long) le32_to_cpu(p->inode)); | |
141 | + if (!quiet) { | |
142 | + p = (ext2_dirent *)(kaddr + offs); | |
143 | + ext2_error(sb, "ext2_check_page", | |
144 | + "entry in directory #%lu spans the page boundary" | |
145 | + "offset=%lu, inode=%lu", | |
146 | + dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, | |
147 | + (unsigned long) le32_to_cpu(p->inode)); | |
148 | + } | |
146 | 149 | fail: |
147 | 150 | SetPageChecked(page); |
148 | 151 | SetPageError(page); |
149 | 152 | } |
150 | 153 | |
151 | -static struct page * ext2_get_page(struct inode *dir, unsigned long n) | |
154 | +static struct page * ext2_get_page(struct inode *dir, unsigned long n, | |
155 | + int quiet) | |
152 | 156 | { |
153 | 157 | struct address_space *mapping = dir->i_mapping; |
154 | 158 | struct page *page = read_cache_page(mapping, n, |
@@ -159,7 +163,7 @@ static struct page * ext2_get_page(struct inode *dir, unsigned long n) | ||
159 | 163 | if (!Page_Uptodate(page)) |
160 | 164 | goto fail; |
161 | 165 | if (!PageChecked(page)) |
162 | - ext2_check_page(page); | |
166 | + ext2_check_page(page, quiet); | |
163 | 167 | if (PageError(page)) |
164 | 168 | goto fail; |
165 | 169 | } |
@@ -257,7 +261,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) | ||
257 | 261 | for ( ; n < npages; n++, offset = 0) { |
258 | 262 | char *kaddr, *limit; |
259 | 263 | ext2_dirent *de; |
260 | - struct page *page = ext2_get_page(inode, n); | |
264 | + struct page *page = ext2_get_page(inode, n, 0); | |
261 | 265 | |
262 | 266 | if (IS_ERR(page)) { |
263 | 267 | ext2_error(sb, __FUNCTION__, |
@@ -320,6 +324,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, | ||
320 | 324 | unsigned long npages = dir_pages(dir); |
321 | 325 | struct page *page = NULL; |
322 | 326 | ext2_dirent * de; |
327 | + int dir_has_error = 0; | |
323 | 328 | |
324 | 329 | /* OFFSET_CACHE */ |
325 | 330 | *res_page = NULL; |
@@ -330,7 +335,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, | ||
330 | 335 | n = start; |
331 | 336 | do { |
332 | 337 | char *kaddr; |
333 | - page = ext2_get_page(dir, n); | |
338 | + page = ext2_get_page(dir, n, dir_has_error); | |
334 | 339 | if (!IS_ERR(page)) { |
335 | 340 | kaddr = page_address(page); |
336 | 341 | de = (ext2_dirent *) kaddr; |
@@ -341,7 +346,9 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, | ||
341 | 346 | de = ext2_next_entry(de); |
342 | 347 | } |
343 | 348 | ext2_put_page(page); |
344 | - } | |
349 | + } else | |
350 | + dir_has_error = 1; | |
351 | + | |
345 | 352 | if (++n >= npages) |
346 | 353 | n = 0; |
347 | 354 | /* next page is past the blocks we've got */ |
@@ -364,7 +371,7 @@ found: | ||
364 | 371 | |
365 | 372 | struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) |
366 | 373 | { |
367 | - struct page *page = ext2_get_page(dir, 0); | |
374 | + struct page *page = ext2_get_page(dir, 0, 0); | |
368 | 375 | ext2_dirent *de = NULL; |
369 | 376 | |
370 | 377 | if (!IS_ERR(page)) { |
@@ -431,7 +438,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) | ||
431 | 438 | |
432 | 439 | /* We take care of directory expansion in the same loop */ |
433 | 440 | for (n = 0; n <= npages; n++) { |
434 | - page = ext2_get_page(dir, n); | |
441 | + page = ext2_get_page(dir, n, 0); | |
435 | 442 | err = PTR_ERR(page); |
436 | 443 | if (IS_ERR(page)) |
437 | 444 | goto out; |
@@ -571,14 +578,17 @@ int ext2_empty_dir (struct inode * inode) | ||
571 | 578 | { |
572 | 579 | struct page *page = NULL; |
573 | 580 | unsigned long i, npages = dir_pages(inode); |
581 | + int dir_has_error = 0; | |
574 | 582 | |
575 | 583 | for (i = 0; i < npages; i++) { |
576 | 584 | char *kaddr; |
577 | 585 | ext2_dirent * de; |
578 | - page = ext2_get_page(inode, i); | |
586 | + page = ext2_get_page(inode, i, dir_has_error); | |
579 | 587 | |
580 | - if (IS_ERR(page)) | |
588 | + if (IS_ERR(page)) { | |
589 | + dir_has_error = 1; | |
581 | 590 | continue; |
591 | + } | |
582 | 592 | |
583 | 593 | kaddr = page_address(page); |
584 | 594 | de = (ext2_dirent *)kaddr; |
@@ -76,6 +76,7 @@ static int ext3_readdir(struct file * filp, | ||
76 | 76 | struct super_block * sb; |
77 | 77 | int err; |
78 | 78 | struct inode *inode = filp->f_dentry->d_inode; |
79 | + int dir_has_error = 0; | |
79 | 80 | |
80 | 81 | sb = inode->i_sb; |
81 | 82 |
@@ -87,9 +88,12 @@ static int ext3_readdir(struct file * filp, | ||
87 | 88 | blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb); |
88 | 89 | bh = ext3_bread (0, inode, blk, 0, &err); |
89 | 90 | if (!bh) { |
90 | - ext3_error (sb, "ext3_readdir", | |
91 | - "directory #%lu contains a hole at offset %lu", | |
92 | - inode->i_ino, (unsigned long)filp->f_pos); | |
91 | + if (!dir_has_error) { | |
92 | + ext3_error (sb, __func__, "directory #%lu " | |
93 | + "contains a hole at offset %lld", | |
94 | + inode->i_ino, filp->f_pos); | |
95 | + dir_has_error = 1; | |
96 | + } | |
93 | 97 | filp->f_pos += sb->s_blocksize - offset; |
94 | 98 | continue; |
95 | 99 | } |