Commit message: net ticp:fix a kernel-infoleak in __tipc_sendmsg()

struct tipc_socket_addr.ref has a 4-byte hole,and __tipc_getname() currently
copying it to user space,causing kernel-infoleak.

BUG: KMSAN: kernel-infoleak in instrument_copy_to_user include/linux/instrumented.h:121 [inline]
BUG: KMSAN: kernel-infoleak in instrument_copy_to_user include/linux/instrumented.h:121 [inline] lib/usercopy.c:33
BUG: KMSAN: kernel-infoleak in _copy_to_user+0x1c9/0x270 lib/usercopy.c:33 lib/usercopy.c:33
 instrument_copy_to_user include/linux/instrumented.h:121 [inline]
 instrument_copy_to_user include/linux/instrumented.h:121 [inline] lib/usercopy.c:33
 _copy_to_user+0x1c9/0x270 lib/usercopy.c:33 lib/usercopy.c:33
 copy_to_user include/linux/uaccess.h:209 [inline]
 copy_to_user include/linux/uaccess.h:209 [inline] net/socket.c:287
 move_addr_to_user+0x3f6/0x600 net/socket.c:287 net/socket.c:287
 __sys_getpeername+0x470/0x6b0 net/socket.c:1987 net/socket.c:1987
 __do_sys_getpeername net/socket.c:1997 [inline]
 __se_sys_getpeername net/socket.c:1994 [inline]
 __do_sys_getpeername net/socket.c:1997 [inline] net/socket.c:1994
 __se_sys_getpeername net/socket.c:1994 [inline] net/socket.c:1994
 __x64_sys_getpeername+0xda/0x120 net/socket.c:1994 net/socket.c:1994
 do_syscall_x64 arch/x86/entry/common.c:51 [inline]
 do_syscall_x64 arch/x86/entry/common.c:51 [inline] arch/x86/entry/common.c:82
 do_syscall_64+0x54/0xd0 arch/x86/entry/common.c:82 arch/x86/entry/common.c:82
 entry_SYSCALL_64_after_hwframe+0x44/0xae

Uninit was stored to memory at:
 tipc_getname+0x575/0x5e0 net/tipc/socket.c:757 net/tipc/socket.c:757
 __sys_getpeername+0x3b3/0x6b0 net/socket.c:1984 net/socket.c:1984
 __do_sys_getpeername net/socket.c:1997 [inline]
 __se_sys_getpeername net/socket.c:1994 [inline]
 __do_sys_getpeername net/socket.c:1997 [inline] net/socket.c:1994
 __se_sys_getpeername net/socket.c:1994 [inline] net/socket.c:1994
 __x64_sys_getpeername+0xda/0x120 net/socket.c:1994 net/socket.c:1994
 do_syscall_x64 arch/x86/entry/common.c:51 [inline]
 do_syscall_x64 arch/x86/entry/common.c:51 [inline] arch/x86/entry/common.c:82
 do_syscall_64+0x54/0xd0 arch/x86/entry/common.c:82 arch/x86/entry/common.c:82
 entry_SYSCALL_64_after_hwframe+0x44/0xae

