Skip to content

Commit 61600ef

Browse files
Yan, ZhengAlex Elder
authored andcommitted
ceph: check PG_Private flag before accessing page->private
I got lots of NULL pointer dereference Oops when compiling kernel on ceph. The bug is because the kernel page migration routine replaces some pages in the page cache with new pages, these new pages' private can be non-zero. Signed-off-by: Zheng Yan <[email protected]> Signed-off-by: Sage Weil <[email protected]> (cherry picked from commit 28c0254)
1 parent f40759e commit 61600ef

File tree

1 file changed

+12
-9
lines changed

1 file changed

+12
-9
lines changed

fs/ceph/addr.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@
5454
(CONGESTION_ON_THRESH(congestion_kb) - \
5555
(CONGESTION_ON_THRESH(congestion_kb) >> 2))
5656

57-
57+
static inline struct ceph_snap_context *page_snap_context(struct page *page)
58+
{
59+
if (PagePrivate(page))
60+
return (void *)page->private;
61+
return NULL;
62+
}
5863

5964
/*
6065
* Dirty a page. Optimistically adjust accounting, on the assumption
@@ -142,10 +147,9 @@ static void ceph_invalidatepage(struct page *page, unsigned long offset)
142147
{
143148
struct inode *inode;
144149
struct ceph_inode_info *ci;
145-
struct ceph_snap_context *snapc = (void *)page->private;
150+
struct ceph_snap_context *snapc = page_snap_context(page);
146151

147152
BUG_ON(!PageLocked(page));
148-
BUG_ON(!page->private);
149153
BUG_ON(!PagePrivate(page));
150154
BUG_ON(!page->mapping);
151155

@@ -182,7 +186,6 @@ static int ceph_releasepage(struct page *page, gfp_t g)
182186
struct inode *inode = page->mapping ? page->mapping->host : NULL;
183187
dout("%p releasepage %p idx %lu\n", inode, page, page->index);
184188
WARN_ON(PageDirty(page));
185-
WARN_ON(page->private);
186189
WARN_ON(PagePrivate(page));
187190
return 0;
188191
}
@@ -443,15 +446,15 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
443446
osdc = &fsc->client->osdc;
444447

445448
/* verify this is a writeable snap context */
446-
snapc = (void *)page->private;
449+
snapc = page_snap_context(page);
447450
if (snapc == NULL) {
448451
dout("writepage %p page %p not dirty?\n", inode, page);
449452
goto out;
450453
}
451454
oldest = get_oldest_context(inode, &snap_size);
452455
if (snapc->seq > oldest->seq) {
453456
dout("writepage %p page %p snapc %p not writeable - noop\n",
454-
inode, page, (void *)page->private);
457+
inode, page, snapc);
455458
/* we should only noop if called by kswapd */
456459
WARN_ON((current->flags & PF_MEMALLOC) == 0);
457460
ceph_put_snap_context(oldest);
@@ -591,7 +594,7 @@ static void writepages_finish(struct ceph_osd_request *req,
591594
clear_bdi_congested(&fsc->backing_dev_info,
592595
BLK_RW_ASYNC);
593596

594-
ceph_put_snap_context((void *)page->private);
597+
ceph_put_snap_context(page_snap_context(page));
595598
page->private = 0;
596599
ClearPagePrivate(page);
597600
dout("unlocking %d %p\n", i, page);
@@ -795,7 +798,7 @@ static int ceph_writepages_start(struct address_space *mapping,
795798
}
796799

797800
/* only if matching snap context */
798-
pgsnapc = (void *)page->private;
801+
pgsnapc = page_snap_context(page);
799802
if (pgsnapc->seq > snapc->seq) {
800803
dout("page snapc %p %lld > oldest %p %lld\n",
801804
pgsnapc, pgsnapc->seq, snapc, snapc->seq);
@@ -984,7 +987,7 @@ static int ceph_update_writeable_page(struct file *file,
984987
BUG_ON(!ci->i_snap_realm);
985988
down_read(&mdsc->snap_rwsem);
986989
BUG_ON(!ci->i_snap_realm->cached_context);
987-
snapc = (void *)page->private;
990+
snapc = page_snap_context(page);
988991
if (snapc && snapc != ci->i_head_snapc) {
989992
/*
990993
* this page is already dirty in another (older) snap

0 commit comments

Comments
 (0)