linux-nfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 00/25] Server-side RPC call header parsing overhaul
@ 2023-01-02 17:05 Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 01/25] SUNRPC: Push svcxdr_init_decode() into svc_process_common() Chuck Lever
                   ` (25 more replies)
  0 siblings, 26 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:05 UTC (permalink / raw)
  To: linux-nfs

Happy new year, campers.

The following series has been percolating for quite a while, thanks
to the extended 6.1-rc cycle. I'd like to get this work reviewed
for possible inclusion in v6.3, so I'm starting early.

The purpose of this series is to replace the svc_get* macros in the
Linux kernel server's RPC call header parsing code with xdr_stream
helpers. I've measured no change in CPU utilization after the
overhaul; svc_recv() and friends remain the highest CPU consumers by
an order of magnitude.

Memory safety: Buffer bounds checking after decoding each XDR item
is more memory-safe than the current decoding mechanism. Subsequent
memory safety improvements to the xdr_stream helpers will benefit
all who use them.

Audit friendliness: The new code has lots of comments and other
clean-up to help align it with the relevant RPC specifications. The
use of common helpers also makes the decoders easier to audit.

I've split the full series in half to make it easier to review. The
patches posted here handle RPC call header decoding. Remaining
patches, to be posted later, deal with RPC reply header encoding.

Yes, there are a lot of patches, but they are each small, easily
chewed mechanical changes.

---

Chuck Lever (25):
      SUNRPC: Push svcxdr_init_decode() into svc_process_common()
      SUNRPC: Move svcxdr_init_decode() into ->accept methods
      SUNRPC: Add an XDR decoding helper for struct opaque_auth
      SUNRPC: Convert svcauth_null_accept() to use xdr_stream
      SUNRPC: Convert svcauth_unix_accept() to use xdr_stream
      SUNRPC: Convert svcauth_tls_accept() to use xdr_stream
      SUNRPC: Move the server-side GSS upcall to a noinline function
      SUNRPC: Hoist common verifier decoding code into svcauth_gss_proc_init()
      SUNRPC: Remove gss_read_common_verf()
      SUNRPC: Remove gss_read_verf()
      SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream
      SUNRPC: Replace read_u32_from_xdr_buf() with existing XDR helper
      SUNRPC: Rename automatic variables in unwrap_integ_data()
      SUNRPC: Convert unwrap_integ_data() to use xdr_stream
      SUNRPC: Rename automatic variables in unwrap_priv_data()
      SUNRPC: Convert unwrap_priv_data() to use xdr_stream
      SUNRPC: Convert gss_verify_header() to use xdr_stream
      SUNRPC: Clean up svcauth_gss_accept's NULL procedure check
      SUNRPC: Convert the svcauth_gss_accept() pre-amble to use xdr_stream
      SUNRPC: Hoist init_decode out of svc_authenticate()
      SUNRPC: Re-order construction of the first reply fields
      SUNRPC: Eliminate unneeded variable
      SUNRPC: Decode most of RPC header with xdr_stream
      SUNRPC: Remove svc_process_common's argv parameter
      SUNRPC: Hoist svcxdr_init_decode() into svc_process()


 fs/lockd/svc.c                    |   1 -
 fs/nfs/callback_xdr.c             |   1 -
 fs/nfsd/nfssvc.c                  |   1 -
 include/linux/sunrpc/msg_prot.h   |   5 +
 include/linux/sunrpc/xdr.h        |   5 +-
 net/sunrpc/auth_gss/svcauth_gss.c | 512 ++++++++++++++++--------------
 net/sunrpc/svc.c                  |  69 ++--
 net/sunrpc/svc_xprt.c             |   1 -
 net/sunrpc/svcauth.c              |  13 +-
 net/sunrpc/svcauth_unix.c         | 132 +++++---
 net/sunrpc/xdr.c                  |  50 ++-
 11 files changed, 468 insertions(+), 322 deletions(-)

--
Chuck Lever


^ permalink raw reply	[flat|nested] 28+ messages in thread

* [PATCH v1 01/25] SUNRPC: Push svcxdr_init_decode() into svc_process_common()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
@ 2023-01-02 17:05 ` Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 02/25] SUNRPC: Move svcxdr_init_decode() into ->accept methods Chuck Lever
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:05 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Now that all vs_dispatch functions invoke svcxdr_init_decode(), it
is common code and can be pushed down into the generic RPC server.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 fs/lockd/svc.c        |    1 -
 fs/nfs/callback_xdr.c |    1 -
 fs/nfsd/nfssvc.c      |    1 -
 net/sunrpc/svc.c      |    1 +
 4 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 59ef8a1f843f..e56d85335599 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -695,7 +695,6 @@ static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp)
 {
 	const struct svc_procedure *procp = rqstp->rq_procinfo;
 
-	svcxdr_init_decode(rqstp);
 	if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream))
 		goto out_decode_err;
 
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index d0cccddb7d08..46d3f5986b4e 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -984,7 +984,6 @@ nfs_callback_dispatch(struct svc_rqst *rqstp, __be32 *statp)
 {
 	const struct svc_procedure *procp = rqstp->rq_procinfo;
 
-	svcxdr_init_decode(rqstp);
 	svcxdr_init_encode(rqstp);
 
 	*statp = procp->pc_func(rqstp);
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 56fba1cba3af..5375a28b102a 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -1040,7 +1040,6 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
 	 */
 	rqstp->rq_cachetype = proc->pc_cachetype;
 
-	svcxdr_init_decode(rqstp);
 	if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
 		goto out_decode_err;
 
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 85f0c3cfc877..96806c3c61a5 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1302,6 +1302,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	if (progp == NULL)
 		goto err_bad_prog;
 
+	svcxdr_init_decode(rqstp);
 	rpc_stat = progp->pg_init_request(rqstp, progp, &process);
 	switch (rpc_stat) {
 	case rpc_success:



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 02/25] SUNRPC: Move svcxdr_init_decode() into ->accept methods
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 01/25] SUNRPC: Push svcxdr_init_decode() into svc_process_common() Chuck Lever
@ 2023-01-02 17:05 ` Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 03/25] SUNRPC: Add an XDR decoding helper for struct opaque_auth Chuck Lever
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:05 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Refactor: So that the overhaul of each ->accept method can be done
in separate smaller patches, temporarily move the
svcxdr_init_decode() call into those methods.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |    5 +++++
 net/sunrpc/svc.c                  |    1 -
 net/sunrpc/svcauth_unix.c         |    3 +++
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 148bb0a7fa5b..8ebc06bebbec 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1280,6 +1280,7 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
 			   rsip->major_status, rsip->minor_status))
 		goto out;
 
+	svcxdr_init_decode(rqstp);
 	ret = SVC_COMPLETE;
 out:
 	cache_put(&rsip->h, sn->rsi_cache);
@@ -1408,6 +1409,7 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
 			   ud.major_status, ud.minor_status))
 		goto out;
 
+	svcxdr_init_decode(rqstp);
 	ret = SVC_COMPLETE;
 out:
 	gss_free_in_token_pages(&ud.in_token);
@@ -1644,6 +1646,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 		rqstp->rq_auth_stat = rpc_autherr_badcred;
 		switch (gc->gc_svc) {
 		case RPC_GSS_SVC_NONE:
+			svcxdr_init_decode(rqstp);
 			break;
 		case RPC_GSS_SVC_INTEGRITY:
 			/* placeholders for length and seq. number: */
@@ -1653,6 +1656,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 					gc->gc_seq, rsci->mechctx))
 				goto garbage_args;
 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
+			svcxdr_init_decode(rqstp);
 			break;
 		case RPC_GSS_SVC_PRIVACY:
 			/* placeholders for length and seq. number: */
@@ -1662,6 +1666,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 					gc->gc_seq, rsci->mechctx))
 				goto garbage_args;
 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
+			svcxdr_init_decode(rqstp);
 			break;
 		default:
 			goto auth_err;
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 96806c3c61a5..85f0c3cfc877 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1302,7 +1302,6 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	if (progp == NULL)
 		goto err_bad_prog;
 
-	svcxdr_init_decode(rqstp);
 	rpc_stat = progp->pg_init_request(rqstp, progp, &process);
 	switch (rpc_stat) {
 	case rpc_success:
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index b1efc34db6ed..3a77f3be2cf0 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -762,6 +762,7 @@ svcauth_null_accept(struct svc_rqst *rqstp)
 	svc_putnl(resv, 0);
 
 	rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
+	svcxdr_init_decode(rqstp);
 	return SVC_OK;
 }
 
@@ -835,6 +836,7 @@ svcauth_tls_accept(struct svc_rqst *rqstp)
 		svc_putnl(resv, 0);
 
 	rqstp->rq_cred.cr_flavor = RPC_AUTH_TLS;
+	svcxdr_init_decode(rqstp);
 	return SVC_OK;
 }
 
@@ -900,6 +902,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp)
 	svc_putnl(resv, 0);
 
 	rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
+	svcxdr_init_decode(rqstp);
 	return SVC_OK;
 
 badcred:



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 03/25] SUNRPC: Add an XDR decoding helper for struct opaque_auth
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 01/25] SUNRPC: Push svcxdr_init_decode() into svc_process_common() Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 02/25] SUNRPC: Move svcxdr_init_decode() into ->accept methods Chuck Lever
@ 2023-01-02 17:05 ` Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 04/25] SUNRPC: Convert svcauth_null_accept() to use xdr_stream Chuck Lever
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:05 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

RFC 5531 defines the body of an RPC Call message like this:

	struct call_body {
		unsigned int rpcvers;
		unsigned int prog;
		unsigned int vers;
		unsigned int proc;
		opaque_auth cred;
		opaque_auth verf;
		/* procedure-specific parameters start here */
	};

In the current server code, decoding a struct opaque_auth type is
open-coded in several places, and is thus difficult to harden
everywhere.

Introduce a helper for decoding an opaque_auth within the context
of a xdr_stream. This helper can be shared with all authentication
flavor implemenations, even on the client-side.

Done as part of hardening the server-side RPC header decoding paths.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 include/linux/sunrpc/xdr.h |    3 +++
 net/sunrpc/xdr.c           |   28 ++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index f84e2a1358e1..8b5c9d0cdcb5 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -346,6 +346,9 @@ ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str,
 		size_t size);
 ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
 		size_t maxlen, gfp_t gfp_flags);
+ssize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor,
+		void **body, unsigned int *body_len);
+
 /**
  * xdr_align_size - Calculate padded size of an object
  * @n: Size of an object being XDR encoded (in bytes)
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index f7767bf22406..4845ba2113fd 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -2274,3 +2274,31 @@ ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
 	return ret;
 }
 EXPORT_SYMBOL_GPL(xdr_stream_decode_string_dup);
+
+/**
+ * xdr_stream_decode_opaque_auth - Decode struct opaque_auth (RFC5531 S8.2)
+ * @xdr: pointer to xdr_stream
+ * @flavor: location to store decoded flavor
+ * @body: location to store decode body
+ * @body_len: location to store length of decoded body
+ *
+ * Return values:
+ *   On success, returns the number of buffer bytes consumed
+ *   %-EBADMSG on XDR buffer overflow
+ *   %-EMSGSIZE if the decoded size of the body field exceeds 400 octets
+ */
+ssize_t xdr_stream_decode_opaque_auth(struct xdr_stream *xdr, u32 *flavor,
+				      void **body, unsigned int *body_len)
+{
+	ssize_t ret, len;
+
+	len = xdr_stream_decode_u32(xdr, flavor);
+	if (unlikely(len < 0))
+		return len;
+	ret = xdr_stream_decode_opaque_inline(xdr, body, RPC_MAX_AUTH_SIZE);
+	if (unlikely(ret < 0))
+		return ret;
+	*body_len = ret;
+	return len + ret;
+}
+EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_auth);



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 04/25] SUNRPC: Convert svcauth_null_accept() to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (2 preceding siblings ...)
  2023-01-02 17:05 ` [PATCH v1 03/25] SUNRPC: Add an XDR decoding helper for struct opaque_auth Chuck Lever
@ 2023-01-02 17:05 ` Chuck Lever
  2023-01-02 17:05 ` [PATCH v1 05/25] SUNRPC: Convert svcauth_unix_accept() " Chuck Lever
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:05 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svcauth_unix.c |   33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)

diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 3a77f3be2cf0..95354f03bb05 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -729,23 +729,41 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
 
 EXPORT_SYMBOL_GPL(svcauth_unix_set_client);
 
+/**
+ * svcauth_null_accept - Decode and validate incoming RPC_AUTH_NULL credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ *   %SVC_OK: Both credential and verifier are valid
+ *   %SVC_DENIED: Credential or verifier is not valid
+ *   %SVC_GARBAGE: Failed to decode credential or verifier
+ *   %SVC_CLOSE: Temporary failure
+ *
+ * rqstp->rq_auth_stat is set as mandated by RFC 5531.
+ */
 static int
 svcauth_null_accept(struct svc_rqst *rqstp)
 {
-	struct kvec	*argv = &rqstp->rq_arg.head[0];
 	struct kvec	*resv = &rqstp->rq_res.head[0];
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
 	struct svc_cred	*cred = &rqstp->rq_cred;
+	u32 flavor, len;
+	void *body;
 
-	if (argv->iov_len < 3*4)
-		return SVC_GARBAGE;
+	svcxdr_init_decode(rqstp);
 
-	if (svc_getu32(argv) != 0) {
-		dprintk("svc: bad null cred\n");
+	/* Length of Call's credential body field: */
+	if (xdr_stream_decode_u32(xdr, &len) < 0)
+		return SVC_GARBAGE;
+	if (len != 0) {
 		rqstp->rq_auth_stat = rpc_autherr_badcred;
 		return SVC_DENIED;
 	}
-	if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
-		dprintk("svc: bad null verf\n");
+
+	/* Call's verf field: */
+	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+		return SVC_GARBAGE;
+	if (flavor != RPC_AUTH_NULL || len != 0) {
 		rqstp->rq_auth_stat = rpc_autherr_badverf;
 		return SVC_DENIED;
 	}
@@ -762,7 +780,6 @@ svcauth_null_accept(struct svc_rqst *rqstp)
 	svc_putnl(resv, 0);
 
 	rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
-	svcxdr_init_decode(rqstp);
 	return SVC_OK;
 }
 



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 05/25] SUNRPC: Convert svcauth_unix_accept() to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (3 preceding siblings ...)
  2023-01-02 17:05 ` [PATCH v1 04/25] SUNRPC: Convert svcauth_null_accept() to use xdr_stream Chuck Lever
