mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 22:26:12 +00:00 
			
		
		
		
	 649f1ee6c7
			
		
	
	
		649f1ee6c7
		
	
	
	
	
		
			
			While testing more corrupted images with hfsplus, i came across one which triggered the following bug: [15840.675016] BUG: unable to handle kernel paging request at fffffffb [15840.675016] IP: [<c0116a4f>] kmap+0x15/0x56 [15840.675016] *pde = 00008067 *pte = 00000000 [15840.675016] Oops: 0000 [#1] PREEMPT DEBUG_PAGEALLOC [15840.675016] Modules linked in: [15840.675016] [15840.675016] Pid: 11575, comm: ln Not tainted (2.6.27-rc4-00123-gd3ee1b4-dirty #29) [15840.675016] EIP: 0060:[<c0116a4f>] EFLAGS: 00010202 CPU: 0 [15840.675016] EIP is at kmap+0x15/0x56 [15840.675016] EAX: 00000246 EBX: fffffffb ECX: 00000000 EDX: cab919c0 [15840.675016] ESI: 000007dd EDI: cab0bcf4 EBP: cab0bc98 ESP: cab0bc94 [15840.675016] DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068 [15840.675016] Process ln (pid: 11575, ti=cab0b000 task=cab919c0 task.ti=cab0b000) [15840.675016] Stack: 00000000 cab0bcdc c0231cfb 00000000 cab0bce0 00000800 ca9290c0 fffffffb [15840.675016] cab145d0 cab919c0 cab15998 22222222 22222222 22222222 00000001 cab15960 [15840.675016] 000007dd cab0bcf4 cab0bd04 c022cb3a cab0bcf4 cab15a6c ca9290c0 00000000 [15840.675016] Call Trace: [15840.675016] [<c0231cfb>] ? hfsplus_block_allocate+0x6f/0x2d3 [15840.675016] [<c022cb3a>] ? hfsplus_file_extend+0xc4/0x1db [15840.675016] [<c022ce41>] ? hfsplus_get_block+0x8c/0x19d [15840.675016] [<c06adde4>] ? sub_preempt_count+0x9d/0xab [15840.675016] [<c019ece6>] ? __block_prepare_write+0x147/0x311 [15840.675016] [<c0161934>] ? __grab_cache_page+0x52/0x73 [15840.675016] [<c019ef4f>] ? block_write_begin+0x79/0xd5 [15840.675016] [<c022cdb5>] ? hfsplus_get_block+0x0/0x19d [15840.675016] [<c019f22a>] ? cont_write_begin+0x27f/0x2af [15840.675016] [<c022cdb5>] ? hfsplus_get_block+0x0/0x19d [15840.675016] [<c0139ebe>] ? tick_program_event+0x28/0x4c [15840.675016] [<c013bd35>] ? trace_hardirqs_off+0xb/0xd [15840.675016] [<c022b723>] ? hfsplus_write_begin+0x2d/0x32 [15840.675016] [<c022cdb5>] ? hfsplus_get_block+0x0/0x19d [15840.675016] [<c0161988>] ? pagecache_write_begin+0x33/0x107 [15840.675016] [<c01879e5>] ? __page_symlink+0x3c/0xae [15840.675016] [<c019ad34>] ? __mark_inode_dirty+0x12f/0x137 [15840.675016] [<c0187a70>] ? page_symlink+0x19/0x1e [15840.675016] [<c022e6eb>] ? hfsplus_symlink+0x41/0xa6 [15840.675016] [<c01886a9>] ? vfs_symlink+0x99/0x101 [15840.675016] [<c018a2f6>] ? sys_symlinkat+0x6b/0xad [15840.675016] [<c018a348>] ? sys_symlink+0x10/0x12 [15840.675016] [<c01038bd>] ? sysenter_do_call+0x12/0x31 [15840.675016] ======================= [15840.675016] Code: 00 00 75 10 83 3d 88 2f ec c0 02 75 07 89 d0 e8 12 56 05 00 5d c3 55 ba 06 00 00 00 89 e5 53 89 c3 b8 3d eb 7e c0 e8 16 74 00 00 <8b> 03 c1 e8 1e 69 c0 d8 02 00 00 05 b8 69 8e c0 2b 80 c4 02 00 [15840.675016] EIP: [<c0116a4f>] kmap+0x15/0x56 SS:ESP 0068:cab0bc94 [15840.675016] ---[ end trace 4fea40dad6b70e5f ]--- This happens because the return value of read_mapping_page() is passed on to kmap unchecked. The bug is triggered after the first read_mapping_page() in hfsplus_block_allocate(), this patch fixes all three usages in this functions but leaves the ones further down in the file unchanged. Signed-off-by: Eric Sesterhenn <snakebyte@gmx.de> Cc: Roman Zippel <zippel@linux-m68k.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
		
			
				
	
	
		
			233 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  linux/fs/hfsplus/bitmap.c
 | |
|  *
 | |
|  * Copyright (C) 2001
 | |
|  * Brad Boyer (flar@allandria.com)
 | |
|  * (C) 2003 Ardis Technologies <roman@ardistech.com>
 | |
|  *
 | |
|  * Handling of allocation file
 | |
|  */
 | |
| 
 | |
| #include <linux/pagemap.h>
 | |
| 
 | |
| #include "hfsplus_fs.h"
 | |
| #include "hfsplus_raw.h"
 | |
| 
 | |
| #define PAGE_CACHE_BITS	(PAGE_CACHE_SIZE * 8)
 | |
| 
 | |
| int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, u32 *max)
 | |
