diff mbox

ceph: avoid 32-bit page index overflow

Message ID 506B0818.1040407@inktank.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Elder Oct. 2, 2012, 3:28 p.m. UTC
A pgoff_t is defined (by default) to have type (unsigned long).  On
architectures such as i686 that's a 32-bit type.  The ceph address
space code was attempting to produce 64 bit offsets by shifting a
page's index by PAGE_CACHE_SHIFT, but the result was not what was
desired because the shift occurred before the result got promoted
to 64 bits.

Fix this by casting all uses of page->index used in this way to
the desired 64-bit type.

This fixes http://tracker.newdream.net/issues/3112

Reported-by:  Mohamed Pakkeer <pakkeer.mohideen@realimage.com>
Signed-off-by: Alex Elder <elder@inktank.com>
---
 fs/ceph/addr.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

 	if (err == -ENOENT)
@@ -286,7 +286,7 @@ static int start_read(struct inode *inode, struct
list_head *page_list, int max)
 	int nr_pages = 0;
 	int ret;

-	off = page->index << PAGE_CACHE_SHIFT;
+	off = (u64) page->index << PAGE_CACHE_SHIFT;

 	/* count pages */
 	next_index = page->index;
@@ -426,7 +426,7 @@ static int writepage_nounlock(struct page *page,
struct writeback_control *wbc)
 	struct ceph_inode_info *ci;
 	struct ceph_fs_client *fsc;
 	struct ceph_osd_client *osdc;
-	loff_t page_off = page->index << PAGE_CACHE_SHIFT;
+	loff_t page_off = (loff_t) page->index << PAGE_CACHE_SHIFT;
 	int len = PAGE_CACHE_SIZE;
 	loff_t i_size;
 	int err = 0;