@ 2023-01-02 17:05 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 06/25] SUNRPC: Convert svcauth_tls_accept() " Chuck Lever
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:05 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Since the server-side of the Linux kernel SunRPC implementation
ignores the contents of the Call's machinename field, there's no
need for its RPC_AUTH_UNIX authenticator to reject names that are
larger than UNX_MAXNODENAME.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 include/linux/sunrpc/msg_prot.h |    5 +++
 net/sunrpc/svcauth_unix.c       |   71 ++++++++++++++++++++++++++++-----------
 2 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/include/linux/sunrpc/msg_prot.h b/include/linux/sunrpc/msg_prot.h
index 02117ed0fa2e..c4b0eb2b2f04 100644
--- a/include/linux/sunrpc/msg_prot.h
+++ b/include/linux/sunrpc/msg_prot.h
@@ -34,6 +34,11 @@ enum rpc_auth_flavors {
 	RPC_AUTH_GSS_SPKMP = 390011,
 };
 
+/* Maximum size (in octets) of the machinename in an AUTH_UNIX
+ * credential (per RFC 5531 Appendix A)
+ */
+#define RPC_MAX_MACHINENAME	(255)
+
 /* Maximum size (in bytes) of an rpc credential or verifier */
 #define RPC_MAX_AUTH_SIZE (400)
 
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 95354f03bb05..b6aef9c5113b 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -867,26 +867,45 @@ struct auth_ops svcauth_tls = {
 };
 
 
