]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net/handshake: Pass negative errno through handshake_complete()
authorChuck Lever <chuck.lever@oracle.com>
Mon, 25 May 2026 16:51:17 +0000 (12:51 -0400)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 28 May 2026 11:35:31 +0000 (13:35 +0200)
handshake_complete() declares status as unsigned int and
tls_handshake_done() negates that value (-status) before handing
it to the TLS consumer. Consumers match on negative errno
constants -- xs_tls_handshake_done() has

switch (status) {
case 0:
case -EACCES:
case -ETIMEDOUT:
lower_transport->xprt_err = status;
break;
default:
lower_transport->xprt_err = -EACCES;
}

so the API as designed expects callers to pass positive errno
values that the tlshd shim then negates.

Three internal callers in handshake_nl_accept_doit(), the
net-exit drain, and a kunit test follow kernel convention and
pass negative errnos -- -EIO, -ETIMEDOUT, -ETIMEDOUT. The
implicit conversion to unsigned int turns -ETIMEDOUT into
0xFFFFFF92; the subsequent -status in tls_handshake_done()
wraps back to 110, the consumer's switch falls through, and
the xprt reports -EACCES on what should be -ETIMEDOUT or -EIO.

Fix the API rather than the call sites. The natural kernel
convention is negative errno in, negative errno out. Change
handshake_complete() and hp_done to take int status, drop the
negation in tls_handshake_done(), and negate once in
handshake_nl_done_doit() where status arrives from the wire
as an unsigned netlink attribute. The three internal callers
were already correct under that convention and need no change.

At the same wire boundary, declare MAX_ERRNO as the netlink
policy upper bound for HANDSHAKE_A_DONE_STATUS. Attribute
validation rejects out-of-range values before
handshake_nl_done_doit() runs, and negating a bounded u32 there
stays within int range -- closing the UBSAN-visible signed-
integer overflow that an unconstrained u32 would invoke.

Fixes: 3b3009ea8abb ("net/handshake: Create a NETLINK service for handling handshake requests")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Reviewed-by: Hannes Reinecke <hare@kernel.org>
Link: https://patch.msgid.link/20260525-handshake-file-pin-v3-3-66c616906ead@oracle.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Documentation/netlink/specs/handshake.yaml
net/handshake/genl.c
net/handshake/genl.h
net/handshake/handshake-test.c
net/handshake/handshake.h
net/handshake/netlink.c
net/handshake/request.c
net/handshake/tlshd.c

index 95c3fade7a8d7b818a15198593f6a0f59bafb489..1024297b38513ac9f1554f9353327ea23cf4d90d 100644 (file)
@@ -12,6 +12,12 @@ protocol: genetlink
 doc: Netlink protocol to request a transport layer security handshake.
 
 definitions:
+  -
+    type: const
+    name: max-errno
+    value: 4095
+    header: linux/err.h
+    scope: kernel
   -
     type: enum
     name: handler-class
@@ -80,6 +86,8 @@ attribute-sets:
       -
         name: status
         type: u32
+        checks:
+          max: max-errno
       -
         name: sockfd
         type: s32
index 8706126094915dc5a99c38050fb5666be0ef5735..4b20cd9cdd0e096fea503c192d4fe4d6a144e88b 100644 (file)
@@ -10,6 +10,7 @@
 #include "genl.h"
 
 #include <uapi/linux/handshake.h>