Uninit was stored to memory at:
 msg_set_word net/tipc/msg.h:212 [inline]
 msg_set_destport net/tipc/msg.h:619 [inline]
 msg_set_word net/tipc/msg.h:212 [inline] net/tipc/socket.c:1486
 msg_set_destport net/tipc/msg.h:619 [inline] net/tipc/socket.c:1486
 __tipc_sendmsg+0x44fa/0x5890 net/tipc/socket.c:1486 net/tipc/socket.c:1486
 tipc_sendmsg+0xeb/0x140 net/tipc/socket.c:1402 net/tipc/socket.c:1402
 sock_sendmsg_nosec net/socket.c:704 [inline]
 sock_sendmsg net/socket.c:724 [inline]
 sock_sendmsg_nosec net/socket.c:704 [inline] net/socket.c:2409
 sock_sendmsg net/socket.c:724 [inline] net/socket.c:2409
 ____sys_sendmsg+0xe11/0x12c0 net/socket.c:2409 net/socket.c:2409
 ___sys_sendmsg net/socket.c:2463 [inline]
 ___sys_sendmsg net/socket.c:2463 [inline] net/socket.c:2492
 __sys_sendmsg+0x704/0x840 net/socket.c:2492 net/socket.c:2492
 __do_sys_sendmsg net/socket.c:2501 [inline]
 __se_sys_sendmsg net/socket.c:2499 [inline]
 __do_sys_sendmsg net/socket.c:2501 [inline] net/socket.c:2499
 __se_sys_sendmsg net/socket.c:2499 [inline] net/socket.c:2499
 __x64_sys_sendmsg+0xe2/0x120 net/socket.c:2499 net/socket.c:2499
 do_syscall_x64 arch/x86/entry/common.c:51 [inline]
 do_syscall_x64 arch/x86/entry/common.c:51 [inline] arch/x86/entry/common.c:82
 do_syscall_64+0x54/0xd0 arch/x86/entry/common.c:82 arch/x86/entry/common.c:82
 entry_SYSCALL_64_after_hwframe+0x44/0xae

Local variable skaddr created at:
 __tipc_sendmsg+0x2d0/0x5890 net/tipc/socket.c:1419 net/tipc/socket.c:1419
 tipc_sendmsg+0xeb/0x140 net/tipc/socket.c:1402 net/tipc/socket.c:1402

Bytes 4-7 of 16 are uninitialized
Memory access of size 16 starts at ffff888113753e00
Data copied to user address 0000000020000280

Reported-by: syzbot+cdbd40e0c3ca02cae3b7@syzkaller.appspotmail.com
Signed-off-by: Haimin Zhang <tcs_kernel@tencent.com>
Acked-by: Jon Maloy <jmaloy@redhat.com>
Link: https://lore.kernel.org/r/1640918123-14547-1-git-send-email-tcs.kernel@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
CVE: CVE-2022-0382
CWE: CWE-200
Vulnerability description: An information leak flaw was found due to uninitialized memory in the Linux kernel's TIPC protocol subsystem, in the way a user sends a TIPC datagram to one or more destinations. This flaw allows a local user to read some kernel memory. This issue is limited to no more than 7 bytes, and the user cannot control what is read. This flaw affects the Linux kernel versions prior to 5.17-rc1.

Project: linux
File: socket.c