+/**
+ * svcauth_unix_accept - Decode and validate incoming RPC_AUTH_SYS credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ *   %SVC_OK: Both credential and verifier are valid
+ *   %SVC_DENIED: Credential or verifier is not valid
+ *   %SVC_GARBAGE: Failed to decode credential or verifier
+ *   %SVC_CLOSE: Temporary failure
+ *
+ * rqstp->rq_auth_stat is set as mandated by RFC 5531.
+ */
 static int
 svcauth_unix_accept(struct svc_rqst *rqstp)
 {
-	struct kvec	*argv = &rqstp->rq_arg.head[0];
 	struct kvec	*resv = &rqstp->rq_res.head[0];
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
 	struct svc_cred	*cred = &rqstp->rq_cred;
 	struct user_namespace *userns;
-	u32		slen, i;
-	int		len   = argv->iov_len;
+	u32 flavor, len, i;
+	void *body;
+	__be32 *p;
+
+	svcxdr_init_decode(rqstp);
 
-	if ((len -= 3*4) < 0)
+	/*
+	 * This implementation ignores the length of the Call's
+	 * credential body field and the timestamp and machinename
+	 * fields.
+	 */
+	p = xdr_inline_decode(xdr, XDR_UNIT * 3);
+	if (!p)
+		return SVC_GARBAGE;
+	len = be32_to_cpup(p + 2);
+	if (len > RPC_MAX_MACHINENAME)
+		return SVC_GARBAGE;
+	if (!xdr_inline_decode(xdr, len))
 		return SVC_GARBAGE;
 
-	svc_getu32(argv);			/* length */
-	svc_getu32(argv);			/* time stamp */
-	slen = XDR_QUADLEN(svc_getnl(argv));	/* machname length */
-	if (slen > 64 || (len -= (slen + 3)*4) < 0)
-		goto badcred;
-	argv->iov_base = (void*)((__be32*)argv->iov_base + slen);	/* skip machname */
-	argv->iov_len -= slen*4;
 	/*
 	 * Note: we skip uid_valid()/gid_valid() checks here for
 	 * backwards compatibility with clients that use -1 id's.
@@ -896,20 +915,33 @@ svcauth_unix_accept(struct svc_rqst *rqstp)
 	 */
 	userns = (rqstp->rq_xprt && rqstp->rq_xprt->xpt_cred) ?
 		rqstp->rq_xprt->xpt_cred->user_ns : &init_user_ns;
-	cred->cr_uid = make_kuid(userns, svc_getnl(argv)); /* uid */
-	cred->cr_gid = make_kgid(userns, svc_getnl(argv)); /* gid */
-	slen = svc_getnl(argv);			/* gids length */
-	if (slen > UNX_NGROUPS || (len -= (slen + 2)*4) < 0)
+	if (xdr_stream_decode_u32(xdr, &i) < 0)
+		return SVC_GARBAGE;
+	cred->cr_uid = make_kuid(userns, i);
+	if (xdr_stream_decode_u32(xdr, &i) < 0)
+		return SVC_GARBAGE;
+	cred->cr_gid = make_kgid(userns, i);
+
+	if (xdr_stream_decode_u32(xdr, &len) < 0)
+		return SVC_GARBAGE;
+	if (len > UNX_NGROUPS)
 		goto badcred;
-	cred->cr_group_info = groups_alloc(slen);
+	p = xdr_inline_decode(xdr, XDR_UNIT * len);
+	if (!p)
+		return SVC_GARBAGE;
+	cred->cr_group_info = groups_alloc(len);
 	if (cred->cr_group_info == NULL)
 		return SVC_CLOSE;
-	for (i = 0; i < slen; i++) {
-		kgid_t kgid = make_kgid(userns, svc_getnl(argv));
+	for (i = 0; i < len; i++) {
+		kgid_t kgid = make_kgid(userns, be32_to_cpup(p++));
 		cred->cr_group_info->gid[i] = kgid;
 	}
 	groups_sort(cred->cr_group_info);
-	if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
+
+	/* Call's verf field: */
+	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+		return SVC_GARBAGE;
+	if (flavor != RPC_AUTH_NULL || len != 0) {
 		rqstp->rq_auth_stat = rpc_autherr_badverf;
 		return SVC_DENIED;
 	}
@@ -919,7 +951,6 @@ svcauth_unix_accept(struct svc_rqst *rqstp)
 	svc_putnl(resv, 0);
 
 	rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
-	svcxdr_init_decode(rqstp);
 	return SVC_OK;
 
 badcred:



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 06/25] SUNRPC: Convert svcauth_tls_accept() to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (4 preceding siblings ...)
  2023-01-02 17:05 ` [PATCH v1 05/25] SUNRPC: Convert svcauth_unix_accept() " Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 07/25] SUNRPC: Move the server-side GSS upcall to a noinline function Chuck Lever
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svcauth_unix.c |   37 ++++++++++++++++++++++++++-----------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index b6aef9c5113b..168e12137754 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -807,25 +807,41 @@ struct auth_ops svcauth_null = {
 };
 
 
+/**
+ * svcauth_tls_accept - Decode and validate incoming RPC_AUTH_TLS credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ *   %SVC_OK: Both credential and verifier are valid
+ *   %SVC_DENIED: Credential or verifier is not valid
+ *   %SVC_GARBAGE: Failed to decode credential or verifier
+ *   %SVC_CLOSE: Temporary failure
+ *
+ * rqstp->rq_auth_stat is set as mandated by RFC 5531.
+ */
 static int
 svcauth_tls_accept(struct svc_rqst *rqstp)
 {
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
 	struct svc_cred	*cred = &rqstp->rq_cred;
-	struct kvec *argv = rqstp->rq_arg.head;
 	struct kvec *resv = rqstp->rq_res.head;
+	u32 flavor, len;
+	void *body;
 
-	if (argv->iov_len < XDR_UNIT * 3)
-		return SVC_GARBAGE;
+	svcxdr_init_decode(rqstp);
 
-	/* Call's cred length */
-	if (svc_getu32(argv) != xdr_zero) {
+	/* Length of Call's credential body field: */
+	if (xdr_stream_decode_u32(xdr, &len) < 0)
+		return SVC_GARBAGE;
+	if (len != 0) {
 		rqstp->rq_auth_stat = rpc_autherr_badcred;
 		return SVC_DENIED;
 	}
 
-	/* Call's verifier flavor and its length */
-	if (svc_getu32(argv) != rpc_auth_null ||
-	    svc_getu32(argv) != xdr_zero) {
+	/* Call's verf field: */
+	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+		return SVC_GARBAGE;
+	if (flavor != RPC_AUTH_NULL || len != 0) {
 		rqstp->rq_auth_stat = rpc_autherr_badverf;
 		return SVC_DENIED;
 	}
@@ -836,12 +852,12 @@ svcauth_tls_accept(struct svc_rqst *rqstp)
 		return SVC_DENIED;
 	}
 
-	/* Mapping to nobody uid/gid is required */
+	/* Signal that mapping to nobody uid/gid is required */
 	cred->cr_uid = INVALID_UID;
 	cred->cr_gid = INVALID_GID;
 	cred->cr_group_info = groups_alloc(0);
 	if (cred->cr_group_info == NULL)
-		return SVC_CLOSE; /* kmalloc failure - client must retry */
+		return SVC_CLOSE;
 
 	/* Reply's verifier */
 	svc_putnl(resv, RPC_AUTH_NULL);
@@ -853,7 +869,6 @@ svcauth_tls_accept(struct svc_rqst *rqstp)
 		svc_putnl(resv, 0);
 
 	rqstp->rq_cred.cr_flavor = RPC_AUTH_TLS;
-	svcxdr_init_decode(rqstp);
 	return SVC_OK;
 }
 



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 07/25] SUNRPC: Move the server-side GSS upcall to a noinline function
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (5 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 06/25] SUNRPC: Convert svcauth_tls_accept() " Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 08/25] SUNRPC: Hoist common verifier decoding code into svcauth_gss_proc_init() Chuck Lever
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Since upcalls are infrequent, ensure the compiler places the upcall
mechanism out-of-line from the I/O path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 8ebc06bebbec..2a11721dd5c4 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1444,6 +1444,14 @@ static bool use_gss_proxy(struct net *net)
 	return sn->use_gss_proxy;
 }
 
+static noinline_for_stack int
+svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc)
+{
+	if (!use_gss_proxy(SVC_NET(rqstp)))
+		return svcauth_gss_legacy_init(rqstp, gc);
+	return svcauth_gss_proxy_init(rqstp, gc);
+}
+
 #ifdef CONFIG_PROC_FS
 
 static ssize_t write_gssp(struct file *file, const char __user *buf,
@@ -1600,10 +1608,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 	switch (gc->gc_proc) {
 	case RPC_GSS_PROC_INIT:
 	case RPC_GSS_PROC_CONTINUE_INIT:
-		if (use_gss_proxy(SVC_NET(rqstp)))
-			return svcauth_gss_proxy_init(rqstp, gc);
-		else
-			return svcauth_gss_legacy_init(rqstp, gc);
+		return svcauth_gss_proc_init(rqstp, gc);
 	case RPC_GSS_PROC_DATA:
 	case RPC_GSS_PROC_DESTROY:
 		/* Look up the context, and check the verifier: */



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 08/25] SUNRPC: Hoist common verifier decoding code into svcauth_gss_proc_init()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (6 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 07/25] SUNRPC: Move the server-side GSS upcall to a noinline function Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 09/25] SUNRPC: Remove gss_read_common_verf() Chuck Lever
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Pre-requisite to replacing gss_read_common_verf().

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 2a11721dd5c4..ba32c05f0258 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1095,18 +1095,6 @@ gss_read_common_verf(struct rpc_gss_wire_cred *gc,
 		     struct kvec *argv, __be32 *authp,
 		     struct xdr_netobj *in_handle)
 {
-	/* Read the verifier; should be NULL: */
-	*authp = rpc_autherr_badverf;
-	if (argv->iov_len < 2 * 4)
-		return SVC_DENIED;
-	if (svc_getnl(argv) != RPC_AUTH_NULL)
-		return SVC_DENIED;
-	if (svc_getnl(argv) != 0)
-		return SVC_DENIED;
-	/* Martial context handle and token for upcall: */
-	*authp = rpc_autherr_badcred;
-	if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0)
-		return SVC_DENIED;
 	if (dup_netobj(in_handle, &gc->gc_ctx))
 		return SVC_CLOSE;
 	*authp = rpc_autherr_badverf;
@@ -1447,6 +1435,20 @@ static bool use_gss_proxy(struct net *net)
 static noinline_for_stack int
 svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc)
 {
+	struct kvec *argv = rqstp->rq_arg.head;
+
+	if (argv->iov_len < 2 * 4)
+		return SVC_DENIED;
+	if (svc_getnl(argv) != RPC_AUTH_NULL)
+		return SVC_DENIED;
+	if (svc_getnl(argv) != 0)
+		return SVC_DENIED;
+
+	if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) {
+		rqstp->rq_auth_stat = rpc_autherr_badcred;
+		return SVC_DENIED;
+	}
+
 	if (!use_gss_proxy(SVC_NET(rqstp)))
 		return svcauth_gss_legacy_init(rqstp, gc);
 	return svcauth_gss_proxy_init(rqstp, gc);



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 09/25] SUNRPC: Remove gss_read_common_verf()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (7 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 08/25] SUNRPC: Hoist common verifier decoding code into svcauth_gss_proc_init() Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 10/25] SUNRPC: Remove gss_read_verf() Chuck Lever
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

gss_read_common_verf() is now just a wrapper for dup_netobj(), thus
it can be replaced with direct calls to dup_netobj().

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   26 +++++---------------------
 1 file changed, 5 insertions(+), 21 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index ba32c05f0258..5da4a3d48a7d 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1090,18 +1090,6 @@ gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
 	return rc;
 }
 
-static inline int
-gss_read_common_verf(struct rpc_gss_wire_cred *gc,
-		     struct kvec *argv, __be32 *authp,
-		     struct xdr_netobj *in_handle)
-{
-	if (dup_netobj(in_handle, &gc->gc_ctx))
-		return SVC_CLOSE;
-	*authp = rpc_autherr_badverf;
-
-	return 0;
-}
-
 static inline int
 gss_read_verf(struct rpc_gss_wire_cred *gc,
 	      struct kvec *argv, __be32 *authp,
@@ -1109,12 +1097,9 @@ gss_read_verf(struct rpc_gss_wire_cred *gc,
 	      struct xdr_netobj *in_token)
 {
 	struct xdr_netobj tmpobj;
-	int res;
-
-	res = gss_read_common_verf(gc, argv, authp, in_handle);
-	if (res)
-		return res;
 
+	if (dup_netobj(in_handle, &gc->gc_ctx))
+		return SVC_CLOSE;
 	if (svc_safe_getnetobj(argv, &tmpobj)) {
 		kfree(in_handle->data);
 		return SVC_DENIED;
@@ -1151,12 +1136,11 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
 {
 	struct kvec *argv = &rqstp->rq_arg.head[0];
 	unsigned int length, pgto_offs, pgfrom_offs;
-	int pages, i, res, pgto, pgfrom;
 	size_t inlen, to_offs, from_offs;
+	int pages, i, pgto, pgfrom;
 
-	res = gss_read_common_verf(gc, argv, &rqstp->rq_auth_stat, in_handle);
-	if (res)
-		return res;
+	if (dup_netobj(in_handle, &gc->gc_ctx))
+		return SVC_CLOSE;
 
 	inlen = svc_getnl(argv);
 	if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) {



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 10/25] SUNRPC: Remove gss_read_verf()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (8 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 09/25] SUNRPC: Remove gss_read_common_verf() Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 11/25] SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream Chuck Lever
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

gss_read_verf() is already short. Fold it into its only caller.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   37 +++++++++++--------------------------
 1 file changed, 11 insertions(+), 26 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 5da4a3d48a7d..5b03a97e32b7 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1090,28 +1090,6 @@ gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp,
 	return rc;
 }
 
-static inline int
-gss_read_verf(struct rpc_gss_wire_cred *gc,
-	      struct kvec *argv, __be32 *authp,
-	      struct xdr_netobj *in_handle,
-	      struct xdr_netobj *in_token)
-{
-	struct xdr_netobj tmpobj;
-
-	if (dup_netobj(in_handle, &gc->gc_ctx))
-		return SVC_CLOSE;
-	if (svc_safe_getnetobj(argv, &tmpobj)) {
-		kfree(in_handle->data);
-		return SVC_DENIED;
-	}
-	if (dup_netobj(in_token, &tmpobj)) {
-		kfree(in_handle->data);
-		return SVC_CLOSE;
-	}
-
-	return 0;
-}
-
 static void gss_free_in_token_pages(struct gssp_in_token *in_token)
 {
 	u32 inlen;
@@ -1224,14 +1202,21 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
 	struct kvec *argv = &rqstp->rq_arg.head[0];
 	struct kvec *resv = &rqstp->rq_res.head[0];
 	struct rsi *rsip, rsikey;
+	struct xdr_netobj tmpobj;
 	int ret;
 	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
 
 	memset(&rsikey, 0, sizeof(rsikey));
-	ret = gss_read_verf(gc, argv, &rqstp->rq_auth_stat,
-			    &rsikey.in_handle, &rsikey.in_token);
-	if (ret)
-		return ret;
+	if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
+		return SVC_CLOSE;
+	if (svc_safe_getnetobj(argv, &tmpobj)) {
+		kfree(rsikey.in_handle.data);
+		return SVC_DENIED;
+	}
+	if (dup_netobj(&rsikey.in_token, &tmpobj)) {
+		kfree(rsikey.in_handle.data);
+		return SVC_CLOSE;
+	}
 
 	/* Perform upcall, or find upcall result: */
 	rsip = rsi_lookup(sn->rsi_cache, &rsikey);



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 11/25] SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (9 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 10/25] SUNRPC: Remove gss_read_verf() Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 12/25] SUNRPC: Replace read_u32_from_xdr_buf() with existing XDR helper Chuck Lever
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

The entire RPC_GSS_PROC_INIT path is converted over to xdr_stream
for decoding the Call credential and verifier.

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   87 +++++++++++++++++++++++++------------
 1 file changed, 58 insertions(+), 29 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 5b03a97e32b7..8e8dec664a89 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1112,39 +1112,43 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
 			       struct xdr_netobj *in_handle,
 			       struct gssp_in_token *in_token)
 {
-	struct kvec *argv = &rqstp->rq_arg.head[0];
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
 	unsigned int length, pgto_offs, pgfrom_offs;
-	size_t inlen, to_offs, from_offs;
 	int pages, i, pgto, pgfrom;
+	size_t to_offs, from_offs;
+	u32 inlen;
 
 	if (dup_netobj(in_handle, &gc->gc_ctx))
 		return SVC_CLOSE;
 
-	inlen = svc_getnl(argv);
-	if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) {
-		kfree(in_handle->data);
-		return SVC_DENIED;
-	}
+	/*
+	 *  RFC 2203 Section 5.2.2
+	 *
+	 *	struct rpc_gss_init_arg {
+	 *		opaque gss_token<>;
+	 *	};
+	 */
+	if (xdr_stream_decode_u32(xdr, &inlen) < 0)
+		goto out_denied_free;
+	if (inlen > xdr_stream_remaining(xdr))
+		goto out_denied_free;
 
 	pages = DIV_ROUND_UP(inlen, PAGE_SIZE);
 	in_token->pages = kcalloc(pages, sizeof(struct page *), GFP_KERNEL);
-	if (!in_token->pages) {
-		kfree(in_handle->data);
-		return SVC_DENIED;
-	}
+	if (!in_token->pages)
+		goto out_denied_free;
 	in_token->page_base = 0;
 	in_token->page_len = inlen;
 	for (i = 0; i < pages; i++) {
 		in_token->pages[i] = alloc_page(GFP_KERNEL);
 		if (!in_token->pages[i]) {
-			kfree(in_handle->data);
 			gss_free_in_token_pages(in_token);
-			return SVC_DENIED;
+			goto out_denied_free;
 		}
 	}
 
-	length = min_t(unsigned int, inlen, argv->iov_len);
-	memcpy(page_address(in_token->pages[0]), argv->iov_base, length);
+	length = min_t(unsigned int, inlen, (char *)xdr->end - (char *)xdr->p);
+	memcpy(page_address(in_token->pages[0]), xdr->p, length);
 	inlen -= length;
 
 	to_offs = length;
@@ -1167,6 +1171,10 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,
 		inlen -= length;
 	}
 	return 0;
+
+out_denied_free:
+	kfree(in_handle->data);
+	return SVC_DENIED;
 }
 
 static inline int
@@ -1196,27 +1204,45 @@ gss_write_resv(struct kvec *resv, size_t size_limit,
  * the upcall results are available, write the verifier and result.
  * Otherwise, drop the request pending an answer to the upcall.
  */
-static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
-				   struct rpc_gss_wire_cred *gc)
+static int
+svcauth_gss_legacy_init(struct svc_rqst *rqstp,
+			struct rpc_gss_wire_cred *gc)
 {
-	struct kvec *argv = &rqstp->rq_arg.head[0];
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
 	struct kvec *resv = &rqstp->rq_res.head[0];
 	struct rsi *rsip, rsikey;
-	struct xdr_netobj tmpobj;
+	__be32 *p;
+	u32 len;
 	int ret;
 	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
 
 	memset(&rsikey, 0, sizeof(rsikey));
 	if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
 		return SVC_CLOSE;
-	if (svc_safe_getnetobj(argv, &tmpobj)) {
+
+	/*
+	 *  RFC 2203 Section 5.2.2
+	 *
+	 *	struct rpc_gss_init_arg {
+	 *		opaque gss_token<>;
+	 *	};
+	 */
+	if (xdr_stream_decode_u32(xdr, &len) < 0) {
+		kfree(rsikey.in_handle.data);
+		return SVC_DENIED;
+	}
+	p = xdr_inline_decode(xdr, len);
+	if (!p) {
 		kfree(rsikey.in_handle.data);
 		return SVC_DENIED;
 	}
-	if (dup_netobj(&rsikey.in_token, &tmpobj)) {
+	rsikey.in_token.data = kmalloc(len, GFP_KERNEL);
+	if (ZERO_OR_NULL_PTR(rsikey.in_token.data)) {
 		kfree(rsikey.in_handle.data);
 		return SVC_CLOSE;
 	}
+	memcpy(rsikey.in_token.data, p, len);
+	rsikey.in_token.len = len;
 
 	/* Perform upcall, or find upcall result: */
 	rsip = rsi_lookup(sn->rsi_cache, &rsikey);
@@ -1237,7 +1263,6 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
 			   rsip->major_status, rsip->minor_status))
 		goto out;
 
-	svcxdr_init_decode(rqstp);
 	ret = SVC_COMPLETE;
 out:
 	cache_put(&rsip->h, sn->rsi_cache);
@@ -1366,7 +1391,6 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
 			   ud.major_status, ud.minor_status))
 		goto out;
 
-	svcxdr_init_decode(rqstp);
 	ret = SVC_COMPLETE;
 out:
 	gss_free_in_token_pages(&ud.in_token);
@@ -1404,14 +1428,19 @@ static bool use_gss_proxy(struct net *net)
 static noinline_for_stack int
 svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc)
 {
-	struct kvec *argv = rqstp->rq_arg.head;
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
+	u32 flavor, len;
+	void *body;
 
-	if (argv->iov_len < 2 * 4)
-		return SVC_DENIED;
-	if (svc_getnl(argv) != RPC_AUTH_NULL)
-		return SVC_DENIED;
-	if (svc_getnl(argv) != 0)
+	svcxdr_init_decode(rqstp);
+
+	/* Call's verf field: */
+	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
+		return SVC_GARBAGE;
+	if (flavor != RPC_AUTH_NULL || len != 0) {
+		rqstp->rq_auth_stat = rpc_autherr_badverf;
 		return SVC_DENIED;
+	}
 
 	if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) {
 		rqstp->rq_auth_stat = rpc_autherr_badcred;



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 12/25] SUNRPC: Replace read_u32_from_xdr_buf() with existing XDR helper
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (10 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 11/25] SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 13/25] SUNRPC: Rename automatic variables in unwrap_integ_data() Chuck Lever
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Clean up / code de-duplication - this functionality is already
available in the generic XDR layer.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 8e8dec664a89..0f9700c86f62 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -891,19 +891,6 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
 }
 EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
 
-static inline int
-read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
-{
-	__be32  raw;
-	int     status;
-
-	status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj));
-	if (status)
-		return status;
-	*obj = ntohl(raw);
-	return 0;
-}
-
 /* It would be nice if this bit of code could be shared with the client.
  * Obstacles:
  *	The client shouldn't malloc(), would have to pass in own memory.
@@ -937,8 +924,7 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
 	if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
 		goto unwrap_failed;
 
-	/* copy out mic... */
-	if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
+	if (xdr_decode_word(buf, integ_len, &mic.len))
 		goto unwrap_failed;
 	if (mic.len > sizeof(gsd->gsd_scratch))
 		goto unwrap_failed;



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 13/25] SUNRPC: Rename automatic variables in unwrap_integ_data()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (11 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 12/25] SUNRPC: Replace read_u32_from_xdr_buf() with existing XDR helper Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:06 ` [PATCH v1 14/25] SUNRPC: Convert unwrap_integ_data() to use xdr_stream Chuck Lever
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Clean up: To help orient readers, name the stack variables to match
the XDR field names.

For readability, I'm also going to rename the unwrap and wrap
functions in a consistent manner, starting with unwrap_integ_data().

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   57 +++++++++++++++++++++----------------
 1 file changed, 33 insertions(+), 24 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 0f9700c86f62..33fe307372d0 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -891,18 +891,27 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
 }
 EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
 
