Skip to content

Commit 1c09990

Browse files
mmhalNipaLocal
authored andcommitted
vsock/test: Cover more CIDs in transport_uaf test
Increase the coverage of test for UAF due to socket unbinding, and losing transport in general. It's a follow up to commit 301a62d ("vsock/test: Add test for UAF due to socket unbinding") and discussion in [1]. The idea remains the same: take an unconnected stream socket with a transport assigned and then attempt to switch the transport by trying (and failing) to connect to some other CID. Now do this iterating over all the well known CIDs (plus one). While at it, drop the redundant synchronization between client and server. Some single-transport setups can't be tested effectively; a warning is issued. Depending on transports available, a variety of splats are possible on unpatched machines. After reverting commit 78dafe1 ("vsock: Orphan socket after transport release") and commit fcdd224 ("vsock: Keep the binding until socket destruction"): BUG: KASAN: slab-use-after-free in __vsock_bind+0x61f/0x720 Read of size 4 at addr ffff88811ff46b54 by task vsock_test/1475 Call Trace: dump_stack_lvl+0x68/0x90 print_report+0x170/0x53d kasan_report+0xc2/0x180 __vsock_bind+0x61f/0x720 vsock_connect+0x727/0xc40 __sys_connect+0xe8/0x100 __x64_sys_connect+0x6e/0xc0 do_syscall_64+0x92/0x1c0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 WARNING: CPU: 0 PID: 1475 at net/vmw_vsock/virtio_transport_common.c:37 virtio_transport_send_pkt_info+0xb2b/0x1160 Call Trace: virtio_transport_connect+0x90/0xb0 vsock_connect+0x782/0xc40 __sys_connect+0xe8/0x100 __x64_sys_connect+0x6e/0xc0 do_syscall_64+0x92/0x1c0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 KASAN: null-ptr-deref in range [0x0000000000000010-0x0000000000000017] RIP: 0010:sock_has_perm+0xa7/0x2a0 Call Trace: selinux_socket_connect_helper.isra.0+0xbc/0x450 selinux_socket_connect+0x3b/0x70 security_socket_connect+0x31/0xd0 __sys_connect_file+0x79/0x1f0 __sys_connect+0xe8/0x100 __x64_sys_connect+0x6e/0xc0 do_syscall_64+0x92/0x1c0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 refcount_t: addition on 0; use-after-free. WARNING: CPU: 7 PID: 1518 at lib/refcount.c:25 refcount_warn_saturate+0xdd/0x140 RIP: 0010:refcount_warn_saturate+0xdd/0x140 Call Trace: __vsock_bind+0x65e/0x720 vsock_connect+0x727/0xc40 __sys_connect+0xe8/0x100 __x64_sys_connect+0x6e/0xc0 do_syscall_64+0x92/0x1c0 entry_SYSCALL_64_after_hwframe+0x4b/0x53 refcount_t: underflow; use-after-free. WARNING: CPU: 0 PID: 1475 at lib/refcount.c:28 refcount_warn_saturate+0x12b/0x140 RIP: 0010:refcount_warn_saturate+0x12b/0x140 Call Trace: vsock_remove_bound+0x18f/0x280 __vsock_release+0x371/0x480 vsock_release+0x88/0x120 __sock_release+0xaa/0x260 sock_close+0x14/0x20 __fput+0x35a/0xaa0 task_work_run+0xff/0x1c0 do_exit+0x849/0x24c0 make_task_dead+0xf3/0x110 rewind_stack_and_make_dead+0x16/0x20 [1]: https://lore.kernel.org/netdev/CAGxU2F5zhfWymY8u0hrKksW8PumXAYz-9_qRmW==92oAx1BX3g@mail.gmail.com/ Suggested-by: Stefano Garzarella <[email protected]> Signed-off-by: Michal Luczaj <[email protected]> Reviewed-by: Stefano Garzarella <[email protected]> Signed-off-by: NipaLocal <nipa@local>
1 parent 86c1ab0 commit 1c09990

File tree

1 file changed

+74
-19
lines changed

1 file changed

+74
-19
lines changed

tools/testing/vsock/vsock_test.c

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,55 +1718,111 @@ static void test_stream_msgzcopy_leak_zcskb_server(const struct test_opts *opts)
17181718

17191719
#define MAX_PORT_RETRIES 24 /* net/vmw_vsock/af_vsock.c */
17201720

