The original problem was from nvme-over-tcp code, who mistakenly uses
kernel_sendpage() to send pages allocated by __get_free_pages() without
__GFP_COMP flag. Such pages don't have refcount (page_count is 0) on
tail pages, sending them by kernel_sendpage() may trigger a kernel panic
from a corrupted kernel heap, because these pages are incorrectly freed
in network stack as page_count 0 pages.
This patch introduces a helper sendpage_ok(), it returns true if the
checking page,
- is not slab page: PageSlab(page) is false.
- has page refcount: page_count(page) is not zero
All drivers who want to send page to remote end by kernel_sendpage()
may use this helper to check whether the page is OK. If the helper does
not return true, the driver should try other non sendpage method (e.g.
sock_no_sendpage()) to handle the page.
Signed-off-by: Coly Li <redacted>
Cc: Chaitanya Kulkarni <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jan Kara <jack@suse.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Mikhail Skorzhinskii <redacted>
Cc: Philipp Reisner <philipp.reisner@linbit.com>
Cc: Sagi Grimberg <sagi@grimberg.me>
Cc: Vlastimil Babka <redacted>
Cc: stable@vger.kernel.org
---
v5, include linux/mm.h in include/linux/net.h
v4, change sendpage_ok() as an inline helper, and post it as
separate patch.
v3, introduce a more common sendpage_ok()
v2, fix typo in patch subject
v1, the initial version.
include/linux/net.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
Currently nvme_tcp_try_send_data() doesn't use kernel_sendpage() to
send slab pages. But for pages allocated by __get_free_pages() without
__GFP_COMP, which also have refcount as 0, they are still sent by
kernel_sendpage() to remote end, this is problematic.
The new introduced helper sendpage_ok() checks both PageSlab tag and
page_count counter, and returns true if the checking page is OK to be
sent by kernel_sendpage().
This patch fixes the page checking issue of nvme_tcp_try_send_data()
with sendpage_ok(). If sendpage_ok() returns true, send this page by
kernel_sendpage(), otherwise use sock_no_sendpage to handle this page.
Signed-off-by: Coly Li <redacted>
Cc: Chaitanya Kulkarni <redacted>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jan Kara <jack@suse.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Mikhail Skorzhinskii <redacted>
Cc: Philipp Reisner <philipp.reisner@linbit.com>
Cc: Sagi Grimberg <sagi@grimberg.me>
Cc: Vlastimil Babka <redacted>
Cc: stable@vger.kernel.org
---
drivers/nvme/host/tcp.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
In _drbd_send_page() a page is checked by following code before sending
it by kernel_sendpage(),
(page_count(page) < 1) || PageSlab(page)
If the check is true, this page won't be send by kernel_sendpage() and
handled by sock_no_sendpage().
This kind of check is exactly what macro sendpage_ok() does, which is
introduced into include/linux/net.h to solve a similar send page issue
in nvme-tcp code.
This patch uses macro sendpage_ok() to replace the open coded checks to
page type and refcount in _drbd_send_page(), as a code cleanup.
Signed-off-by: Coly Li <redacted>
Cc: Philipp Reisner <philipp.reisner@linbit.com>
Cc: Sagi Grimberg <sagi@grimberg.me>
---
drivers/block/drbd/drbd_main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Eric Dumazet <hidden> Date: 2020-08-18 08:08:37
On 8/16/20 12:08 AM, Coly Li wrote:
quoted hunk
The original problem was from nvme-over-tcp code, who mistakenly uses
kernel_sendpage() to send pages allocated by __get_free_pages() without
__GFP_COMP flag. Such pages don't have refcount (page_count is 0) on
tail pages, sending them by kernel_sendpage() may trigger a kernel panic
from a corrupted kernel heap, because these pages are incorrectly freed
in network stack as page_count 0 pages.
This patch introduces a helper sendpage_ok(), it returns true if the
checking page,
- is not slab page: PageSlab(page) is false.
- has page refcount: page_count(page) is not zero
All drivers who want to send page to remote end by kernel_sendpage()
may use this helper to check whether the page is OK. If the helper does
not return true, the driver should try other non sendpage method (e.g.
sock_no_sendpage()) to handle the page.
@@ -286,6 +287,21 @@ do { \#define net_get_random_once_wait(buf, nbytes) \get_random_once_wait((buf),(nbytes))+/*+*E.g.XFSmeta-&log-dataisinslabpages,orbcachemeta+*datapages,orotherhighorderpagesallocatedby+*__get_free_pages()without__GFP_COMP,whichhaveapage_count+*of0and/orhavePageSlab()set.Wecannotusesend_pagefor+*those,asthatdoesget_page();put_page();andwouldcause+*eitheraVM_BUGdirectly,or__page_cache_releaseapagethat+*wouldactuallystillbereferencedbysomeone,leadingtosome+*obscuredelayedOopssomewhereelse.+*/+staticinlineboolsendpage_ok(structpage*page)+{+return(!PageSlab(page)&&page_count(page)>=1);+}
return (A);
Can simply be written :
return A;
In this case :
return !PageSlab(page) && page_count(page) >= 1;
BTW, do you have plans to refine code added with commit a10674bf2406afc2554f9c7d31b2dc65d6a27fd9
("tcp: detecting the misuse of .sendpage for Slab objects")
The original problem was from nvme-over-tcp code, who mistakenly uses
kernel_sendpage() to send pages allocated by __get_free_pages() without
__GFP_COMP flag. Such pages don't have refcount (page_count is 0) on
tail pages, sending them by kernel_sendpage() may trigger a kernel panic
from a corrupted kernel heap, because these pages are incorrectly freed
in network stack as page_count 0 pages.
This patch introduces a helper sendpage_ok(), it returns true if the
checking page,
- is not slab page: PageSlab(page) is false.
- has page refcount: page_count(page) is not zero
All drivers who want to send page to remote end by kernel_sendpage()
may use this helper to check whether the page is OK. If the helper does
not return true, the driver should try other non sendpage method (e.g.
sock_no_sendpage()) to handle the page.
@@ -286,6 +287,21 @@ do { \#define net_get_random_once_wait(buf, nbytes) \get_random_once_wait((buf),(nbytes))+/*+*E.g.XFSmeta-&log-dataisinslabpages,orbcachemeta+*datapages,orotherhighorderpagesallocatedby+*__get_free_pages()without__GFP_COMP,whichhaveapage_count+*of0and/orhavePageSlab()set.Wecannotusesend_pagefor+*those,asthatdoesget_page();put_page();andwouldcause+*eitheraVM_BUGdirectly,or__page_cache_releaseapagethat+*wouldactuallystillbereferencedbysomeone,leadingtosome+*obscuredelayedOopssomewhereelse.+*/+staticinlineboolsendpage_ok(structpage*page)+{+return(!PageSlab(page)&&page_count(page)>=1);+}
Hi Eric,
return (A);
Can simply be written :
return A;
In this case :
return !PageSlab(page) && page_count(page) >= 1;
Sure, I update it in v6 series.
BTW, do you have plans to refine code added with commit a10674bf2406afc2554f9c7d31b2dc65d6a27fd9
("tcp: detecting the misuse of .sendpage for Slab objects")
Thanks for the hint, I will remove this piece in v6 series.
Coly Li