static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen)
{
	struct sock *sk = sock->sk;
	struct net *net = sock_net(sk);
	struct tipc_sock *tsk = tipc_sk(sk);
	struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name;
	long timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
	struct list_head *clinks = &tsk->cong_links;
	bool syn = !tipc_sk_type_connectionless(sk);
	struct tipc_group *grp = tsk->group;
	struct tipc_msg *hdr = &tsk->phdr;
	struct tipc_socket_addr skaddr;
	struct sk_buff_head pkts;
	int atype, mtu, rc;

	if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE))
		return -EMSGSIZE;

	if (ua) {
		if (!tipc_uaddr_valid(ua, m->msg_namelen))
			return -EINVAL;
		atype = ua->addrtype;
	}

	/* If socket belongs to a communication group follow other paths */
	if (grp) {
		if (!ua)
			return tipc_send_group_bcast(sock, m, dlen, timeout);
		if (atype == TIPC_SERVICE_ADDR)
			return tipc_send_group_anycast(sock, m, dlen, timeout);
		if (atype == TIPC_SOCKET_ADDR)
			return tipc_send_group_unicast(sock, m, dlen, timeout);
		if (atype == TIPC_SERVICE_RANGE)
			return tipc_send_group_mcast(sock, m, dlen, timeout);
		return -EINVAL;
	}

	if (!ua) {
		ua = (struct tipc_uaddr *)&tsk->peer;
		if (!syn && ua->family != AF_TIPC)
			return -EDESTADDRREQ;
		atype = ua->addrtype;
	}

	if (unlikely(syn)) {
		if (sk->sk_state == TIPC_LISTEN)
			return -EPIPE;
		if (sk->sk_state != TIPC_OPEN)
			return -EISCONN;
		if (tsk->published)
			return -EOPNOTSUPP;
		if (atype == TIPC_SERVICE_ADDR)
			tsk->conn_addrtype = atype;
		msg_set_syn(hdr, 1);
	}

	/* Determine destination */
	if (atype == TIPC_SERVICE_RANGE) {
		return tipc_sendmcast(sock, ua, m, dlen, timeout);
	} else if (atype == TIPC_SERVICE_ADDR) {
		skaddr.node = ua->lookup_node;
		ua->scope = tipc_node2scope(skaddr.node);
		if (!tipc_nametbl_lookup_anycast(net, ua, &skaddr))
			return -EHOSTUNREACH;
	} else if (atype == TIPC_SOCKET_ADDR) {
		skaddr = ua->sk;
	} else {
		return -EINVAL;
	}

	/* Block or return if destination link is congested */
	rc = tipc_wait_for_cond(sock, &timeout,
				!tipc_dest_find(clinks, skaddr.node, 0));
	if (unlikely(rc))
		return rc;

	/* Finally build message header */
	msg_set_destnode(hdr, skaddr.node);
	msg_set_destport(hdr, skaddr.ref);
	if (atype == TIPC_SERVICE_ADDR) {
		msg_set_type(hdr, TIPC_NAMED_MSG);
		msg_set_hdr_sz(hdr, NAMED_H_SIZE);
		msg_set_nametype(hdr, ua->sa.type);
		msg_set_nameinst(hdr, ua->sa.instance);
		msg_set_lookup_scope(hdr, ua->scope);
	} else { /* TIPC_SOCKET_ADDR */
		msg_set_type(hdr, TIPC_DIRECT_MSG);
		msg_set_lookup_scope(hdr, 0);
		msg_set_hdr_sz(hdr, BASIC_H_SIZE);
	}

	/* Add message body */
	__skb_queue_head_init(&pkts);
	mtu = tipc_node_get_mtu(net, skaddr.node, tsk->portid, true);
	rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts);
	if (unlikely(rc != dlen))
		return rc;
	if (unlikely(syn && !tipc_msg_skb_clone(&pkts, &sk->sk_write_queue))) {
		__skb_queue_purge(&pkts);
		return -ENOMEM;
	}

	/* Send message */
	trace_tipc_sk_sendmsg(sk, skb_peek(&pkts), TIPC_DUMP_SK_SNDQ, " ");
	rc = tipc_node_xmit(net, &pkts, skaddr.node, tsk->portid);
	if (unlikely(rc == -ELINKCONG)) {
		tipc_dest_push(clinks, skaddr.node, 0);
		tsk->cong_link_cnt++;
		rc = 0;
	}

	if (unlikely(syn && !rc)) {
		tipc_set_sk_state(sk, TIPC_CONNECTING);
		if (dlen && timeout) {
			timeout = msecs_to_jiffies(timeout);
			tipc_wait_for_connect(sock, &timeout);
		}
	}

	return rc ? rc : dlen;
}

Project: linux
File: socket.c

