Commit f4bc5bbb authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull more nfsd fixes from Chuck Lever:
 "Ensure that NFS clients cannot send file size or offset values that
  can cause the NFS server to crash or to return incorrect or surprising
  results.

  In particular, fix how the NFS server handles values larger than
  OFFSET_MAX"

* tag 'nfsd-5.17-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
  NFSD: Deprecate NFS_OFFSET_MAX
  NFSD: Fix offset type in I/O trace points
  NFSD: COMMIT operations must not return NFS?ERR_INVAL
  NFSD: Clamp WRITE offsets
  NFSD: Fix NFSv3 SETATTR/CREATE's handling of large file sizes
  NFSD: Fix ia_size underflow
  NFSD: Fix the behavior of READ near OFFSET_MAX
parents f9f94c9d c306d737
Loading
Loading
Loading
Loading
+11 −8
Original line number Original line Diff line number Diff line
@@ -150,13 +150,17 @@ nfsd3_proc_read(struct svc_rqst *rqstp)
	unsigned int len;
	unsigned int len;
	int v;
	int v;


	argp->count = min_t(u32, argp->count, max_blocksize);

	dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n",
	dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n",
				SVCFH_fmt(&argp->fh),
				SVCFH_fmt(&argp->fh),
				(unsigned long) argp->count,
				(unsigned long) argp->count,
				(unsigned long long) argp->offset);
				(unsigned long long) argp->offset);


	argp->count = min_t(u32, argp->count, max_blocksize);
	if (argp->offset > (u64)OFFSET_MAX)
		argp->offset = (u64)OFFSET_MAX;
	if (argp->offset + argp->count > (u64)OFFSET_MAX)
		argp->count = (u64)OFFSET_MAX - argp->offset;

	v = 0;
	v = 0;
	len = argp->count;
	len = argp->count;
	resp->pages = rqstp->rq_next_page;
	resp->pages = rqstp->rq_next_page;
@@ -199,6 +203,11 @@ nfsd3_proc_write(struct svc_rqst *rqstp)
				(unsigned long long) argp->offset,
				(unsigned long long) argp->offset,
				argp->stable? " stable" : "");
				argp->stable? " stable" : "");


	resp->status = nfserr_fbig;
	if (argp->offset > (u64)OFFSET_MAX ||
	    argp->offset + argp->len > (u64)OFFSET_MAX)
		return rpc_success;

	fh_copy(&resp->fh, &argp->fh);
	fh_copy(&resp->fh, &argp->fh);
	resp->committed = argp->stable;
	resp->committed = argp->stable;
	nvecs = svc_fill_write_vector(rqstp, &argp->payload);
	nvecs = svc_fill_write_vector(rqstp, &argp->payload);
@@ -651,15 +660,9 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
				argp->count,
				argp->count,
				(unsigned long long) argp->offset);
				(unsigned long long) argp->offset);


	if (argp->offset > NFS_OFFSET_MAX) {
		resp->status = nfserr_inval;
		goto out;
	}

	fh_copy(&resp->fh, &argp->fh);
	fh_copy(&resp->fh, &argp->fh);
	resp->status = nfsd_commit(rqstp, &resp->fh, argp->offset,
	resp->status = nfsd_commit(rqstp, &resp->fh, argp->offset,
				   argp->count, resp->verf);
				   argp->count, resp->verf);
out:
	return rpc_success;
	return rpc_success;
}
}


+2 −2
Original line number Original line Diff line number Diff line
@@ -254,7 +254,7 @@ svcxdr_decode_sattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
		if (xdr_stream_decode_u64(xdr, &newsize) < 0)
		if (xdr_stream_decode_u64(xdr, &newsize) < 0)
			return false;
			return false;
		iap->ia_valid |= ATTR_SIZE;
		iap->ia_valid |= ATTR_SIZE;
		iap->ia_size = min_t(u64, newsize, NFS_OFFSET_MAX);
		iap->ia_size = newsize;
	}
	}
	if (xdr_stream_decode_u32(xdr, &set_it) < 0)
	if (xdr_stream_decode_u32(xdr, &set_it) < 0)
		return false;
		return false;