1721-
/* Test attempts to trigger a transport release for an unbound socket. This can
1722-
* lead to a reference count mishandling.
1723-
*/
1724-
static void test_stream_transport_uaf_client(const struct test_opts *opts)
1721+
static bool test_stream_transport_uaf(int cid)
17251722
{
17261723
int sockets[MAX_PORT_RETRIES];
17271724
struct sockaddr_vm addr;
1728-
int fd, i, alen;
1725+
socklen_t alen;
1726+
int fd, i, c;
1727+
bool ret;
1728+
1729+
/* Probe for a transport by attempting a local CID bind. Unavailable
1730+
* transport (or more specifically: an unsupported transport/CID
1731+
* combination) results in EADDRNOTAVAIL, other errnos are fatal.
1732+
*/
1733+
fd = vsock_bind_try(cid, VMADDR_PORT_ANY, SOCK_STREAM);
1734+
if (fd < 0) {
1735+
if (errno != EADDRNOTAVAIL) {
1736+
perror("Unexpected bind() errno");
1737+
exit(EXIT_FAILURE);
1738+
}
17291739

1730-
fd = vsock_bind(VMADDR_CID_ANY, VMADDR_PORT_ANY, SOCK_STREAM);
1740+
return false;
1741+
}
17311742

17321743
alen = sizeof(addr);
17331744
if (getsockname(fd, (struct sockaddr *)&addr, &alen)) {
17341745
perror("getsockname");
17351746
exit(EXIT_FAILURE);
17361747
}
17371748

1749+
/* Drain the autobind pool; see __vsock_bind_connectible(). */
17381750
for (i = 0; i < MAX_PORT_RETRIES; ++i)
1739-
sockets[i] = vsock_bind(VMADDR_CID_ANY, ++addr.svm_port,
1740-
SOCK_STREAM);
1751+
sockets[i] = vsock_bind(cid, ++addr.svm_port, SOCK_STREAM);
17411752

17421753
close(fd);
1743-
fd = socket(AF_VSOCK, SOCK_STREAM, 0);
1754+
1755+
/* Setting SOCK_NONBLOCK makes connect() return soon after
1756+
* (re-)assigning the transport. We are not connecting to anything
1757+
* anyway, so there is no point entering the main loop in
1758+
* vsock_connect(); waiting for timeout, checking for signals, etc.
1759+
*/
1760+
fd = socket(AF_VSOCK, SOCK_STREAM | SOCK_NONBLOCK, 0);
17441761
if (fd < 0) {
17451762
perror("socket");
17461763
exit(EXIT_FAILURE);
17471764
}
17481765

1749-
if (!vsock_connect_fd(fd, addr.svm_cid, addr.svm_port)) {
1750-
perror("Unexpected connect() #1 success");
1766+
/* Assign transport, while failing to autobind. Autobind pool was
1767+
* drained, so EADDRNOTAVAIL coming from __vsock_bind_connectible() is
1768+
* expected.
1769+
*
1770+
* One exception is ENODEV which is thrown by vsock_assign_transport(),
1771+
* i.e. before vsock_auto_bind(), when the only transport loaded is
1772+
* vhost.
1773+
*/
1774+
if (!connect(fd, (struct sockaddr *)&addr, alen)) {
1775+
fprintf(stderr, "Unexpected connect() success\n");
17511776
exit(EXIT_FAILURE);
17521777
}
1753-
1754-
/* Vulnerable system may crash now. */
1755-
if (!vsock_connect_fd(fd, VMADDR_CID_HOST, VMADDR_PORT_ANY)) {
1756-
perror("Unexpected connect() #2 success");
1778+
if (errno == ENODEV && cid == VMADDR_CID_HOST) {
1779+
ret = false;
1780+
goto cleanup;
1781+
}
1782+
if (errno != EADDRNOTAVAIL) {
1783+
perror("Unexpected connect() errno");
17571784
exit(EXIT_FAILURE);
17581785
}
17591786

1787+
/* Reassign transport, triggering old transport release and
1788+
* (potentially) unbinding of an unbound socket.
1789+
*
1790+
* Vulnerable system may crash now.
1791+
*/
1792+
for (c = VMADDR_CID_HYPERVISOR; c <= VMADDR_CID_HOST + 1; ++c) {
1793+
if (c != cid) {
1794+
addr.svm_cid = c;
1795+
(void)connect(fd, (struct sockaddr *)&addr, alen);
1796+
}
1797+
}
1798+
1799+
ret = true;
1800+
cleanup:
17601801
close(fd);
17611802
while (i--)
17621803
close(sockets[i]);
17631804

1764-
control_writeln("DONE");
1805+
return ret;
17651806
}
17661807

1767-
static void test_stream_transport_uaf_server(const struct test_opts *opts)
1808+
/* Test attempts to trigger a transport release for an unbound socket. This can
1809+
* lead to a reference count mishandling.
1810+
*/
1811+
static void test_stream_transport_uaf_client(const struct test_opts *opts)
17681812
{
1769-
control_expectln("DONE");
1813+
bool tested = false;
1814+
int cid, tr;
1815+
1816+
for (cid = VMADDR_CID_HYPERVISOR; cid <= VMADDR_CID_HOST + 1; ++cid)
1817+
tested |= test_stream_transport_uaf(cid);
1818+
1819+
tr = get_transports();
1820+
if (!tr)
1821+
fprintf(stderr, "No transports detected\n");
1822+
else if (tr == TRANSPORT_VIRTIO)
1823+
fprintf(stderr, "Setup unsupported: sole virtio transport\n");
1824+
else if (!tested)
1825+
fprintf(stderr, "No transports tested\n");
17701826
}
17711827

17721828
static void test_stream_connect_retry_client(const struct test_opts *opts)
@@ -2034,7 +2090,6 @@ static struct test_case test_cases[] = {
20342090
{
20352091
.name = "SOCK_STREAM transport release use-after-free",
20362092
.run_client = test_stream_transport_uaf_client,
2037-
.run_server = test_stream_transport_uaf_server,
20382093
},
20392094
{
20402095
.name = "SOCK_STREAM retry failed connect()",

0 commit comments

Comments
 (0)