static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen)
{
	struct sock *sk = sock->sk;
	struct net *net = sock_net(sk);
	struct tipc_sock *tsk = tipc_sk(sk);
	struct tipc_uaddr *ua = (struct tipc_uaddr *)m->msg_name;
	long timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
	struct list_head *clinks = &tsk->cong_links;
	bool syn = !tipc_sk_type_connectionless(sk);
	struct tipc_group *grp = tsk->group;
	struct tipc_msg *hdr = &tsk->phdr;
	struct tipc_socket_addr skaddr;
	struct sk_buff_head pkts;
	int atype, mtu, rc;

	if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE))
		return -EMSGSIZE;

	if (ua) {
		if (!tipc_uaddr_valid(ua, m->msg_namelen))
			return -EINVAL;
		atype = ua->addrtype;
	}

	/* If socket belongs to a communication group follow other paths */
	if (grp) {
		if (!ua)
			return tipc_send_group_bcast(sock, m, dlen, timeout);
		if (atype == TIPC_SERVICE_ADDR)
			return tipc_send_group_anycast(sock, m, dlen, timeout);
		if (atype == TIPC_SOCKET_ADDR)
			return tipc_send_group_unicast(sock, m, dlen, timeout);
		if (atype == TIPC_SERVICE_RANGE)
			return tipc_send_group_mcast(sock, m, dlen, timeout);
		return -EINVAL;
	}

	if (!ua) {
		ua = (struct tipc_uaddr *)&tsk->peer;
		if (!syn && ua->family != AF_TIPC)
			return -EDESTADDRREQ;
		atype = ua->addrtype;
	}

	if (unlikely(syn)) {
		if (sk->sk_state == TIPC_LISTEN)
			return -EPIPE;
		if (sk->sk_state != TIPC_OPEN)
			return -EISCONN;
		if (tsk->published)
			return -EOPNOTSUPP;
		if (atype == TIPC_SERVICE_ADDR)
			tsk->conn_addrtype = atype;
		msg_set_syn(hdr, 1);
	}

	memset(&skaddr, 0, sizeof(skaddr));

	/* Determine destination */
	if (atype == TIPC_SERVICE_RANGE) {
		return tipc_sendmcast(sock, ua, m, dlen, timeout);
	} else if (atype == TIPC_SERVICE_ADDR) {
		skaddr.node = ua->lookup_node;
		ua->scope = tipc_node2scope(skaddr.node);
		if (!tipc_nametbl_lookup_anycast(net, ua, &skaddr))
			return -EHOSTUNREACH;
	} else if (atype == TIPC_SOCKET_ADDR) {
		skaddr = ua->sk;
	} else {
		return -EINVAL;
	}

	/* Block or return if destination link is congested */
	rc = tipc_wait_for_cond(sock, &timeout,
				!tipc_dest_find(clinks, skaddr.node, 0));
	if (unlikely(rc))
		return rc;

	/* Finally build message header */
	msg_set_destnode(hdr, skaddr.node);
	msg_set_destport(hdr, skaddr.ref);
	if (atype == TIPC_SERVICE_ADDR) {
		msg_set_type(hdr, TIPC_NAMED_MSG);
		msg_set_hdr_sz(hdr, NAMED_H_SIZE);
		msg_set_nametype(hdr, ua->sa.type);
		msg_set_nameinst(hdr, ua->sa.instance);
		msg_set_lookup_scope(hdr, ua->scope);
	} else { /* TIPC_SOCKET_ADDR */
		msg_set_type(hdr, TIPC_DIRECT_MSG);
		msg_set_lookup_scope(hdr, 0);
		msg_set_hdr_sz(hdr, BASIC_H_SIZE);
	}

	/* Add message body */
	__skb_queue_head_init(&pkts);
	mtu = tipc_node_get_mtu(net, skaddr.node, tsk->portid, true);
	rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts);
	if (unlikely(rc != dlen))
		return rc;
	if (unlikely(syn && !tipc_msg_skb_clone(&pkts, &sk->sk_write_queue))) {
		__skb_queue_purge(&pkts);
		return -ENOMEM;
	}

	/* Send message */
	trace_tipc_sk_sendmsg(sk, skb_peek(&pkts), TIPC_DUMP_SK_SNDQ, " ");
	rc = tipc_node_xmit(net, &pkts, skaddr.node, tsk->portid);
	if (unlikely(rc == -ELINKCONG)) {
		tipc_dest_push(clinks, skaddr.node, 0);
		tsk->cong_link_cnt++;
		rc = 0;
	}

	if (unlikely(syn && !rc)) {
		tipc_set_sk_state(sk, TIPC_CONNECTING);
		if (dlen && timeout) {
			timeout = msecs_to_jiffies(timeout);
			tipc_wait_for_connect(sock, &timeout);
		}
	}

	return rc ? rc : dlen;
}