| {
 | |
| 	struct page *page;
 | |
| 	struct address_space *mapping;
 | |
| 	__be32 *pptr, *curr, *end;
 | |
| 	u32 mask, start, len, n;
 | |
| 	__be32 val;
 | |
| 	int i;
 | |
| 
 | |
| 	len = *max;
 | |
| 	if (!len)
 | |
| 		return size;
 | |
| 
 | |
| 	dprint(DBG_BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len);
 | |
| 	mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
 | |
| 	mapping = HFSPLUS_SB(sb).alloc_file->i_mapping;
 | |
| 	page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL);
 | |
| 	if (IS_ERR(page)) {
 | |
| 		start = size;
 | |
| 		goto out;
 | |
| 	}
 | |
| 	pptr = kmap(page);
 | |
| 	curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
 | |
| 	i = offset % 32;
 | |
| 	offset &= ~(PAGE_CACHE_BITS - 1);
 | |
| 	if ((size ^ offset) / PAGE_CACHE_BITS)
 | |
| 		end = pptr + PAGE_CACHE_BITS / 32;
 | |
| 	else
 | |
| 		end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
 | |
| 
 | |
| 	/* scan the first partial u32 for zero bits */
 | |
| 	val = *curr;
 | |
| 	if (~val) {
 | |
| 		n = be32_to_cpu(val);
 | |
| 		mask = (1U << 31) >> i;
 | |
| 		for (; i < 32; mask >>= 1, i++) {
 | |
| 			if (!(n & mask))
 | |
| 				goto found;
 | |
| 		}
 | |
| 	}
 | |
| 	curr++;
 | |
| 
 | |
| 	/* scan complete u32s for the first zero bit */
 | |
| 	while (1) {
 | |
| 		while (curr < end) {
 | |
| 			val = *curr;
 | |
| 			if (~val) {
 | |
| 				n = be32_to_cpu(val);
 | |
| 				mask = 1 << 31;
 | |
| 				for (i = 0; i < 32; mask >>= 1, i++) {
 | |
| 					if (!(n & mask))
 | |
| 						goto found;
 | |
| 				}
 | |
| 			}
 | |
| 			curr++;
 | |
| 		}
 | |
| 		kunmap(page);
 | |
| 		offset += PAGE_CACHE_BITS;
 | |
| 		if (offset >= size)
 | |
| 			break;
 | |
| 		page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
 | |
| 					 NULL);
 | |
| 		if (IS_ERR(page)) {
 | |
| 			start = size;
 | |
| 			goto out;
 | |
| 		}
 | |
| 		curr = pptr = kmap(page);
 | |
| 		if ((size ^ offset) / PAGE_CACHE_BITS)
 | |
| 			end = pptr + PAGE_CACHE_BITS / 32;
 | |
| 		else
 | |
| 			end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
 | |
| 	}
 | |
| 	dprint(DBG_BITMAP, "bitmap full\n");
 | |
| 	start = size;
 | |
| 	goto out;
 | |
| 
 | |
| found:
 | |
| 	start = offset + (curr - pptr) * 32 + i;
 | |