-/* It would be nice if this bit of code could be shared with the client.
- * Obstacles:
- *	The client shouldn't malloc(), would have to pass in own memory.
- *	The server uses base of head iovec as read pointer, while the
- *	client uses separate pointer. */
+/*
+ * RFC 2203, Section 5.3.2.2
+ *
+ *	struct rpc_gss_integ_data {
+ *		opaque databody_integ<>;
+ *		opaque checksum<>;
+ *	};
+ *
+ *	struct rpc_gss_data_t {
+ *		unsigned int seq_num;
+ *		proc_req_arg_t arg;
+ *	};
+ */
 static int
-unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
+svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq,
+			 struct gss_ctx *ctx)
 {
 	struct gss_svc_data *gsd = rqstp->rq_auth_data;
-	u32 integ_len, rseqno, maj_stat;
-	struct xdr_netobj mic;
-	struct xdr_buf integ_buf;
+	struct xdr_buf databody_integ;
+	u32 len, seq_num, maj_stat;
+	struct xdr_netobj checksum;
 
 	/* NFS READ normally uses splice to send data in-place. However
 	 * the data in cache can change after the reply's MIC is computed
@@ -916,36 +925,36 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
 	if (rqstp->rq_deferred)
 		return 0;
 
-	integ_len = svc_getnl(&buf->head[0]);
-	if (integ_len & 3)
+	len = svc_getnl(&buf->head[0]);
+	if (len & 3)
 		goto unwrap_failed;
-	if (integ_len > buf->len)
+	if (len > buf->len)
 		goto unwrap_failed;
-	if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
+	if (xdr_buf_subsegment(buf, &databody_integ, 0, len))
 		goto unwrap_failed;
 
-	if (xdr_decode_word(buf, integ_len, &mic.len))
+	if (xdr_decode_word(buf, len, &checksum.len))
 		goto unwrap_failed;
-	if (mic.len > sizeof(gsd->gsd_scratch))
+	if (checksum.len > sizeof(gsd->gsd_scratch))
 		goto unwrap_failed;
-	mic.data = gsd->gsd_scratch;
-	if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len))
+	checksum.data = gsd->gsd_scratch;
+	if (read_bytes_from_xdr_buf(buf, len + 4, checksum.data, checksum.len))
 		goto unwrap_failed;
-	maj_stat = gss_verify_mic(ctx, &integ_buf, &mic);
+	maj_stat = gss_verify_mic(ctx, &databody_integ, &checksum);
 	if (maj_stat != GSS_S_COMPLETE)
 		goto bad_mic;
-	rseqno = svc_getnl(&buf->head[0]);
-	if (rseqno != seq)
+	seq_num = svc_getnl(&buf->head[0]);
+	if (seq_num != seq)
 		goto bad_seqno;
 	/* trim off the mic and padding at the end before returning */
-	xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
+	xdr_buf_trim(buf, round_up_to_quad(checksum.len) + 4);
 	return 0;
 
 unwrap_failed:
 	trace_rpcgss_svc_unwrap_failed(rqstp);
 	return -EINVAL;
 bad_seqno:
-	trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
+	trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num);
 	return -EINVAL;
 bad_mic:
 	trace_rpcgss_svc_mic(rqstp, maj_stat);
@@ -1643,8 +1652,8 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 			/* placeholders for length and seq. number: */
 			svc_putnl(resv, 0);
 			svc_putnl(resv, 0);
-			if (unwrap_integ_data(rqstp, &rqstp->rq_arg,
-					gc->gc_seq, rsci->mechctx))
+			if (svcauth_gss_unwrap_integ(rqstp, &rqstp->rq_arg,
+						     gc->gc_seq, rsci->mechctx))
 				goto garbage_args;
 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
 			svcxdr_init_decode(rqstp);



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 14/25] SUNRPC: Convert unwrap_integ_data() to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (12 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 13/25] SUNRPC: Rename automatic variables in unwrap_integ_data() Chuck Lever
@ 2023-01-02 17:06 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 15/25] SUNRPC: Rename automatic variables in unwrap_priv_data() Chuck Lever
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:06 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 include/linux/sunrpc/xdr.h        |    1 +
 net/sunrpc/auth_gss/svcauth_gss.c |   47 ++++++++++++++++++++++++-------------
 net/sunrpc/xdr.c                  |   15 ++++++++++++
 3 files changed, 47 insertions(+), 16 deletions(-)

diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 8b5c9d0cdcb5..accfe8d6e283 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -247,6 +247,7 @@ extern int xdr_reserve_space_vec(struct xdr_stream *xdr, struct kvec *vec,
 		size_t nbytes);
 extern void __xdr_commit_encode(struct xdr_stream *xdr);
 extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
+extern void xdr_truncate_decode(struct xdr_stream *xdr, size_t len);
 extern int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen);
 extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
 		unsigned int base, unsigned int len);
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 33fe307372d0..d049db997ab7 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -904,13 +904,14 @@ EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
  *		proc_req_arg_t arg;
  *	};
  */
-static int
-svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq,
-			 struct gss_ctx *ctx)
+static noinline_for_stack int
+svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx)
 {
 	struct gss_svc_data *gsd = rqstp->rq_auth_data;
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
+	u32 len, offset, seq_num, maj_stat;
+	struct xdr_buf *buf = xdr->buf;
 	struct xdr_buf databody_integ;
-	u32 len, seq_num, maj_stat;
 	struct xdr_netobj checksum;
 
 	/* NFS READ normally uses splice to send data in-place. However
@@ -925,29 +926,43 @@ svcauth_gss_unwrap_integ(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq,
 	if (rqstp->rq_deferred)
 		return 0;
 
-	len = svc_getnl(&buf->head[0]);
-	if (len & 3)
+	if (xdr_stream_decode_u32(xdr, &len) < 0)
 		goto unwrap_failed;
-	if (len > buf->len)
+	if (len & 3)
 		goto unwrap_failed;
-	if (xdr_buf_subsegment(buf, &databody_integ, 0, len))
+	offset = xdr_stream_pos(xdr);
+	if (xdr_buf_subsegment(buf, &databody_integ, offset, len))
 		goto unwrap_failed;
 
-	if (xdr_decode_word(buf, len, &checksum.len))
+	/*
+	 * The xdr_stream now points to the @seq_num field. The next
+	 * XDR data item is the @arg field, which contains the clear
+	 * text RPC program payload. The checksum, which follows the
+	 * @arg field, is located and decoded without updating the
+	 * xdr_stream.
+	 */
+
+	offset += len;
+	if (xdr_decode_word(buf, offset, &checksum.len))
 		goto unwrap_failed;
 	if (checksum.len > sizeof(gsd->gsd_scratch))
 		goto unwrap_failed;
 	checksum.data = gsd->gsd_scratch;
-	if (read_bytes_from_xdr_buf(buf, len + 4, checksum.data, checksum.len))
+	if (read_bytes_from_xdr_buf(buf, offset + XDR_UNIT, checksum.data,
+				    checksum.len))
 		goto unwrap_failed;
+
 	maj_stat = gss_verify_mic(ctx, &databody_integ, &checksum);
 	if (maj_stat != GSS_S_COMPLETE)
 		goto bad_mic;
-	seq_num = svc_getnl(&buf->head[0]);
+
+	/* The received seqno is protected by the checksum. */
+	if (xdr_stream_decode_u32(xdr, &seq_num) < 0)
+		goto unwrap_failed;
 	if (seq_num != seq)
 		goto bad_seqno;
-	/* trim off the mic and padding at the end before returning */
-	xdr_buf_trim(buf, round_up_to_quad(checksum.len) + 4);
+
+	xdr_truncate_decode(xdr, XDR_UNIT + checksum.len);
 	return 0;
 
 unwrap_failed:
@@ -1652,11 +1667,11 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 			/* placeholders for length and seq. number: */
 			svc_putnl(resv, 0);
 			svc_putnl(resv, 0);
-			if (svcauth_gss_unwrap_integ(rqstp, &rqstp->rq_arg,
-						     gc->gc_seq, rsci->mechctx))
+			svcxdr_init_decode(rqstp);
+			if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq,
+						     rsci->mechctx))
 				goto garbage_args;
 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
-			svcxdr_init_decode(rqstp);
 			break;
 		case RPC_GSS_SVC_PRIVACY:
 			/* placeholders for length and seq. number: */
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 4845ba2113fd..c7e89921d511 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -1192,6 +1192,21 @@ void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
 }
 EXPORT_SYMBOL(xdr_truncate_encode);
 
+/**
+ * xdr_truncate_decode - Truncate a decoding stream
+ * @xdr: pointer to struct xdr_stream
+ * @len: Number of bytes to remove
+ *
+ */
+void xdr_truncate_decode(struct xdr_stream *xdr, size_t len)
+{
+	unsigned int nbytes = xdr_align_size(len);
+
+	xdr->buf->len -= nbytes;
+	xdr->nwords -= XDR_QUADLEN(nbytes);
+}
+EXPORT_SYMBOL_GPL(xdr_truncate_decode);
+
 /**
  * xdr_restrict_buflen - decrease available buffer space
  * @xdr: pointer to xdr_stream



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 15/25] SUNRPC: Rename automatic variables in unwrap_priv_data()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (13 preceding siblings ...)
  2023-01-02 17:06 ` [PATCH v1 14/25] SUNRPC: Convert unwrap_integ_data() to use xdr_stream Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 16/25] SUNRPC: Convert unwrap_priv_data() to use xdr_stream Chuck Lever
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Clean up: To help orient readers, name the stack variables to match
the XDR field names.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   38 ++++++++++++++++++++++++-------------
 1 file changed, 25 insertions(+), 13 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index d049db997ab7..8f91768d0be0 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -993,16 +993,28 @@ fix_priv_head(struct xdr_buf *buf, int pad)
 	}
 }
 
+/*
+ * RFC 2203, Section 5.3.2.3
+ *
+ *	struct rpc_gss_priv_data {
+ *		opaque databody_priv<>
+ *	};
+ *
+ *	struct rpc_gss_data_t {
+ *		unsigned int seq_num;
+ *		proc_req_arg_t arg;
+ *	};
+ */
 static int
-unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx)
+svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq,
+			struct gss_ctx *ctx)
 {
-	u32 priv_len, maj_stat;
+	u32 len, seq_num, maj_stat;
 	int pad, remaining_len, offset;
-	u32 rseqno;
 
 	__clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
 
-	priv_len = svc_getnl(&buf->head[0]);
+	len = svc_getnl(&buf->head[0]);
 	if (rqstp->rq_deferred) {
 		/* Already decrypted last time through! The sequence number
 		 * check at out_seq is unnecessary but harmless: */
@@ -1012,14 +1024,14 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
 	 * request to the end, where head[0].iov_len is just the bytes
 	 * not yet read from the head, so these two values are different: */
 	remaining_len = total_buf_len(buf);
-	if (priv_len > remaining_len)
+	if (len > remaining_len)
 		goto unwrap_failed;
-	pad = remaining_len - priv_len;
+	pad = remaining_len - len;
 	buf->len -= pad;
 	fix_priv_head(buf, pad);
 
-	maj_stat = gss_unwrap(ctx, 0, priv_len, buf);
-	pad = priv_len - buf->len;
+	maj_stat = gss_unwrap(ctx, 0, len, buf);
+	pad = len - buf->len;
 	/* The upper layers assume the buffer is aligned on 4-byte boundaries.
 	 * In the krb5p case, at least, the data ends up offset, so we need to
 	 * move it around. */
@@ -1035,8 +1047,8 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
 	if (maj_stat != GSS_S_COMPLETE)
 		goto bad_unwrap;
 out_seq:
-	rseqno = svc_getnl(&buf->head[0]);
-	if (rseqno != seq)
+	seq_num = svc_getnl(&buf->head[0]);
+	if (seq_num != seq)
 		goto bad_seqno;
 	return 0;
 
@@ -1044,7 +1056,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
 	trace_rpcgss_svc_unwrap_failed(rqstp);
 	return -EINVAL;
 bad_seqno:
-	trace_rpcgss_svc_seqno_bad(rqstp, seq, rseqno);
+	trace_rpcgss_svc_seqno_bad(rqstp, seq, seq_num);
 	return -EINVAL;
 bad_unwrap:
 	trace_rpcgss_svc_unwrap(rqstp, maj_stat);
@@ -1677,8 +1689,8 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 			/* placeholders for length and seq. number: */
 			svc_putnl(resv, 0);
 			svc_putnl(resv, 0);
-			if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
-					gc->gc_seq, rsci->mechctx))
+			if (svcauth_gss_unwrap_priv(rqstp, &rqstp->rq_arg,
+						    gc->gc_seq, rsci->mechctx))
 				goto garbage_args;
 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
 			svcxdr_init_decode(rqstp);



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 16/25] SUNRPC: Convert unwrap_priv_data() to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (14 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 15/25] SUNRPC: Rename automatic variables in unwrap_priv_data() Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 17/25] SUNRPC: Convert gss_verify_header() " Chuck Lever
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 include/linux/sunrpc/xdr.h        |    1 -
 net/sunrpc/auth_gss/svcauth_gss.c |   65 ++++++++++++-------------------------
 net/sunrpc/xdr.c                  |    7 ----
 3 files changed, 21 insertions(+), 52 deletions(-)

diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index accfe8d6e283..884df67009f4 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -188,7 +188,6 @@ xdr_adjust_iovec(struct kvec *iov, __be32 *p)
 /*
  * XDR buffer helper functions
  */
-extern void xdr_shift_buf(struct xdr_buf *, size_t);
 extern void xdr_buf_from_iov(const struct kvec *, struct xdr_buf *);
 extern int xdr_buf_subsegment(const struct xdr_buf *, struct xdr_buf *, unsigned int, unsigned int);
 extern void xdr_buf_trim(struct xdr_buf *, unsigned int);
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 8f91768d0be0..074bfe9fb838 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -982,17 +982,6 @@ total_buf_len(struct xdr_buf *buf)
 	return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len;
 }
 
-static void
-fix_priv_head(struct xdr_buf *buf, int pad)
-{
-	if (buf->page_len == 0) {
-		/* We need to adjust head and buf->len in tandem in this
-		 * case to make svc_defer() work--it finds the original
-		 * buffer start using buf->len - buf->head[0].iov_len. */
-		buf->head[0].iov_len -= pad;
-	}
-}
-
 /*
  * RFC 2203, Section 5.3.2.3
  *
@@ -1005,49 +994,37 @@ fix_priv_head(struct xdr_buf *buf, int pad)
  *		proc_req_arg_t arg;
  *	};
  */
-static int
-svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq,
-			struct gss_ctx *ctx)
+static noinline_for_stack int
+svcauth_gss_unwrap_priv(struct svc_rqst *rqstp, u32 seq, struct gss_ctx *ctx)
 {
-	u32 len, seq_num, maj_stat;
-	int pad, remaining_len, offset;
+	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
+	u32 len, maj_stat, seq_num, offset;
+	struct xdr_buf *buf = xdr->buf;
+	unsigned int saved_len;
 
 	__clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
 
-	len = svc_getnl(&buf->head[0]);
+	if (xdr_stream_decode_u32(xdr, &len) < 0)
+		goto unwrap_failed;
 	if (rqstp->rq_deferred) {
 		/* Already decrypted last time through! The sequence number
 		 * check at out_seq is unnecessary but harmless: */
 		goto out_seq;
 	}
-	/* buf->len is the number of bytes from the original start of the
-	 * request to the end, where head[0].iov_len is just the bytes
-	 * not yet read from the head, so these two values are different: */
-	remaining_len = total_buf_len(buf);
-	if (len > remaining_len)
+	if (len > xdr_stream_remaining(xdr))
 		goto unwrap_failed;
-	pad = remaining_len - len;
-	buf->len -= pad;
-	fix_priv_head(buf, pad);
-
-	maj_stat = gss_unwrap(ctx, 0, len, buf);
-	pad = len - buf->len;
-	/* The upper layers assume the buffer is aligned on 4-byte boundaries.
-	 * In the krb5p case, at least, the data ends up offset, so we need to
-	 * move it around. */
-	/* XXX: This is very inefficient.  It would be better to either do
-	 * this while we encrypt, or maybe in the receive code, if we can peak
-	 * ahead and work out the service and mechanism there. */
-	offset = xdr_pad_size(buf->head[0].iov_len);
-	if (offset) {
-		buf->buflen = RPCSVC_MAXPAYLOAD;
-		xdr_shift_buf(buf, offset);
-		fix_priv_head(buf, pad);
-	}
+	offset = xdr_stream_pos(xdr);
+
+	saved_len = buf->len;
+	maj_stat = gss_unwrap(ctx, offset, offset + len, buf);
 	if (maj_stat != GSS_S_COMPLETE)
 		goto bad_unwrap;
+	xdr->nwords -= XDR_QUADLEN(saved_len - buf->len);
+
 out_seq:
-	seq_num = svc_getnl(&buf->head[0]);
+	/* gss_unwrap() decrypted the sequence number. */
+	if (xdr_stream_decode_u32(xdr, &seq_num) < 0)
+		goto unwrap_failed;
 	if (seq_num != seq)
 		goto bad_seqno;
 	return 0;
@@ -1689,11 +1666,11 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 			/* placeholders for length and seq. number: */
 			svc_putnl(resv, 0);
 			svc_putnl(resv, 0);
-			if (svcauth_gss_unwrap_priv(rqstp, &rqstp->rq_arg,
-						    gc->gc_seq, rsci->mechctx))
+			svcxdr_init_decode(rqstp);
+			if (svcauth_gss_unwrap_priv(rqstp, gc->gc_seq,
+						    rsci->mechctx))
 				goto garbage_args;
 			rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
-			svcxdr_init_decode(rqstp);
 			break;
 		default:
 			goto auth_err;
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index c7e89921d511..56d87c784c9e 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -863,13 +863,6 @@ static unsigned int xdr_shrink_pagelen(struct xdr_buf *buf, unsigned int len)
 	return shift;
 }
 
-void
-xdr_shift_buf(struct xdr_buf *buf, size_t len)
-{
-	xdr_shrink_bufhead(buf, buf->head->iov_len - len);
-}
-EXPORT_SYMBOL_GPL(xdr_shift_buf);
-
 /**
  * xdr_stream_pos - Return the current offset from the start of the xdr_stream
  * @xdr: pointer to struct xdr_stream



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 17/25] SUNRPC: Convert gss_verify_header() to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (15 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 16/25] SUNRPC: Convert unwrap_priv_data() to use xdr_stream Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 18/25] SUNRPC: Clean up svcauth_gss_accept's NULL procedure check Chuck Lever
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   52 +++++++++++++++++++++----------------
 1 file changed, 29 insertions(+), 23 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 074bfe9fb838..8362fc143754 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -732,38 +732,48 @@ svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
 }
 
 /*
- * Verify the checksum on the header and return SVC_OK on success.
- * Otherwise, return SVC_DROP (in the case of a bad sequence number)
- * or return SVC_DENIED and indicate error in rqstp->rq_auth_stat.
+ * Decode and verify a Call's verifier field. For RPC_AUTH_GSS Calls,
+ * the body of this field contains a variable length checksum.
+ *
+ * GSS-specific auth_stat values are mandated by RFC 2203 Section
+ * 5.3.3.3.
  */
 static int
-gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
-		  __be32 *rpcstart, struct rpc_gss_wire_cred *gc)
+svcauth_gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
+			  __be32 *rpcstart, struct rpc_gss_wire_cred *gc)
 {
+	struct xdr_stream	*xdr = &rqstp->rq_arg_stream;
 	struct gss_ctx		*ctx_id = rsci->mechctx;
+	u32			flavor, maj_stat;
 	struct xdr_buf		rpchdr;
 	struct xdr_netobj	checksum;
-	u32			flavor = 0;
-	struct kvec		*argv = &rqstp->rq_arg.head[0];
 	struct kvec		iov;
 
-	/* data to compute the checksum over: */
+	/*
+	 * Compute the checksum of the incoming Call from the
+	 * XID field to credential field:
+	 */
 	iov.iov_base = rpcstart;
-	iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
+	iov.iov_len = (u8 *)xdr->p - (u8 *)rpcstart;
 	xdr_buf_from_iov(&iov, &rpchdr);
 
-	rqstp->rq_auth_stat = rpc_autherr_badverf;
-	if (argv->iov_len < 4)
-		return SVC_DENIED;
-	flavor = svc_getnl(argv);
-	if (flavor != RPC_AUTH_GSS)
+	/* Call's verf field: */
+	if (xdr_stream_decode_opaque_auth(xdr, &flavor,
+					  (void **)&checksum.data,
+					  &checksum.len) < 0) {
+		rqstp->rq_auth_stat = rpc_autherr_badverf;
 		return SVC_DENIED;
-	if (svc_safe_getnetobj(argv, &checksum))
+	}
+	if (flavor != RPC_AUTH_GSS) {
+		rqstp->rq_auth_stat = rpc_autherr_badverf;
 		return SVC_DENIED;
+	}
 
-	if (rqstp->rq_deferred) /* skip verification of revisited request */
+	if (rqstp->rq_deferred)
 		return SVC_OK;
-	if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
+	maj_stat = gss_verify_mic(ctx_id, &rpchdr, &checksum);
+	if (maj_stat != GSS_S_COMPLETE) {
+		trace_rpcgss_svc_mic(rqstp, maj_stat);
 		rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
 		return SVC_DENIED;
 	}
@@ -1431,8 +1441,6 @@ svcauth_gss_proc_init(struct svc_rqst *rqstp, struct rpc_gss_wire_cred *gc)
 	u32 flavor, len;
 	void *body;
 
-	svcxdr_init_decode(rqstp);
-
 	/* Call's verf field: */
 	if (xdr_stream_decode_opaque_auth(xdr, &flavor, &body, &len) < 0)
 		return SVC_GARBAGE;
@@ -1603,6 +1611,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
 		goto auth_err;
 
+	svcxdr_init_decode(rqstp);
 	rqstp->rq_auth_stat = rpc_autherr_badverf;
 	switch (gc->gc_proc) {
 	case RPC_GSS_PROC_INIT:
@@ -1615,7 +1624,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 		rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
 		if (!rsci)
 			goto auth_err;
-		switch (gss_verify_header(rqstp, rsci, rpcstart, gc)) {
+		switch (svcauth_gss_verify_header(rqstp, rsci, rpcstart, gc)) {
 		case SVC_OK:
 			break;
 		case SVC_DENIED:
@@ -1650,13 +1659,11 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 		rqstp->rq_auth_stat = rpc_autherr_badcred;
 		switch (gc->gc_svc) {
 		case RPC_GSS_SVC_NONE:
-			svcxdr_init_decode(rqstp);
 			break;
 		case RPC_GSS_SVC_INTEGRITY:
 			/* placeholders for length and seq. number: */
 			svc_putnl(resv, 0);
 			svc_putnl(resv, 0);
-			svcxdr_init_decode(rqstp);
 			if (svcauth_gss_unwrap_integ(rqstp, gc->gc_seq,
 						     rsci->mechctx))
 				goto garbage_args;
@@ -1666,7 +1673,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 			/* placeholders for length and seq. number: */
 			svc_putnl(resv, 0);
 			svc_putnl(resv, 0);
-			svcxdr_init_decode(rqstp);
 			if (svcauth_gss_unwrap_priv(rqstp, gc->gc_seq,
 						    rsci->mechctx))
 				goto garbage_args;



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 18/25] SUNRPC: Clean up svcauth_gss_accept's NULL procedure check
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (16 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 17/25] SUNRPC: Convert gss_verify_header() " Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 19/25] SUNRPC: Convert the svcauth_gss_accept() pre-amble to use xdr_stream Chuck Lever
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Micro-optimizations:

1. The value of rqstp->rq_auth_stat is replaced no matter which
   arm of the switch is taken, so the initial assignment can be
   safely removed.

2. Avoid checking the value of gc->gc_proc twice in the I/O
   (RPC_GSS_PROC_DATA) path.

The cost is a little extra code redundancy.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |   14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 8362fc143754..363c25198547 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1608,17 +1608,19 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 	if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
 		goto auth_err;
 
-	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
-		goto auth_err;
-
 	svcxdr_init_decode(rqstp);
-	rqstp->rq_auth_stat = rpc_autherr_badverf;
+
 	switch (gc->gc_proc) {
 	case RPC_GSS_PROC_INIT:
 	case RPC_GSS_PROC_CONTINUE_INIT:
+		if (rqstp->rq_proc != 0)
+			goto auth_err;
 		return svcauth_gss_proc_init(rqstp, gc);
-	case RPC_GSS_PROC_DATA:
 	case RPC_GSS_PROC_DESTROY:
+		if (rqstp->rq_proc != 0)
+			goto auth_err;
+		fallthrough;
+	case RPC_GSS_PROC_DATA:
 		/* Look up the context, and check the verifier: */
 		rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
 		rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
@@ -1634,6 +1636,8 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 		}
 		break;
 	default:
+		if (rqstp->rq_proc != 0)
+			goto auth_err;
 		rqstp->rq_auth_stat = rpc_autherr_rejectedcred;
 		goto auth_err;
 	}



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 19/25] SUNRPC: Convert the svcauth_gss_accept() pre-amble to use xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (17 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 18/25] SUNRPC: Clean up svcauth_gss_accept's NULL procedure check Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 20/25] SUNRPC: Hoist init_decode out of svc_authenticate() Chuck Lever
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |  125 ++++++++++++++++++++++---------------
 1 file changed, 75 insertions(+), 50 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 363c25198547..557de28127fe 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -697,23 +697,6 @@ static inline u32 round_up_to_quad(u32 i)
 	return (i + 3 ) & ~3;
 }
 
-static inline int
-svc_safe_getnetobj(struct kvec *argv, struct xdr_netobj *o)
-{
-	int l;
-
-	if (argv->iov_len < 4)
-		return -1;
-	o->len = svc_getnl(argv);
-	l = round_up_to_quad(o->len);
-	if (argv->iov_len < l)
-		return -1;
-	o->data = argv->iov_base;
-	argv->iov_base += l;
-	argv->iov_len -= l;
-	return 0;
-}
-
 static inline int
 svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)
 {
@@ -1553,27 +1536,91 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
 #endif /* CONFIG_PROC_FS */
 
 /*
- * Accept an rpcsec packet.
- * If context establishment, punt to user space
- * If data exchange, verify/decrypt
- * If context destruction, handle here
- * In the context establishment and destruction case we encode
- * response here and return SVC_COMPLETE.
+ * The Call's credential body should contain a struct rpc_gss_cred_t.
+ *
+ * RFC 2203 Section 5
+ *
+ *	struct rpc_gss_cred_t {
+ *		union switch (unsigned int version) {
+ *		case RPCSEC_GSS_VERS_1:
+ *			struct {
+ *				rpc_gss_proc_t gss_proc;
+ *				unsigned int seq_num;
+ *				rpc_gss_service_t service;
+ *				opaque handle<>;
+ *			} rpc_gss_cred_vers_1_t;
+ *		}
+ *	};
+ */
+static bool
+svcauth_gss_decode_credbody(struct xdr_stream *xdr,
+			    struct rpc_gss_wire_cred *gc,
+			    __be32 **rpcstart)
+{
+	ssize_t handle_len;
+	u32 body_len;
+	__be32 *p;
+
+	p = xdr_inline_decode(xdr, XDR_UNIT);
+	if (!p)
+		return false;
+	/*
+	 * start of rpc packet is 7 u32's back from here:
+	 * xid direction rpcversion prog vers proc flavour
+	 */
+	*rpcstart = p - 7;
+	body_len = be32_to_cpup(p);
+	if (body_len > RPC_MAX_AUTH_SIZE)
+		return false;
+
+	/* struct rpc_gss_cred_t */
+	if (xdr_stream_decode_u32(xdr, &gc->gc_v) < 0)
+		return false;
+	if (xdr_stream_decode_u32(xdr, &gc->gc_proc) < 0)
+		return false;
+	if (xdr_stream_decode_u32(xdr, &gc->gc_seq) < 0)
+		return false;
+	if (xdr_stream_decode_u32(xdr, &gc->gc_svc) < 0)
+		return false;
+	handle_len = xdr_stream_decode_opaque_inline(xdr,
+						     (void **)&gc->gc_ctx.data,
+						     body_len);
+	if (handle_len < 0)
+		return false;
+	if (body_len != XDR_UNIT * 5 + xdr_align_size(handle_len))
+		return false;
+
+	gc->gc_ctx.len = handle_len;
+	return true;
+}
+
+/**
+ * svcauth_gss_accept - Decode and validate incoming RPC_AUTH_GSS credential
+ * @rqstp: RPC transaction
+ *
+ * Return values:
+ *   %SVC_OK: Success
+ *   %SVC_COMPLETE: GSS context lifetime event
+ *   %SVC_DENIED: Credential or verifier is not valid
+ *   %SVC_GARBAGE: Failed to decode credential or verifier
+ *   %SVC_CLOSE: Temporary failure
+ *
+ * The rqstp->rq_auth_stat field is also set (see RFCs 2203 and 5531).
  */
 static int
 svcauth_gss_accept(struct svc_rqst *rqstp)
 {
-	struct kvec	*argv = &rqstp->rq_arg.head[0];
 	struct kvec	*resv = &rqstp->rq_res.head[0];
-	u32		crlen;
 	struct gss_svc_data *svcdata = rqstp->rq_auth_data;
+	__be32		*rpcstart;
 	struct rpc_gss_wire_cred *gc;
 	struct rsc	*rsci = NULL;
-	__be32		*rpcstart;
 	__be32		*reject_stat = resv->iov_base + resv->iov_len;
 	int		ret;
 	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
 
+	svcxdr_init_decode(rqstp);
+
 	rqstp->rq_auth_stat = rpc_autherr_badcred;
 	if (!svcdata)
 		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
@@ -1584,31 +1631,10 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 	svcdata->rsci = NULL;
 	gc = &svcdata->clcred;
 
-	/* start of rpc packet is 7 u32's back from here:
-	 * xid direction rpcversion prog vers proc flavour
-	 */
-	rpcstart = argv->iov_base;
-	rpcstart -= 7;
-
-	/* credential is:
-	 *   version(==1), proc(0,1,2,3), seq, service (1,2,3), handle
-	 * at least 5 u32s, and is preceded by length, so that makes 6.
-	 */
-
-	if (argv->iov_len < 5 * 4)
+	if (!svcauth_gss_decode_credbody(&rqstp->rq_arg_stream, gc, &rpcstart))
 		goto auth_err;
-	crlen = svc_getnl(argv);
-	if (svc_getnl(argv) != RPC_GSS_VERSION)
+	if (gc->gc_v != RPC_GSS_VERSION)
 		goto auth_err;
-	gc->gc_proc = svc_getnl(argv);
-	gc->gc_seq = svc_getnl(argv);
-	gc->gc_svc = svc_getnl(argv);
-	if (svc_safe_getnetobj(argv, &gc->gc_ctx))
-		goto auth_err;
-	if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4)
-		goto auth_err;
-
-	svcxdr_init_decode(rqstp);
 
 	switch (gc->gc_proc) {
 	case RPC_GSS_PROC_INIT:
@@ -1621,7 +1647,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 			goto auth_err;
 		fallthrough;
 	case RPC_GSS_PROC_DATA:
-		/* Look up the context, and check the verifier: */
 		rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
 		rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
 		if (!rsci)



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 20/25] SUNRPC: Hoist init_decode out of svc_authenticate()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (18 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 19/25] SUNRPC: Convert the svcauth_gss_accept() pre-amble to use xdr_stream Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 21/25] SUNRPC: Re-order construction of the first reply fields Chuck Lever
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Now that each ->accept method has been converted to use xdr_stream,
the svcxdr_init_decode() calls can be hoisted back up into the
generic RPC server code.

The dprintk in svc_authenticate() is removed, since
trace_svc_authenticate() reports the same information.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/auth_gss/svcauth_gss.c |    2 --
 net/sunrpc/svc.c                  |    1 +
 net/sunrpc/svcauth.c              |   13 ++++++++-----
 net/sunrpc/svcauth_unix.c         |    6 ------
 4 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 557de28127fe..2e603358fae1 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1619,8 +1619,6 @@ svcauth_gss_accept(struct svc_rqst *rqstp)
 	int		ret;
 	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
 
-	svcxdr_init_decode(rqstp);
-
 	rqstp->rq_auth_stat = rpc_autherr_badcred;
 	if (!svcdata)
 		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 85f0c3cfc877..e4f8a177763e 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1276,6 +1276,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	 * We do this before anything else in order to get a decent
 	 * auth verifier.
 	 */
+	svcxdr_init_decode(rqstp);
 	auth_res = svc_authenticate(rqstp);
 	/* Also give the program a chance to reject this call: */
 	if (auth_res == SVC_OK && progp)
diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c
index e72ba2f13f6c..67d8245a08af 100644
--- a/net/sunrpc/svcauth.c
+++ b/net/sunrpc/svcauth.c
@@ -63,14 +63,17 @@ svc_put_auth_ops(struct auth_ops *aops)
 int
 svc_authenticate(struct svc_rqst *rqstp)
 {
-	rpc_authflavor_t	flavor;
-	struct auth_ops		*aops;
+	struct auth_ops *aops;
+	u32 flavor;
 
 	rqstp->rq_auth_stat = rpc_auth_ok;
 
-	flavor = svc_getnl(&rqstp->rq_arg.head[0]);
-
-	dprintk("svc: svc_authenticate (%d)\n", flavor);
+	/*
+	 * Decode the Call credential's flavor field. The credential's
+	 * body field is decoded in the chosen ->accept method below.
+	 */
+	if (xdr_stream_decode_u32(&rqstp->rq_arg_stream, &flavor) < 0)
+		return SVC_GARBAGE;
 
 	aops = svc_get_auth_ops(flavor);
 	if (aops == NULL) {
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 168e12137754..f09a148aa0c1 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -750,8 +750,6 @@ svcauth_null_accept(struct svc_rqst *rqstp)
 	u32 flavor, len;
 	void *body;
 
-	svcxdr_init_decode(rqstp);
-
 	/* Length of Call's credential body field: */
 	if (xdr_stream_decode_u32(xdr, &len) < 0)
 		return SVC_GARBAGE;
@@ -828,8 +826,6 @@ svcauth_tls_accept(struct svc_rqst *rqstp)
 	u32 flavor, len;
 	void *body;
 
-	svcxdr_init_decode(rqstp);
-
 	/* Length of Call's credential body field: */
 	if (xdr_stream_decode_u32(xdr, &len) < 0)
 		return SVC_GARBAGE;
@@ -905,8 +901,6 @@ svcauth_unix_accept(struct svc_rqst *rqstp)
 	void *body;
 	__be32 *p;
 
-	svcxdr_init_decode(rqstp);
-
 	/*
 	 * This implementation ignores the length of the Call's
 	 * credential body field and the timestamp and machinename



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 21/25] SUNRPC: Re-order construction of the first reply fields
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (19 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 20/25] SUNRPC: Hoist init_decode out of svc_authenticate() Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 22/25] SUNRPC: Eliminate unneeded variable Chuck Lever
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Clean up: Group these together for legibility.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svc.c |   10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index e4f8a177763e..e473c19b2f8d 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1248,19 +1248,15 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	__set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
 	__clear_bit(RQ_DROPME, &rqstp->rq_flags);
 
+	/* Construct the first words of the reply: */
 	svc_putu32(resv, rqstp->rq_xid);
+	svc_putnl(resv, RPC_REPLY);
+	reply_statp = resv->iov_base + resv->iov_len;
 
 	vers = svc_getnl(argv);
-
-	/* First words of reply: */
-	svc_putnl(resv, 1);		/* REPLY */
-
 	if (vers != 2)		/* RPC version number */
 		goto err_bad_rpc;
 
-	/* Save position in case we later decide to reject: */
-	reply_statp = resv->iov_base + resv->iov_len;
-
 	svc_putnl(resv, 0);		/* ACCEPT */
 
 	rqstp->rq_prog = prog = svc_getnl(argv);	/* program number */



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 22/25] SUNRPC: Eliminate unneeded variable
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (20 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 21/25] SUNRPC: Re-order construction of the first reply fields Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 23/25] SUNRPC: Decode most of RPC header with xdr_stream Chuck Lever
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Clean up: Saving the RPC program number in two places is
unnecessary.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svc.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index e473c19b2f8d..3c1574f362fe 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1232,7 +1232,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	struct svc_serv		*serv = rqstp->rq_server;
 	struct svc_process_info process;
 	__be32			*statp;
