Commit 16942cf4 authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Jakub Kicinski
Browse files

sctp: Use sk_clone() in sctp_accept().



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: default avatarKuniyuki Iwashima <kuniyu@google.com>
Acked-by: default avatarXin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/20251023231751.4168390-6-kuniyu@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 151b98d1
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -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);
+76 −37
Original line number Diff line number Diff line
@@ -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. */