]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
sctp: Use sk_clone() in sctp_accept().
authorKuniyuki Iwashima <kuniyu@google.com>
Thu, 23 Oct 2025 23:16:54 +0000 (23:16 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 28 Oct 2025 01:04:58 +0000 (18:04 -0700)
sctp_accept() calls sctp_v[46]_create_accept_sk() to allocate a new
socket and calls sctp_sock_migrate() to copy fields from the parent
socket to the new socket.

sctp_v4_create_accept_sk() allocates sk by sk_alloc(), initialises
it by sock_init_data(), and copy a bunch of fields from the parent
socekt by sctp_copy_sock().

sctp_sock_migrate() calls sctp_copy_descendant() to copy most fields
in sctp_sock from the parent socket by memcpy().

These can be simply replaced by sk_clone().

Let's consolidate sctp_v[46]_create_accept_sk() to sctp_clone_sock()
with sk_clone().

We will reuse sctp_clone_sock() for sctp_do_peeloff() and then remove
sctp_copy_descendant().

Note that sock_reset_flag(newsk, SOCK_ZAPPED) is not copied to
sctp_clone_sock() as sctp does not use SOCK_ZAPPED at all.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Acked-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/20251023231751.4168390-6-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/af_inet.c
net/sctp/socket.c

index e8771faa5bbfdea8b24ee562804edd176f7b267b..77f6ae0fc231d8e0f45e78b95cb5bd83189965a4 100644 (file)
@@ -755,9 +755,7 @@ EXPORT_SYMBOL(inet_stream_connect);
 
 void __inet_accept(struct socket *sock, struct socket *newsock, struct sock *newsk)
 {
-       /* TODO: use sk_clone_lock() in SCTP and remove protocol checks */
-       if (mem_cgroup_sockets_enabled &&
-           (!IS_ENABLED(CONFIG_IP_SCTP) || sk_is_tcp(newsk))) {
+       if (mem_cgroup_sockets_enabled) {
                gfp_t gfp = GFP_KERNEL | __GFP_NOFAIL;
 
                mem_cgroup_sk_alloc(newsk);
index 70c75ac8da55dc74f35a02e9b39a32969db6b6ce..826f17747f176e7b7ddd95a935aa8d10a5f19f77 100644 (file)
@@ -4842,6 +4842,74 @@ static int sctp_disconnect(struct sock *sk, int flags)
        return 0;
 }
 
+static struct sock *sctp_clone_sock(struct sock *sk,
+                                   struct sctp_association *asoc,
+                                   enum sctp_socket_type type)
+{
+       struct sock *newsk = sk_clone(sk, GFP_KERNEL, false);
+       struct inet_sock *newinet;
+       struct sctp_sock *newsp;
+       int err = -ENOMEM;
+
+       if (!newsk)
+               return ERR_PTR(err);
+
+       /* sk_clone() sets refcnt to 2 */
+       sock_put(newsk);
+
+       newinet = inet_sk(newsk);
+       newsp = sctp_sk(newsk);
+
+       newsp->pf->to_sk_daddr(&asoc->peer.primary_addr, newsk);
+       newinet->inet_dport = htons(asoc->peer.port);
+
+       newsp->pf->copy_ip_options(sk, newsk);
+       atomic_set(&newinet->inet_id, get_random_u16());
+
+       inet_set_bit(MC_LOOP, newsk);
+       newinet->mc_ttl = 1;
+       newinet->mc_index = 0;
+       newinet->mc_list = NULL;
+
+#if IS_ENABLED(CONFIG_IPV6)
+       if (sk->sk_family == AF_INET6) {
+               struct ipv6_pinfo *newnp = inet6_sk(newsk);
+
+               newinet->pinet6 = &((struct sctp6_sock *)newsk)->inet6;
+               newinet->ipv6_fl_list = NULL;
+
+               memcpy(newnp, inet6_sk(sk), sizeof(struct ipv6_pinfo));
+               newnp->ipv6_mc_list = NULL;
+               newnp->ipv6_ac_list = NULL;
+       }
+#endif
+
+       skb_queue_head_init(&newsp->pd_lobby);
+
+       newsp->ep = sctp_endpoint_new(newsk, GFP_KERNEL);
+       if (!newsp->ep)
+               goto out_release;
+
+       SCTP_DBG_OBJCNT_INC(sock);
+       sk_sockets_allocated_inc(newsk);
+       sock_prot_inuse_add(sock_net(sk), newsk->sk_prot, 1);
+
+       err = sctp_sock_migrate(sk, newsk, asoc, type);
+       if (err)
+               goto out_release;
+
+       /* Set newsk security attributes from original sk and connection
+        * security attribute from asoc.
+        */
+       security_sctp_sk_clone(asoc, sk, newsk);
+
+       return newsk;
+
+out_release:
+       sk_common_release(newsk);
+       return ERR_PTR(err);
+}
+
 /* 4.1.4 accept() - TCP Style Syntax
  *
  * Applications use accept() call to remove an established SCTP
@@ -4851,18 +4919,13 @@ static int sctp_disconnect(struct sock *sk, int flags)
  */
 static struct sock *sctp_accept(struct sock *sk, struct proto_accept_arg *arg)
 {
-       struct sctp_sock *sp, *newsp;
-       struct sctp_endpoint *ep;
-       struct sock *newsk = NULL;
        struct sctp_association *asoc;
-       long timeo;
+       struct sock *newsk = NULL;
        int error = 0;
+       long timeo;
 
        lock_sock(sk);
 
-       sp = sctp_sk(sk);
-       ep = sp->ep;
-
        if (!sctp_style(sk, TCP)) {
                error = -EOPNOTSUPP;
                goto out;
@@ -4883,43 +4946,19 @@ static struct sock *sctp_accept(struct sock *sk, struct proto_accept_arg *arg)
        /* We treat the list of associations on the endpoint as the accept
         * queue and pick the first association on the list.
         */
-       asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);
-
-       newsk = sp->pf->create_accept_sk(sk, asoc, arg->kern);
-       if (!newsk) {
-               error = -ENOMEM;
-               goto out;
-       }
+       asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
+                         struct sctp_association, asocs);
 
-       newsp = sctp_sk(newsk);
-       newsp->ep = sctp_endpoint_new(newsk, GFP_KERNEL);
-       if (!newsp->ep) {
-               error = -ENOMEM;
-               goto out_release;
+       newsk = sctp_clone_sock(sk, asoc, SCTP_SOCKET_TCP);
+       if (IS_ERR(newsk)) {
+               error = PTR_ERR(newsk);
+               newsk = NULL;
        }
 
-       skb_queue_head_init(&newsp->pd_lobby);
-
-       sk_sockets_allocated_inc(newsk);
-       sock_prot_inuse_add(sock_net(sk), newsk->sk_prot, 1);
-       SCTP_DBG_OBJCNT_INC(sock);
-
-       /* Populate the fields of the newsk from the oldsk and migrate the
-        * asoc to the newsk.
-        */
-       error = sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);
-       if (error)
-               goto out_release;
-
 out:
        release_sock(sk);
        arg->err = error;
        return newsk;
-
-out_release:
-       sk_common_release(newsk);
-       newsk = NULL;
-       goto out;
 }
 
 /* The SCTP ioctl handler. */