@@ -1060,7 +1060,7 @@ svcxdr_encode_entry3_common(struct nfsd3_readdirres *resp, const char *name,
		return false;
		return false;
	/* cookie */
	/* cookie */
	resp->cookie_offset = dirlist->len;
	resp->cookie_offset = dirlist->len;
	if (xdr_stream_encode_u64(xdr, NFS_OFFSET_MAX) < 0)
	if (xdr_stream_encode_u64(xdr, OFFSET_MAX) < 0)
		return false;
		return false;


	return true;
	return true;
+9 −4
Original line number Original line Diff line number Diff line
@@ -782,12 +782,16 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	__be32 status;
	__be32 status;


	read->rd_nf = NULL;
	read->rd_nf = NULL;
	if (read->rd_offset >= OFFSET_MAX)
		return nfserr_inval;


	trace_nfsd_read_start(rqstp, &cstate->current_fh,
	trace_nfsd_read_start(rqstp, &cstate->current_fh,
			      read->rd_offset, read->rd_length);
			      read->rd_offset, read->rd_length);


	read->rd_length = min_t(u32, read->rd_length, svc_max_payload(rqstp));
	if (read->rd_offset > (u64)OFFSET_MAX)
		read->rd_offset = (u64)OFFSET_MAX;
	if (read->rd_offset + read->rd_length > (u64)OFFSET_MAX)
		read->rd_length = (u64)OFFSET_MAX - read->rd_offset;

	/*
	/*
	 * If we do a zero copy read, then a client will see read data
	 * If we do a zero copy read, then a client will see read data
	 * that reflects the state of the file *after* performing the
	 * that reflects the state of the file *after* performing the
@@ -1018,8 +1022,9 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	unsigned long cnt;
	unsigned long cnt;
	int nvecs;
	int nvecs;


	if (write->wr_offset >= OFFSET_MAX)
	if (write->wr_offset > (u64)OFFSET_MAX ||
		return nfserr_inval;
	    write->wr_offset + write->wr_buflen > (u64)OFFSET_MAX)
		return nfserr_fbig;


	cnt = write->wr_buflen;
	cnt = write->wr_buflen;
	trace_nfsd_write_start(rqstp, &cstate->current_fh,
	trace_nfsd_write_start(rqstp, &cstate->current_fh,
+3 −7
Original line number Original line Diff line number Diff line
@@ -3495,7 +3495,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
	p = xdr_reserve_space(xdr, 3*4 + namlen);
	p = xdr_reserve_space(xdr, 3*4 + namlen);
	if (!p)
	if (!p)
		goto fail;
		goto fail;
	p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
	p = xdr_encode_hyper(p, OFFSET_MAX);        /* offset of next entry */
	p = xdr_encode_array(p, name, namlen);      /* name length & name */
	p = xdr_encode_array(p, name, namlen);      /* name length & name */


	nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
	nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
@@ -3986,10 +3986,8 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
	}
	}
	xdr_commit_encode(xdr);
	xdr_commit_encode(xdr);


	maxcount = svc_max_payload(resp->rqstp);
	maxcount = min_t(unsigned long, read->rd_length,
	maxcount = min_t(unsigned long, maxcount,
			 (xdr->buf->buflen - xdr->buf->len));
			 (xdr->buf->buflen - xdr->buf->len));
	maxcount = min_t(unsigned long, maxcount, read->rd_length);


	if (file->f_op->splice_read &&
	if (file->f_op->splice_read &&
	    test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags))
	    test_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags))
@@ -4826,10 +4824,8 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr,
		return nfserr_resource;
		return nfserr_resource;
	xdr_commit_encode(xdr);
	xdr_commit_encode(xdr);


	maxcount = svc_max_payload(resp->rqstp);
	maxcount = min_t(unsigned long, read->rd_length,
	maxcount = min_t(unsigned long, maxcount,
			 (xdr->buf->buflen - xdr->buf->len));
			 (xdr->buf->buflen - xdr->buf->len));
	maxcount = min_t(unsigned long, maxcount, read->rd_length);
	count    = maxcount;
	count    = maxcount;


	eof = read->rd_offset >= i_size_read(file_inode(file));
	eof = read->rd_offset >= i_size_read(file_inode(file));
+7 −7
Original line number Original line Diff line number Diff line
@@ -306,14 +306,14 @@ TRACE_EVENT(nfsd_export_update,
DECLARE_EVENT_CLASS(nfsd_io_class,
DECLARE_EVENT_CLASS(nfsd_io_class,
	TP_PROTO(struct svc_rqst *rqstp,
	TP_PROTO(struct svc_rqst *rqstp,
		 struct svc_fh	*fhp,
		 struct svc_fh	*fhp,
		 loff_t		offset,
		 u64		offset,
		 unsigned long	len),
		 u32		len),
	TP_ARGS(rqstp, fhp, offset, len),
	TP_ARGS(rqstp, fhp, offset, len),
	TP_STRUCT__entry(
	TP_STRUCT__entry(
		__field(u32, xid)
		__field(u32, xid)
		__field(u32, fh_hash)
		__field(u32, fh_hash)
		__field(loff_t, offset)
		__field(u64, offset)
		__field(unsigned long, len)
		__field(u32, len)
	),
	),
	TP_fast_assign(
	TP_fast_assign(
		__entry->xid = be32_to_cpu(rqstp->rq_xid);
		__entry->xid = be32_to_cpu(rqstp->rq_xid);
@@ -321,7 +321,7 @@ DECLARE_EVENT_CLASS(nfsd_io_class,
		__entry->offset = offset;
		__entry->offset = offset;
		__entry->len = len;
		__entry->len = len;
	),
	),
	TP_printk("xid=0x%08x fh_hash=0x%08x offset=%lld len=%lu",
	TP_printk("xid=0x%08x fh_hash=0x%08x offset=%llu len=%u",
		  __entry->xid, __entry->fh_hash,
		  __entry->xid, __entry->fh_hash,
		  __entry->offset, __entry->len)
		  __entry->offset, __entry->len)
)
)
@@ -330,8 +330,8 @@ DECLARE_EVENT_CLASS(nfsd_io_class,
DEFINE_EVENT(nfsd_io_class, nfsd_##name,	\
DEFINE_EVENT(nfsd_io_class, nfsd_##name,	\
	TP_PROTO(struct svc_rqst *rqstp,	\
	TP_PROTO(struct svc_rqst *rqstp,	\
		 struct svc_fh	*fhp,		\
		 struct svc_fh	*fhp,		\
		 loff_t		offset,		\
		 u64		offset,		\
		 unsigned long	len),		\
		 u32		len),		\
	TP_ARGS(rqstp, fhp, offset, len))
	TP_ARGS(rqstp, fhp, offset, len))


DEFINE_NFSD_IO_EVENT(read_start);
DEFINE_NFSD_IO_EVENT(read_start);
Loading