Skip to contentSkip to conversation

ceph: avoid 32-bit page index overflow

PATCH In review Alex Elder 3 participants 4 messages Oct 2, 2012 Ceph client

lore
Share & tools
lore

Alex Elder

author
#0 lore raw

PATCH 1/1 ceph: avoid 32-bit page index overflow

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

---
 fs/ceph/addr.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
fs/ceph/addr.c +0 -0
index 4469b63..15dee48 100644
@@ -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;

Signature · 1 line
1.7.9.5
Reviewed-by on v1 1/1
#1 lore raw

Yay!

Quoted 81 lines · Alex Elder

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

---
 fs/ceph/addr.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)
fs/ceph/addr.c +1 -1
index 4469b63..15dee48 100644
@@ -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);
fs/ceph/addr.c +1 -1
 	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 */
fs/ceph/addr.c +1 -1
 	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;
fs/ceph/addr.c +2 -3
 	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;
Signature · 7 lines
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#2 lore raw
In reply to Alex Elder
Quoted 9 lines · Alex Elder

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.

author
#3 lore raw
Quoted 8 lines · Christoph Hellwig
Quoted 7 lines · Alex Elder

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.

Quoted 6 lines
Quoted 2 lines

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