-	u32			prog, vers;
+	u32			vers;
 	__be32			rpc_stat;
 	int			auth_res, rc;
 	__be32			*reply_statp;
@@ -1259,12 +1259,12 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 
 	svc_putnl(resv, 0);		/* ACCEPT */
 
-	rqstp->rq_prog = prog = svc_getnl(argv);	/* program number */
+	rqstp->rq_prog = svc_getnl(argv);	/* program number */
 	rqstp->rq_vers = svc_getnl(argv);	/* version number */
 	rqstp->rq_proc = svc_getnl(argv);	/* procedure number */
 
 	for (progp = serv->sv_program; progp; progp = progp->pg_next)
-		if (prog == progp->pg_prog)
+		if (rqstp->rq_prog == progp->pg_prog)
 			break;
 
 	/*
@@ -1389,7 +1389,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	goto sendit;
 
 err_bad_prog:
-	dprintk("svc: unknown program %d\n", prog);
+	dprintk("svc: unknown program %d\n", rqstp->rq_prog);
 	serv->sv_stats->rpcbadfmt++;
 	svc_putnl(resv, RPC_PROG_UNAVAIL);
 	goto sendit;



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 23/25] SUNRPC: Decode most of RPC header with xdr_stream
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (21 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 22/25] SUNRPC: Eliminate unneeded variable Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:07 ` [PATCH v1 24/25] SUNRPC: Remove svc_process_common's argv parameter Chuck Lever
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Done as part of hardening the server-side RPC header decoding path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svc.c |   20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 3c1574f362fe..f292b898f200 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1231,17 +1231,13 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	const struct svc_procedure *procp = NULL;
 	struct svc_serv		*serv = rqstp->rq_server;
 	struct svc_process_info process;
-	__be32			*statp;
-	u32			vers;
+	__be32			*p, *statp;
 	__be32			rpc_stat;
 	int			auth_res, rc;
 	__be32			*reply_statp;
 
 	rpc_stat = rpc_success;
 
-	if (argv->iov_len < 6*4)
-		goto err_short_len;
-
 	/* Will be turned off by GSS integrity and privacy services */
 	__set_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
 	/* Will be turned off only when NFSv4 Sessions are used */
@@ -1253,15 +1249,18 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	svc_putnl(resv, RPC_REPLY);
 	reply_statp = resv->iov_base + resv->iov_len;
 
-	vers = svc_getnl(argv);
-	if (vers != 2)		/* RPC version number */
+	svcxdr_init_decode(rqstp);
+	p = xdr_inline_decode(&rqstp->rq_arg_stream, XDR_UNIT * 4);
+	if (unlikely(!p))
+		goto err_short_len;
+	if (*p++ != cpu_to_be32(RPC_VERSION))
 		goto err_bad_rpc;
 
 	svc_putnl(resv, 0);		/* ACCEPT */
 
-	rqstp->rq_prog = svc_getnl(argv);	/* program number */
-	rqstp->rq_vers = svc_getnl(argv);	/* version number */
-	rqstp->rq_proc = svc_getnl(argv);	/* procedure number */
+	rqstp->rq_prog = be32_to_cpup(p++);
+	rqstp->rq_vers = be32_to_cpup(p++);
+	rqstp->rq_proc = be32_to_cpup(p);
 
 	for (progp = serv->sv_program; progp; progp = progp->pg_next)
 		if (rqstp->rq_prog == progp->pg_prog)
@@ -1272,7 +1271,6 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	 * We do this before anything else in order to get a decent
 	 * auth verifier.
 	 */
-	svcxdr_init_decode(rqstp);
 	auth_res = svc_authenticate(rqstp);
 	/* Also give the program a chance to reject this call: */
 	if (auth_res == SVC_OK && progp)



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 24/25] SUNRPC: Remove svc_process_common's argv parameter
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (22 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 23/25] SUNRPC: Decode most of RPC header with xdr_stream Chuck Lever
@ 2023-01-02 17:07 ` Chuck Lever
  2023-01-02 17:08 ` [PATCH v1 25/25] SUNRPC: Hoist svcxdr_init_decode() into svc_process() Chuck Lever
  2023-01-03 14:52 ` [PATCH v1 00/25] Server-side RPC call header parsing overhaul Jeff Layton
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:07 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Clean up: With xdr_stream decoding, the @argv parameter is no longer
used.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svc.c |   10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index f292b898f200..571151a17e87 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1225,7 +1225,7 @@ EXPORT_SYMBOL_GPL(svc_generic_init_request);
  * Common routine for processing the RPC request.
  */
 static int
-svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
+svc_process_common(struct svc_rqst *rqstp, struct kvec *resv)
 {
 	struct svc_program	*progp;
 	const struct svc_procedure *procp = NULL;
@@ -1363,8 +1363,8 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 	return 0;
 
 err_short_len:
-	svc_printk(rqstp, "short len %zd, dropping request\n",
-			argv->iov_len);
+	svc_printk(rqstp, "short len %u, dropping request\n",
+		   rqstp->rq_arg.len);
 	goto close_xprt;
 
 err_bad_rpc:
@@ -1453,7 +1453,7 @@ svc_process(struct svc_rqst *rqstp)
 	dir = svc_getu32(argv);
 	if (dir != rpc_call)
 		goto out_baddir;
-	if (!svc_process_common(rqstp, argv, resv))
+	if (!svc_process_common(rqstp, resv))
 		goto out_drop;
 	return svc_send(rqstp);
 
@@ -1519,7 +1519,7 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
 	svc_getnl(argv);	/* CALLDIR */
 
 	/* Parse and execute the bc call */
-	proc_error = svc_process_common(rqstp, argv, resv);
+	proc_error = svc_process_common(rqstp, resv);
 
 	atomic_dec(&req->rq_xprt->bc_slot_count);
 	if (!proc_error) {



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* [PATCH v1 25/25] SUNRPC: Hoist svcxdr_init_decode() into svc_process()
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (23 preceding siblings ...)
  2023-01-02 17:07 ` [PATCH v1 24/25] SUNRPC: Remove svc_process_common's argv parameter Chuck Lever
@ 2023-01-02 17:08 ` Chuck Lever
  2023-01-03 14:52 ` [PATCH v1 00/25] Server-side RPC call header parsing overhaul Jeff Layton
  25 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever @ 2023-01-02 17:08 UTC (permalink / raw)
  To: linux-nfs

From: Chuck Lever <chuck.lever@oracle.com>

Now the entire RPC Call header parsing path is handled via struct
xdr_stream-based decoders.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svc.c      |   28 +++++++++++++++++-----------
 net/sunrpc/svc_xprt.c |    1 -
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 571151a17e87..94f7efca60fc 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1249,7 +1249,6 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *resv)
 	svc_putnl(resv, RPC_REPLY);
 	reply_statp = resv->iov_base + resv->iov_len;
 
-	svcxdr_init_decode(rqstp);
 	p = xdr_inline_decode(&rqstp->rq_arg_stream, XDR_UNIT * 4);
 	if (unlikely(!p))
 		goto err_short_len;
