From: Andrew Morton Date: Sun, 8 Jan 2006 09:03:05 +0000 (-0800) Subject: [PATCH] fix possible PAGE_CACHE_SHIFT overflows X-Git-Tag: v2.6.16-rc1~766 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=54b21a7992a31d30c9a91f7e0a00ffdb4bd0caee;p=linux-2.6 [PATCH] fix possible PAGE_CACHE_SHIFT overflows We've had two instances recently of overflows when doing 64_bit_value = (32_bit_value << PAGE_CACHE_SHIFT) I did a tree-wide grep of `<<.*PAGE_CACHE_SHIFT' and this is the result. - afs_rxfs_fetch_descriptor.offset is of type off_t, which seems broken. - jfs and jffs are limited to 4GB anyway. - reiserfs map_block_for_writepage() takes an unsigned long for the block - it should take sector_t. (It'll fail for huge filesystems with blocksizepage_base) Cc: Oleg Drokin Cc: David Howells Cc: David Woodhouse Cc: Cc: Christoph Hellwig Cc: Anton Altaparmakov Cc: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Cc: Roman Zippel Cc: Cc: Miklos Szeredi Cc: Russell King Cc: Trond Myklebust Cc: Neil Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 6682d6d7f2..5c61c24dab 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -137,7 +137,7 @@ static inline void afs_dir_check_page(struct inode *dir, struct page *page) #endif /* determine how many magic numbers there should be in this page */ - latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT); + latter = dir->i_size - page_offset(page); if (latter >= PAGE_SIZE) qty = PAGE_SIZE; else diff --git a/fs/buffer.c b/fs/buffer.c index 263df0f192..55f0975a9b 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1762,7 +1762,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page, * handle that here by just cleaning them. */ - block = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + block = (sector_t)page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); head = page_buffers(page); bh = head; @@ -2635,7 +2635,7 @@ int block_truncate_page(struct address_space *mapping, pgoff_t index = from >> PAGE_CACHE_SHIFT; unsigned offset = from & (PAGE_CACHE_SIZE-1); unsigned blocksize; - pgoff_t iblock; + sector_t iblock; unsigned length, pos; struct inode *inode = mapping->host; struct page *page; @@ -2651,7 +2651,7 @@ int block_truncate_page(struct address_space *mapping, return 0; length = blocksize - length; - iblock = index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + iblock = (sector_t)index << (PAGE_CACHE_SHIFT - inode->i_blkbits); page = grab_cache_page(mapping, index); err = -ENOMEM; diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c index d0401dc68d..6f5df1700e 100644 --- a/fs/freevxfs/vxfs_immed.c +++ b/fs/freevxfs/vxfs_immed.c @@ -99,8 +99,8 @@ static int vxfs_immed_readpage(struct file *fp, struct page *pp) { struct vxfs_inode_info *vip = VXFS_INO(pp->mapping->host); - u_int64_t offset = pp->index << PAGE_CACHE_SHIFT; - caddr_t kaddr; + u_int64_t offset = (u_int64_t)pp->index << PAGE_CACHE_SHIFT; + caddr_t kaddr; kaddr = kmap(pp); memcpy(kaddr, vip->vii_immed.vi_immed + offset, PAGE_CACHE_SIZE); diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 3dcc6d2162..2559ee10be 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -757,7 +757,7 @@ jffs_do_readpage_nolock(struct file *file, struct page *page) read_len = 0; result = 0; - offset = page->index << PAGE_CACHE_SHIFT; + offset = page_offset(page); kmap(page); buf = page_address(page); @@ -1545,7 +1545,7 @@ jffs_commit_write(struct file *filp, struct page *page, { void *addr = page_address(page) + from; /* XXX: PAGE_CACHE_SHIFT or PAGE_SHIFT */ - loff_t pos = (page->index<index << (PAGE_CACHE_SHIFT - blkbits); + block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); last_block = (i_size_read(inode) + blocksize - 1) >> blkbits; bh.b_page = page; @@ -466,7 +466,7 @@ __mpage_writepage(struct bio *bio, struct page *page, get_block_t get_block, * The page has no buffers: map it to disk */ BUG_ON(!PageUptodate(page)); - block_in_file = page->index << (PAGE_CACHE_SHIFT - blkbits); + block_in_file = (sector_t)page->index << (PAGE_CACHE_SHIFT - blkbits); last_block = (i_size - 1) >> blkbits; map_bh.b_page = page; for (page_block = 0; page_block < blocks_per_page; ) { diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index c74f382dab..0a13859fd5 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -418,7 +418,7 @@ static int romfs_readpage(struct file *file, struct page * page) { struct inode *inode = page->mapping->host; - unsigned long offset, avail, readlen; + loff_t offset, avail, readlen; void *buf; int result = -EIO; @@ -429,8 +429,8 @@ romfs_readpage(struct file *file, struct page * page) goto err_out; /* 32 bit warning -- but not for us :) */ - offset = page->index << PAGE_CACHE_SHIFT; - if (offset < inode->i_size) { + offset = page_offset(page); + if (offset < i_size_read(inode)) { avail = inode->i_size-offset; readlen = min_t(unsigned long, avail, PAGE_SIZE); if (romfs_copyfrom(inode, buf, ROMFS_I(inode)->i_dataoffset+offset, readlen) == readlen) { diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index 3c6eb3ba71..7042e62726 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -209,8 +209,8 @@ smb_updatepage(struct file *file, struct page *page, unsigned long offset, { struct dentry *dentry = file->f_dentry; - DEBUG1("(%s/%s %d@%ld)\n", DENTRY_PATH(dentry), - count, (page->index << PAGE_CACHE_SHIFT)+offset); + DEBUG1("(%s/%s %d@%lld)\n", DENTRY_PATH(dentry), count, + ((unsigned long long)page->index << PAGE_CACHE_SHIFT) + offset); return smb_writepage_sync(dentry->d_inode, page, offset, count); } diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 69a085abad..cce8b05cba 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -103,7 +103,7 @@ static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) offset = (char *)de - kaddr; over = filldir(dirent, name, strnlen(name,SYSV_NAMELEN), - (n<inode), DT_UNKNOWN); if (over) { @@ -115,7 +115,7 @@ static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir) } done: - filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; + filp->f_pos = ((loff_t)n << PAGE_CACHE_SHIFT) | offset; unlock_kernel(); return 0; }