Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

NFSD: Watch for rq_pages bounds checking errors in nfsd_splice_actor()

There have been several bugs over the years where the NFSD splice
actor has attempted to write outside the rq_pages array.

This is a "should never happen" condition, but if for some reason
the pipe splice actor should attempt to walk past the end of
rq_pages, it needs to terminate the READ operation to prevent
corruption of the pointer addresses in the fields just beyond the
array.

A server crash is thus prevented. Since the code is not behaving,
the READ operation returns -EIO to the client. None of the READ
payload data can be trusted if the splice actor isn't operating as
expected.

Suggested-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>

+45 -3
+5 -1
fs/nfsd/vfs.c
··· 930 930 * Grab and keep cached pages associated with a file in the svc_rqst 931 931 * so that they can be passed to the network sendmsg/sendpage routines 932 932 * directly. They will be released after the sending has completed. 933 + * 934 + * Return values: Number of bytes consumed, or -EIO if there are no 935 + * remaining pages in rqstp->rq_pages. 933 936 */ 934 937 static int 935 938 nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, ··· 951 948 */ 952 949 if (page == *(rqstp->rq_next_page - 1)) 953 950 continue; 954 - svc_rqst_replace_page(rqstp, page); 951 + if (unlikely(!svc_rqst_replace_page(rqstp, page))) 952 + return -EIO; 955 953 } 956 954 if (rqstp->rq_res.page_len == 0) // first call 957 955 rqstp->rq_res.page_base = offset % PAGE_SIZE;
+1 -1
include/linux/sunrpc/svc.h
··· 422 422 int (*threadfn)(void *data)); 423 423 struct svc_rqst *svc_rqst_alloc(struct svc_serv *serv, 424 424 struct svc_pool *pool, int node); 425 - void svc_rqst_replace_page(struct svc_rqst *rqstp, 425 + bool svc_rqst_replace_page(struct svc_rqst *rqstp, 426 426 struct page *page); 427 427 void svc_rqst_free(struct svc_rqst *); 428 428 void svc_exit_thread(struct svc_rqst *);
+25
include/trace/events/sunrpc.h
··· 1790 1790 TP_PROTO(const struct svc_rqst *rqst, int status), 1791 1791 TP_ARGS(rqst, status)); 1792 1792 1793 + TRACE_EVENT(svc_replace_page_err, 1794 + TP_PROTO(const struct svc_rqst *rqst), 1795 + 1796 + TP_ARGS(rqst), 1797 + TP_STRUCT__entry( 1798 + SVC_RQST_ENDPOINT_FIELDS(rqst) 1799 + 1800 + __field(const void *, begin) 1801 + __field(const void *, respages) 1802 + __field(const void *, nextpage) 1803 + ), 1804 + 1805 + TP_fast_assign( 1806 + SVC_RQST_ENDPOINT_ASSIGNMENTS(rqst); 1807 + 1808 + __entry->begin = rqst->rq_pages; 1809 + __entry->respages = rqst->rq_respages; 1810 + __entry->nextpage = rqst->rq_next_page; 1811 + ), 1812 + 1813 + TP_printk(SVC_RQST_ENDPOINT_FORMAT " begin=%p respages=%p nextpage=%p", 1814 + SVC_RQST_ENDPOINT_VARARGS, 1815 + __entry->begin, __entry->respages, __entry->nextpage) 1816 + ); 1817 + 1793 1818 TRACE_EVENT(svc_stats_latency, 1794 1819 TP_PROTO( 1795 1820 const struct svc_rqst *rqst
+14 -1
net/sunrpc/svc.c
··· 842 842 * 843 843 * When replacing a page in rq_pages, batch the release of the 844 844 * replaced pages to avoid hammering the page allocator. 845 + * 846 + * Return values: 847 + * %true: page replaced 848 + * %false: array bounds checking failed 845 849 */ 846 - void svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page) 850 + bool svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page) 847 851 { 852 + struct page **begin = rqstp->rq_pages; 853 + struct page **end = &rqstp->rq_pages[RPCSVC_MAXPAGES]; 854 + 855 + if (unlikely(rqstp->rq_next_page < begin || rqstp->rq_next_page > end)) { 856 + trace_svc_replace_page_err(rqstp); 857 + return false; 858 + } 859 + 848 860 if (*rqstp->rq_next_page) { 849 861 if (!pagevec_space(&rqstp->rq_pvec)) 850 862 __pagevec_release(&rqstp->rq_pvec); ··· 865 853 866 854 get_page(page); 867 855 *(rqstp->rq_next_page++) = page; 856 + return true; 868 857 } 869 858 EXPORT_SYMBOL_GPL(svc_rqst_replace_page); 870 859