| 	if (start >= size) {
 | |
| 		dprint(DBG_BITMAP, "bitmap full\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 	/* do any partial u32 at the start */
 | |
| 	len = min(size - start, len);
 | |
| 	while (1) {
 | |
| 		n |= mask;
 | |
| 		if (++i >= 32)
 | |
| 			break;
 | |
| 		mask >>= 1;
 | |
| 		if (!--len || n & mask)
 | |
| 			goto done;
 | |
| 	}
 | |
| 	if (!--len)
 | |
| 		goto done;
 | |
| 	*curr++ = cpu_to_be32(n);
 | |
| 	/* do full u32s */
 | |
| 	while (1) {
 | |
| 		while (curr < end) {
 | |
| 			n = be32_to_cpu(*curr);
 | |
| 			if (len < 32)
 | |
| 				goto last;
 | |
| 			if (n) {
 | |
| 				len = 32;
 | |
| 				goto last;
 | |
| 			}
 | |
| 			*curr++ = cpu_to_be32(0xffffffff);
 | |
| 			len -= 32;
 | |
| 		}
 | |
| 		set_page_dirty(page);
 | |
| 		kunmap(page);
 | |
| 		offset += PAGE_CACHE_BITS;
 | |
| 		page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
 | |
| 					 NULL);
 | |
| 		if (IS_ERR(page)) {
 | |
| 			start = size;
 | |
| 			goto out;
 | |
| 		}
 | |
| 		pptr = kmap(page);
 | |
| 		curr = pptr;
 | |
| 		end = pptr + PAGE_CACHE_BITS / 32;
 | |
| 	}
 | |
| last:
 | |
| 	/* do any partial u32 at end */
 | |
| 	mask = 1U << 31;
 | |
| 	for (i = 0; i < len; i++) {
 | |
| 		if (n & mask)
 | |
| 			break;
 | |
| 		n |= mask;
 | |
| 		mask >>= 1;
 | |
| 	}
 | |
| done:
 | |
| 	*curr = cpu_to_be32(n);
 | |
| 	set_page_dirty(page);
 | |
| 	kunmap(page);
 | |
| 	*max = offset + (curr - pptr) * 32 + i - start;
 | |
| 	HFSPLUS_SB(sb).free_blocks -= *max;
 | |
| 	sb->s_dirt = 1;
 | |
| 	dprint(DBG_BITMAP, "-> %u,%u\n", start, *max);
 | |
| out:
 | |
| 	mutex_unlock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
 | |
| 	return start;
 | |
| }
 | |
| 
 | |
| int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count)
 | |
| {
 | |
| 	struct page *page;
 | |
| 	struct address_space *mapping;
 | |
| 	__be32 *pptr, *curr, *end;
 | |
| 	u32 mask, len, pnr;
 | |
| 	int i;
 | |
| 
 | |
| 	/* is there any actual work to be done? */
 | |
| 	if (!count)
 | |
| 		return 0;
 | |
| 
 | |
| 	dprint(DBG_BITMAP, "block_free: %u,%u\n", offset, count);
 | |
| 	/* are all of the bits in range? */
 | |
| 	if ((offset + count) > HFSPLUS_SB(sb).total_blocks)
 | |
| 		return -2;
 | |
| 
 | |
| 	mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
 | |
| 	mapping = HFSPLUS_SB(sb).alloc_file->i_mapping;
 | |
| 	pnr = offset / PAGE_CACHE_BITS;
 | |
| 	page = read_mapping_page(mapping, pnr, NULL);
 | |
| 	pptr = kmap(page);
 | |
| 	curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
 | |
| 	end = pptr + PAGE_CACHE_BITS / 32;
 | |
| 	len = count;
 | |
| 
 | |
| 	/* do any partial u32 at the start */
 | |
| 	i = offset % 32;
 | |
| 	if (i) {
 | |
| 		int j = 32 - i;
 | |
| 		mask = 0xffffffffU << j;
 | |
| 		if (j > count) {
 | |
| 			mask |= 0xffffffffU >> (i + count);
 | |
| 			*curr++ &= cpu_to_be32(mask);
 | |
| 			goto out;
 | |
| 		}
 | |
| 		*curr++ &= cpu_to_be32(mask);
 | |
| 		count -= j;
 | |
| 	}
 | |
| 
 | |
| 	/* do full u32s */
 | |
| 	while (1) {
 | |
| 		while (curr < end) {
 | |
| 			if (count < 32)
 | |
| 				goto done;
 | |
| 			*curr++ = 0;
 | |
| 			count -= 32;
 | |
| 		}
 | |
| 		if (!count)
 | |
| 			break;
 | |
| 		set_page_dirty(page);
 | |
| 		kunmap(page);
 | |
| 		page = read_mapping_page(mapping, ++pnr, NULL);
 | |
| 		pptr = kmap(page);
 | |
| 		curr = pptr;
 | |
| 		end = pptr + PAGE_CACHE_BITS / 32;
 | |
| 	}
 | |
| done:
 | |
| 	/* do any partial u32 at end */
 | |
| 	if (count) {
 | |
| 		mask = 0xffffffffU >> count;
 | |
| 		*curr &= cpu_to_be32(mask);
 | |
| 	}
 | |
| out:
 | |
| 	set_page_dirty(page);
 | |
| 	kunmap(page);
 | |
| 	HFSPLUS_SB(sb).free_blocks += len;
 | |
| 	sb->s_dirt = 1;
 | |
| 	mutex_unlock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
 | |
| 
 | |
| 	return 0;
 | |
| }
 |