@@ -817,8 +817,7 @@ get_more_pages:
 			/* ok */
 			if (locked_pages == 0) {
 				/* prepare async write request */
-				offset = (unsigned long long)page->index
-					<< PAGE_CACHE_SHIFT;
+				offset = (u64) page->index << PAGE_CACHE_SHIFT;
 				len = wsize;
 				req = ceph_osdc_new_request(&fsc->client->osdc,
 					    &ci->i_layout,
@@ -1180,7 +1179,7 @@ static int ceph_page_mkwrite(struct vm_area_struct
*vma, struct vm_fault *vmf)
 	struct inode *inode = vma->vm_file->f_dentry->d_inode;
 	struct page *page = vmf->page;
 	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
-	loff_t off = page->index << PAGE_CACHE_SHIFT;
+	loff_t off = (loff_t) page->index << PAGE_CACHE_SHIFT;
 	loff_t size, len;
 	int ret;

Comments

Sage Weil Oct. 2, 2012, 4:20 p.m. UTC | #1
Yay!

Reviewed-by: Sage Weil <sage@inktank.com>

On Tue, 2 Oct 2012, Alex Elder wrote:

> A pgoff_t is defined (by default) to have type (unsigned long).  On
> architectures such as i686 that's a 32-bit type.  The ceph address
> space code was attempting to produce 64 bit offsets by shifting a
> page's index by PAGE_CACHE_SHIFT, but the result was not what was
> desired because the shift occurred before the result got promoted
> to 64 bits.
> 
> Fix this by casting all uses of page->index used in this way to
> the desired 64-bit type.
> 
> This fixes http://tracker.newdream.net/issues/3112
> 
> Reported-by:  Mohamed Pakkeer <pakkeer.mohideen@realimage.com>
> Signed-off-by: Alex Elder <elder@inktank.com>
> ---
>  fs/ceph/addr.c |   11 +++++------
>  1 file changed, 5 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
> index 4469b63..15dee48 100644
> --- a/fs/ceph/addr.c
> +++ b/fs/ceph/addr.c
> @@ -205,7 +205,7 @@ static int readpage_nounlock(struct file *filp,
> struct page *page)
>  	dout("readpage inode %p file %p page %p index %lu\n",
>  	     inode, filp, page, page->index);
>  	err = ceph_osdc_readpages(osdc, ceph_vino(inode), &ci->i_layout,
> -				  page->index << PAGE_CACHE_SHIFT, &len,
> +				  (u64) page->index << PAGE_CACHE_SHIFT, &len,
>  				  ci->i_truncate_seq, ci->i_truncate_size,
>  				  &page, 1, 0);
>  	if (err == -ENOENT)
> @@ -286,7 +286,7 @@ static int start_read(struct inode *inode, struct
> list_head *page_list, int max)
>  	int nr_pages = 0;
>  	int ret;
> 
> -	off = page->index << PAGE_CACHE_SHIFT;
> +	off = (u64) page->index << PAGE_CACHE_SHIFT;
> 
>  	/* count pages */
>  	next_index = page->index;
> @@ -426,7 +426,7 @@ static int writepage_nounlock(struct page *page,
> struct writeback_control *wbc)
>  	struct ceph_inode_info *ci;
>  	struct ceph_fs_client *fsc;
>  	struct ceph_osd_client *osdc;
> -	loff_t page_off = page->index << PAGE_CACHE_SHIFT;
> +	loff_t page_off = (loff_t) page->index << PAGE_CACHE_SHIFT;
>  	int len = PAGE_CACHE_SIZE;
>  	loff_t i_size;
>  	int err = 0;
> @@ -817,8 +817,7 @@ get_more_pages:
>  			/* ok */
>  			if (locked_pages == 0) {
>  				/* prepare async write request */
> -				offset = (unsigned long long)page->index
> -					<< PAGE_CACHE_SHIFT;
> +				offset = (u64) page->index << PAGE_CACHE_SHIFT;
>  				len = wsize;
>  				req = ceph_osdc_new_request(&fsc->client->osdc,
>  					    &ci->i_layout,
> @@ -1180,7 +1179,7 @@ static int ceph_page_mkwrite(struct vm_area_struct
> *vma, struct vm_fault *vmf)
>  	struct inode *inode = vma->vm_file->f_dentry->d_inode;
>  	struct page *page = vmf->page;
>  	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
> -	loff_t off = page->index << PAGE_CACHE_SHIFT;
> +	loff_t off = (loff_t) page->index << PAGE_CACHE_SHIFT;
>  	loff_t size, len;
>  	int ret;
> 
> -- 
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoph Hellwig Oct. 2, 2012, 4:32 p.m. UTC | #2
On Tue, Oct 02, 2012 at 10:28:24AM -0500, Alex Elder wrote:
> A pgoff_t is defined (by default) to have type (unsigned long).  On
> architectures such as i686 that's a 32-bit type.  The ceph address
> space code was attempting to produce 64 bit offsets by shifting a
> page's index by PAGE_CACHE_SHIFT, but the result was not what was
> desired because the shift occurred before the result got promoted
> to 64 bits.
> 
> Fix this by casting all uses of page->index used in this way to
> the desired 64-bit type.

It would be cleaner if you'd use the page_offset helper, which was
added to fix this problem without having to remember the right casts
everywhere.
--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Alex Elder Oct. 2, 2012, 7:21 p.m. UTC | #3
On 10/02/2012 11:32 AM, Christoph Hellwig wrote:
> On Tue, Oct 02, 2012 at 10:28:24AM -0500, Alex Elder wrote:
>> A pgoff_t is defined (by default) to have type (unsigned long).  On
>> architectures such as i686 that's a 32-bit type.  The ceph address
>> space code was attempting to produce 64 bit offsets by shifting a
>> page's index by PAGE_CACHE_SHIFT, but the result was not what was
>> desired because the shift occurred before the result got promoted
>> to 64 bits.
>>
>> Fix this by casting all uses of page->index used in this way to
>> the desired 64-bit type.
> 
> It would be cleaner if you'd use the page_offset helper, which was
> added to fix this problem without having to remember the right casts
> everywhere.

Will do.  Thanks Christoph.

					-Alex
--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 4469b63..15dee48 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -205,7 +205,7 @@  static int readpage_nounlock(struct file *filp,
struct page *page)
 	dout("readpage inode %p file %p page %p index %lu\n",
 	     inode, filp, page, page->index);
 	err = ceph_osdc_readpages(osdc, ceph_vino(inode), &ci->i_layout,
-				  page->index << PAGE_CACHE_SHIFT, &len,
+				  (u64) page->index << PAGE_CACHE_SHIFT, &len,
 				  ci->i_truncate_seq, ci->i_truncate_size,
 				  &page, 1, 0);