+#include <linux/err.h>
 
 /* HANDSHAKE_CMD_ACCEPT - do */
 static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HANDLER_CLASS + 1] = {
@@ -18,7 +19,7 @@ static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HAN
 
 /* HANDSHAKE_CMD_DONE - do */
 static const struct nla_policy handshake_done_nl_policy[HANDSHAKE_A_DONE_REMOTE_AUTH + 1] = {
-       [HANDSHAKE_A_DONE_STATUS] = { .type = NLA_U32, },
+       [HANDSHAKE_A_DONE_STATUS] = NLA_POLICY_MAX(NLA_U32, MAX_ERRNO),
        [HANDSHAKE_A_DONE_SOCKFD] = { .type = NLA_S32, },
        [HANDSHAKE_A_DONE_REMOTE_AUTH] = { .type = NLA_U32, },
 };
index 8d3e18672dafcf4e1d20c3e621b0c9af970367a4..46b65f131669a66ecca9201f5f6469b21e0c0ab1 100644 (file)
@@ -11,6 +11,7 @@
 #include <net/genetlink.h>
 
 #include <uapi/linux/handshake.h>
+#include <linux/err.h>
 
 int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info);
 int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info);
index 55442b2f518afbc2a845b4108cbcabd27f8d17fc..df3948e807a0fd691fff71ec26f5dab7521a8c57 100644 (file)
@@ -25,7 +25,7 @@ static int test_accept_func(struct handshake_req *req, struct genl_info *info,
        return 0;
 }
 
-static void test_done_func(struct handshake_req *req, unsigned int status,
+static void test_done_func(struct handshake_req *req, int status,
                           struct genl_info *info)
 {
 }
index a48163765a7a1d425783519be9b754712c71a2d5..2289b0e274f40a833af8d9aed0dbcce4a8e0bd73 100644 (file)
@@ -57,7 +57,7 @@ struct handshake_proto {
        int                     (*hp_accept)(struct handshake_req *req,
                                             struct genl_info *info, int fd);
        void                    (*hp_done)(struct handshake_req *req,
-                                          unsigned int status,
+                                          int status,
                                           struct genl_info *info);
        void                    (*hp_destroy)(struct handshake_req *req);
 };
@@ -86,7 +86,7 @@ struct handshake_req *handshake_req_hash_lookup(struct sock *sk);
 struct handshake_req *handshake_req_next(struct handshake_net *hn, int class);
 int handshake_req_submit(struct socket *sock, struct handshake_req *req,
                         gfp_t flags);
-void handshake_complete(struct handshake_req *req, unsigned int status,
+void handshake_complete(struct handshake_req *req, int status,
                        struct genl_info *info);
 bool handshake_req_cancel(struct sock *sk);
 
index 97114ec8027a5a1594a3130d89da97ebfdf2429d..039344979de934a352af6e0ed483797cacb1011b 100644 (file)
@@ -160,7 +160,7 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info)
 
        status = -EIO;
        if (info->attrs[HANDSHAKE_A_DONE_STATUS])
-               status = nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]);
+               status = -(int)nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]);
 
        handshake_complete(req, status, info);
        sockfd_put(sock);
index 5d4a17f902d201cdcc356f41ab453c35ba79aad5..97f9f82399499474919cfe85efb1cdfe85e1dec9 100644 (file)
@@ -284,7 +284,7 @@ out_err:
 }
 EXPORT_SYMBOL(handshake_req_submit);
 
-void handshake_complete(struct handshake_req *req, unsigned int status,
+void handshake_complete(struct handshake_req *req, int status,
                        struct genl_info *info)
 {
        struct sock *sk = req->hr_sk;
index af294c6cc717313cb5deb66d2e4cd9ffa288284d..7567150c2a4f95ab6d91ed977ee699187aca4439 100644 (file)
@@ -93,7 +93,7 @@ static void tls_handshake_remote_peerids(struct tls_handshake_req *treq,
  *
  */
 static void tls_handshake_done(struct handshake_req *req,
-                              unsigned int status, struct genl_info *info)
+                              int status, struct genl_info *info)
 {
        struct tls_handshake_req *treq = handshake_req_private(req);
 
@@ -104,7 +104,7 @@ static void tls_handshake_done(struct handshake_req *req,
        if (!status)
                set_bit(HANDSHAKE_F_REQ_SESSION, &req->hr_flags);
 
-       treq->th_consumer_done(treq->th_consumer_data, -status,
+       treq->th_consumer_done(treq->th_consumer_data, status,
                               treq->th_peerid[0]);
 }