@@ -1425,9 +1424,8 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *resv)
 int
 svc_process(struct svc_rqst *rqstp)
 {
-	struct kvec		*argv = &rqstp->rq_arg.head[0];
 	struct kvec		*resv = &rqstp->rq_res.head[0];
-	__be32			dir;
+	__be32 *p;
 
 #if IS_ENABLED(CONFIG_FAIL_SUNRPC)
 	if (!fail_sunrpc.ignore_server_disconnect &&
@@ -1450,16 +1448,21 @@ svc_process(struct svc_rqst *rqstp)
 	rqstp->rq_res.tail[0].iov_base = NULL;
 	rqstp->rq_res.tail[0].iov_len = 0;
 
-	dir = svc_getu32(argv);
-	if (dir != rpc_call)
+	svcxdr_init_decode(rqstp);
+	p = xdr_inline_decode(&rqstp->rq_arg_stream, XDR_UNIT * 2);
+	if (unlikely(!p))
+		goto out_drop;
+	rqstp->rq_xid = *p++;
+	if (unlikely(*p != rpc_call))
 		goto out_baddir;
+
 	if (!svc_process_common(rqstp, resv))
 		goto out_drop;
 	return svc_send(rqstp);
 
 out_baddir:
 	svc_printk(rqstp, "bad direction 0x%08x, dropping request\n",
-		   be32_to_cpu(dir));
+		   be32_to_cpu(*p));
 	rqstp->rq_server->sv_stats->rpcbadfmt++;
 out_drop:
 	svc_drop(rqstp);
@@ -1476,7 +1479,6 @@ int
 bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
 	       struct svc_rqst *rqstp)
 {
-	struct kvec	*argv = &rqstp->rq_arg.head[0];
 	struct kvec	*resv = &rqstp->rq_res.head[0];
 	struct rpc_task *task;
 	int proc_error;
@@ -1511,12 +1513,16 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
 	/* reset result send buffer "put" position */
 	resv->iov_len = 0;
 
+	svcxdr_init_decode(rqstp);
+
 	/*
-	 * Skip the next two words because they've already been
-	 * processed in the transport
+	 * Skip the XID and calldir fields because they've already
+	 * been processed by the caller.
 	 */
-	svc_getu32(argv);	/* XID */
-	svc_getnl(argv);	/* CALLDIR */
+	if (!xdr_inline_decode(&rqstp->rq_arg_stream, XDR_UNIT * 2)) {
+		error = -EINVAL;
+		goto out;
+	}
 
 	/* Parse and execute the bc call */
 	proc_error = svc_process_common(rqstp, resv);
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 2106003645a7..b4697984b4b2 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -890,7 +890,6 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
 
 	xprt->xpt_ops->xpo_secure_port(rqstp);
 	rqstp->rq_chandle.defer = svc_defer;
-	rqstp->rq_xid = svc_getu32(&rqstp->rq_arg.head[0]);
 
 	if (serv->sv_stats)
 		serv->sv_stats->netcnt++;



^ permalink raw reply related	[flat|nested] 28+ messages in thread

* Re: [PATCH v1 00/25] Server-side RPC call header parsing overhaul
  2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
                   ` (24 preceding siblings ...)
  2023-01-02 17:08 ` [PATCH v1 25/25] SUNRPC: Hoist svcxdr_init_decode() into svc_process() Chuck Lever
@ 2023-01-03 14:52 ` Jeff Layton
  2023-01-03 16:33   ` Chuck Lever III
  25 siblings, 1 reply; 28+ messages in thread
From: Jeff Layton @ 2023-01-03 14:52 UTC (permalink / raw)
  To: Chuck Lever, linux-nfs

On Mon, 2023-01-02 at 12:05 -0500, Chuck Lever wrote:
> Happy new year, campers.
> 
> The following series has been percolating for quite a while, thanks
> to the extended 6.1-rc cycle. I'd like to get this work reviewed
> for possible inclusion in v6.3, so I'm starting early.
> 
> The purpose of this series is to replace the svc_get* macros in the
> Linux kernel server's RPC call header parsing code with xdr_stream
> helpers. I've measured no change in CPU utilization after the
> overhaul; svc_recv() and friends remain the highest CPU consumers by
> an order of magnitude.
> 
> Memory safety: Buffer bounds checking after decoding each XDR item
> is more memory-safe than the current decoding mechanism. Subsequent
> memory safety improvements to the xdr_stream helpers will benefit
> all who use them.
> 
> Audit friendliness: The new code has lots of comments and other
> clean-up to help align it with the relevant RPC specifications. The
> use of common helpers also makes the decoders easier to audit.
> 
> I've split the full series in half to make it easier to review. The
> patches posted here handle RPC call header decoding. Remaining
> patches, to be posted later, deal with RPC reply header encoding.
> 
> Yes, there are a lot of patches, but they are each small, easily
> chewed mechanical changes.
> 
> ---
> 
> Chuck Lever (25):
>       SUNRPC: Push svcxdr_init_decode() into svc_process_common()
>       SUNRPC: Move svcxdr_init_decode() into ->accept methods
>       SUNRPC: Add an XDR decoding helper for struct opaque_auth
>       SUNRPC: Convert svcauth_null_accept() to use xdr_stream
>       SUNRPC: Convert svcauth_unix_accept() to use xdr_stream
>       SUNRPC: Convert svcauth_tls_accept() to use xdr_stream
>       SUNRPC: Move the server-side GSS upcall to a noinline function
>       SUNRPC: Hoist common verifier decoding code into svcauth_gss_proc_init()
>       SUNRPC: Remove gss_read_common_verf()
>       SUNRPC: Remove gss_read_verf()
>       SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream
>       SUNRPC: Replace read_u32_from_xdr_buf() with existing XDR helper
>       SUNRPC: Rename automatic variables in unwrap_integ_data()
>       SUNRPC: Convert unwrap_integ_data() to use xdr_stream
>       SUNRPC: Rename automatic variables in unwrap_priv_data()
>       SUNRPC: Convert unwrap_priv_data() to use xdr_stream
>       SUNRPC: Convert gss_verify_header() to use xdr_stream
>       SUNRPC: Clean up svcauth_gss_accept's NULL procedure check
>       SUNRPC: Convert the svcauth_gss_accept() pre-amble to use xdr_stream
>       SUNRPC: Hoist init_decode out of svc_authenticate()
>       SUNRPC: Re-order construction of the first reply fields
>       SUNRPC: Eliminate unneeded variable
>       SUNRPC: Decode most of RPC header with xdr_stream
>       SUNRPC: Remove svc_process_common's argv parameter
>       SUNRPC: Hoist svcxdr_init_decode() into svc_process()
> 
> 
>  fs/lockd/svc.c                    |   1 -
>  fs/nfs/callback_xdr.c             |   1 -
>  fs/nfsd/nfssvc.c                  |   1 -
>  include/linux/sunrpc/msg_prot.h   |   5 +
>  include/linux/sunrpc/xdr.h        |   5 +-
>  net/sunrpc/auth_gss/svcauth_gss.c | 512 ++++++++++++++++--------------
>  net/sunrpc/svc.c                  |  69 ++--
>  net/sunrpc/svc_xprt.c             |   1 -
>  net/sunrpc/svcauth.c              |  13 +-
>  net/sunrpc/svcauth_unix.c         | 132 +++++---
>  net/sunrpc/xdr.c                  |  50 ++-
>  11 files changed, 468 insertions(+), 322 deletions(-)
> 
> --
> Chuck Lever
> 

I went through the whole set and it's all a mostly logical set of
methodical changes. You can add:

Reviewed-by: Jeff Layton <jlayton@kernel.org>

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: [PATCH v1 00/25] Server-side RPC call header parsing overhaul
  2023-01-03 14:52 ` [PATCH v1 00/25] Server-side RPC call header parsing overhaul Jeff Layton
@ 2023-01-03 16:33   ` Chuck Lever III
  0 siblings, 0 replies; 28+ messages in thread
From: Chuck Lever III @ 2023-01-03 16:33 UTC (permalink / raw)
  To: Jeff Layton; +Cc: Linux NFS Mailing List



> On Jan 3, 2023, at 9:52 AM, Jeff Layton <jlayton@kernel.org> wrote:
> 
> On Mon, 2023-01-02 at 12:05 -0500, Chuck Lever wrote:
>> Happy new year, campers.
>> 
>> The following series has been percolating for quite a while, thanks
>> to the extended 6.1-rc cycle. I'd like to get this work reviewed
>> for possible inclusion in v6.3, so I'm starting early.
>> 
>> The purpose of this series is to replace the svc_get* macros in the
>> Linux kernel server's RPC call header parsing code with xdr_stream
>> helpers. I've measured no change in CPU utilization after the
>> overhaul; svc_recv() and friends remain the highest CPU consumers by
>> an order of magnitude.
>> 
>> Memory safety: Buffer bounds checking after decoding each XDR item
>> is more memory-safe than the current decoding mechanism. Subsequent
>> memory safety improvements to the xdr_stream helpers will benefit
>> all who use them.
>> 
>> Audit friendliness: The new code has lots of comments and other
>> clean-up to help align it with the relevant RPC specifications. The
>> use of common helpers also makes the decoders easier to audit.
>> 
>> I've split the full series in half to make it easier to review. The
>> patches posted here handle RPC call header decoding. Remaining
>> patches, to be posted later, deal with RPC reply header encoding.
>> 
>> Yes, there are a lot of patches, but they are each small, easily
>> chewed mechanical changes.
>> 
>> ---
>> 
>> Chuck Lever (25):
>>      SUNRPC: Push svcxdr_init_decode() into svc_process_common()
>>      SUNRPC: Move svcxdr_init_decode() into ->accept methods
>>      SUNRPC: Add an XDR decoding helper for struct opaque_auth
>>      SUNRPC: Convert svcauth_null_accept() to use xdr_stream
>>      SUNRPC: Convert svcauth_unix_accept() to use xdr_stream
>>      SUNRPC: Convert svcauth_tls_accept() to use xdr_stream
>>      SUNRPC: Move the server-side GSS upcall to a noinline function
>>      SUNRPC: Hoist common verifier decoding code into svcauth_gss_proc_init()
>>      SUNRPC: Remove gss_read_common_verf()
>>      SUNRPC: Remove gss_read_verf()
>>      SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream
>>      SUNRPC: Replace read_u32_from_xdr_buf() with existing XDR helper
>>      SUNRPC: Rename automatic variables in unwrap_integ_data()
>>      SUNRPC: Convert unwrap_integ_data() to use xdr_stream
>>      SUNRPC: Rename automatic variables in unwrap_priv_data()
>>      SUNRPC: Convert unwrap_priv_data() to use xdr_stream
>>      SUNRPC: Convert gss_verify_header() to use xdr_stream
>>      SUNRPC: Clean up svcauth_gss_accept's NULL procedure check
>>      SUNRPC: Convert the svcauth_gss_accept() pre-amble to use xdr_stream
>>      SUNRPC: Hoist init_decode out of svc_authenticate()
>>      SUNRPC: Re-order construction of the first reply fields
>>      SUNRPC: Eliminate unneeded variable
>>      SUNRPC: Decode most of RPC header with xdr_stream
>>      SUNRPC: Remove svc_process_common's argv parameter
>>      SUNRPC: Hoist svcxdr_init_decode() into svc_process()
>> 
>> 
>> fs/lockd/svc.c                    |   1 -
>> fs/nfs/callback_xdr.c             |   1 -
>> fs/nfsd/nfssvc.c                  |   1 -
>> include/linux/sunrpc/msg_prot.h   |   5 +
>> include/linux/sunrpc/xdr.h        |   5 +-
>> net/sunrpc/auth_gss/svcauth_gss.c | 512 ++++++++++++++++--------------
>> net/sunrpc/svc.c                  |  69 ++--
>> net/sunrpc/svc_xprt.c             |   1 -
>> net/sunrpc/svcauth.c              |  13 +-
>> net/sunrpc/svcauth_unix.c         | 132 +++++---
>> net/sunrpc/xdr.c                  |  50 ++-
>> 11 files changed, 468 insertions(+), 322 deletions(-)
>> 
>> --
>> Chuck Lever
>> 
> 
> I went through the whole set and it's all a mostly logical set of
> methodical changes. You can add:
> 
> Reviewed-by: Jeff Layton <jlayton@kernel.org>

Thanks Jeff! I've applied these and pushed them to nfsd's for-next,
but comments are still open, and testing results are welcome.


--
Chuck Lever




^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2023-01-03 16:33 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-02 17:05 [PATCH v1 00/25] Server-side RPC call header parsing overhaul Chuck Lever
2023-01-02 17:05 ` [PATCH v1 01/25] SUNRPC: Push svcxdr_init_decode() into svc_process_common() Chuck Lever
2023-01-02 17:05 ` [PATCH v1 02/25] SUNRPC: Move svcxdr_init_decode() into ->accept methods Chuck Lever
2023-01-02 17:05 ` [PATCH v1 03/25] SUNRPC: Add an XDR decoding helper for struct opaque_auth Chuck Lever
2023-01-02 17:05 ` [PATCH v1 04/25] SUNRPC: Convert svcauth_null_accept() to use xdr_stream Chuck Lever
2023-01-02 17:05 ` [PATCH v1 05/25] SUNRPC: Convert svcauth_unix_accept() " Chuck Lever
2023-01-02 17:06 ` [PATCH v1 06/25] SUNRPC: Convert svcauth_tls_accept() " Chuck Lever
2023-01-02 17:06 ` [PATCH v1 07/25] SUNRPC: Move the server-side GSS upcall to a noinline function Chuck Lever
2023-01-02 17:06 ` [PATCH v1 08/25] SUNRPC: Hoist common verifier decoding code into svcauth_gss_proc_init() Chuck Lever
2023-01-02 17:06 ` [PATCH v1 09/25] SUNRPC: Remove gss_read_common_verf() Chuck Lever
2023-01-02 17:06 ` [PATCH v1 10/25] SUNRPC: Remove gss_read_verf() Chuck Lever
2023-01-02 17:06 ` [PATCH v1 11/25] SUNRPC: Convert server-side GSS upcall helpers to use xdr_stream Chuck Lever
2023-01-02 17:06 ` [PATCH v1 12/25] SUNRPC: Replace read_u32_from_xdr_buf() with existing XDR helper Chuck Lever
2023-01-02 17:06 ` [PATCH v1 13/25] SUNRPC: Rename automatic variables in unwrap_integ_data() Chuck Lever
2023-01-02 17:06 ` [PATCH v1 14/25] SUNRPC: Convert unwrap_integ_data() to use xdr_stream Chuck Lever
2023-01-02 17:07 ` [PATCH v1 15/25] SUNRPC: Rename automatic variables in unwrap_priv_data() Chuck Lever
2023-01-02 17:07 ` [PATCH v1 16/25] SUNRPC: Convert unwrap_priv_data() to use xdr_stream Chuck Lever
2023-01-02 17:07 ` [PATCH v1 17/25] SUNRPC: Convert gss_verify_header() " Chuck Lever
2023-01-02 17:07 ` [PATCH v1 18/25] SUNRPC: Clean up svcauth_gss_accept's NULL procedure check Chuck Lever
2023-01-02 17:07 ` [PATCH v1 19/25] SUNRPC: Convert the svcauth_gss_accept() pre-amble to use xdr_stream Chuck Lever
2023-01-02 17:07 ` [PATCH v1 20/25] SUNRPC: Hoist init_decode out of svc_authenticate() Chuck Lever
2023-01-02 17:07 ` [PATCH v1 21/25] SUNRPC: Re-order construction of the first reply fields Chuck Lever
2023-01-02 17:07 ` [PATCH v1 22/25] SUNRPC: Eliminate unneeded variable Chuck Lever
2023-01-02 17:07 ` [PATCH v1 23/25] SUNRPC: Decode most of RPC header with xdr_stream Chuck Lever
2023-01-02 17:07 ` [PATCH v1 24/25] SUNRPC: Remove svc_process_common's argv parameter Chuck Lever
2023-01-02 17:08 ` [PATCH v1 25/25] SUNRPC: Hoist svcxdr_init_decode() into svc_process() Chuck Lever
2023-01-03 14:52 ` [PATCH v1 00/25] Server-side RPC call header parsing overhaul Jeff Layton
2023-01-03 16:33   ` Chuck Lever III

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).