Skip to content

Commit 61182c7

Browse files
committed
SUNRPC: kmap() the xdr pages during decode
If the pages are in HIGHMEM then we need to make sure they're mapped before trying to read data off of them, otherwise we could end up with a NULL pointer dereference. The downside to this is that we need an extra cleanup step at the end of decode to kunmap() the last page. I introduced an xdr_finish_decode() function to do this. Right now this function only calls the unmap_current_page() function, but other generic cleanup steps could be added in the future if we come across anything else. Reported-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
1 parent 303a780 commit 61182c7

4 files changed

Lines changed: 31 additions & 1 deletion

File tree

include/linux/sunrpc/xdr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ struct xdr_stream {
224224
struct kvec *iov; /* pointer to the current kvec */
225225
struct kvec scratch; /* Scratch buffer */
226226
struct page **page_ptr; /* pointer to the current page */
227+
void *page_kaddr; /* kmapped address of the current page */
227228
unsigned int nwords; /* Remaining decode buffer length */
228229

229230
struct rpc_rqst *rqst; /* For debugging */
@@ -255,6 +256,7 @@ extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf,
255256
__be32 *p, struct rpc_rqst *rqst);
256257
extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
257258
struct page **pages, unsigned int len);
259+
extern void xdr_finish_decode(struct xdr_stream *xdr);
258260
extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
259261
extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
260262
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);

net/sunrpc/clnt.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,6 +2602,7 @@ call_decode(struct rpc_task *task)
26022602
case 0:
26032603
task->tk_action = rpc_exit_task;
26042604
task->tk_status = rpcauth_unwrap_resp(task, &xdr);
2605+
xdr_finish_decode(&xdr);
26052606
return;
26062607
case -EAGAIN:
26072608
task->tk_status = 0;

net/sunrpc/svc.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,8 @@ svc_process_common(struct svc_rqst *rqstp)
13701370
rc = process.dispatch(rqstp);
13711371
if (procp->pc_release)
13721372
procp->pc_release(rqstp);
1373+
xdr_finish_decode(xdr);
1374+
13731375
if (!rc)
13741376
goto dropit;
13751377
if (rqstp->rq_auth_stat != rpc_auth_ok)

net/sunrpc/xdr.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,14 @@ static unsigned int xdr_set_tail_base(struct xdr_stream *xdr,
12881288
return xdr_set_iov(xdr, buf->tail, base, len);
12891289
}
12901290

1291+
static void xdr_stream_unmap_current_page(struct xdr_stream *xdr)
1292+
{
1293+
if (xdr->page_kaddr) {
1294+
kunmap_local(xdr->page_kaddr);
1295+
xdr->page_kaddr = NULL;
1296+
}
1297+
}
1298+
12911299
static unsigned int xdr_set_page_base(struct xdr_stream *xdr,
12921300
unsigned int base, unsigned int len)
12931301
{
@@ -1305,12 +1313,18 @@ static unsigned int xdr_set_page_base(struct xdr_stream *xdr,
13051313
if (len > maxlen)
13061314
len = maxlen;
13071315

1316+
xdr_stream_unmap_current_page(xdr);
13081317
xdr_stream_page_set_pos(xdr, base);
13091318
base += xdr->buf->page_base;
13101319

13111320
pgnr = base >> PAGE_SHIFT;
13121321
xdr->page_ptr = &xdr->buf->pages[pgnr];
1313-
kaddr = page_address(*xdr->page_ptr);
1322+
1323+
if (PageHighMem(*xdr->page_ptr)) {
1324+
xdr->page_kaddr = kmap_local_page(*xdr->page_ptr);
1325+
kaddr = xdr->page_kaddr;
1326+
} else
1327+
kaddr = page_address(*xdr->page_ptr);
13141328

13151329
pgoff = base & ~PAGE_MASK;
13161330
xdr->p = (__be32*)(kaddr + pgoff);
@@ -1364,6 +1378,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p,
13641378
struct rpc_rqst *rqst)
13651379
{
13661380
xdr->buf = buf;
1381+
xdr->page_kaddr = NULL;
13671382
xdr_reset_scratch_buffer(xdr);
13681383
xdr->nwords = XDR_QUADLEN(buf->len);
13691384
if (xdr_set_iov(xdr, buf->head, 0, buf->len) == 0 &&
@@ -1396,6 +1411,16 @@ void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
13961411
}
13971412
EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
13981413

1414+
/**
1415+
* xdr_finish_decode - Clean up the xdr_stream after decoding data.
1416+
* @xdr: pointer to xdr_stream struct
1417+
*/
1418+
void xdr_finish_decode(struct xdr_stream *xdr)
1419+
{
1420+
xdr_stream_unmap_current_page(xdr);
1421+
}
1422+
EXPORT_SYMBOL(xdr_finish_decode);
1423+
13991424
static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
14001425
{
14011426
unsigned int nwords = XDR_QUADLEN(nbytes);

0 commit comments

Comments
 (0)