@@ -1561,6 +1561,153 @@ static void test_stream_msgzcopy_leak_errq_server(const struct test_opts *opts)
1561
1561
close (fd );
1562
1562
}
1563
1563
1564
+ /* Test msgzcopy_leak_zcskb is meant to exercise sendmsg() error handling path,
1565
+ * that might leak an skb. The idea is to fail virtio_transport_init_zcopy_skb()
1566
+ * by hitting net.core.optmem_max limit in sock_omalloc(), specifically
1567
+ *
1568
+ * vsock_connectible_sendmsg
1569
+ * virtio_transport_stream_enqueue
1570
+ * virtio_transport_send_pkt_info
1571
+ * virtio_transport_init_zcopy_skb
1572
+ * . msg_zerocopy_realloc
1573
+ * . msg_zerocopy_alloc
1574
+ * . sock_omalloc
1575
+ * . sk_omem_alloc + size > sysctl_optmem_max
1576
+ * return -ENOMEM
1577
+ *
1578
+ * We abuse the implementation detail of net/socket.c:____sys_sendmsg().
1579
+ * sk_omem_alloc can be precisely bumped by sock_kmalloc(), as it is used to
1580
+ * fetch user-provided control data.
1581
+ *
1582
+ * While this approach works for now, it relies on assumptions regarding the
1583
+ * implementation and configuration (for example, order of net.core.optmem_max
1584
+ * can not exceed MAX_PAGE_ORDER), which may not hold in the future. A more
1585
+ * resilient testing could be implemented by leveraging the Fault injection
1586
+ * framework (CONFIG_FAULT_INJECTION), e.g.
1587
+ *
1588
+ * client# echo N > /sys/kernel/debug/failslab/ignore-gfp-wait
1589
+ * client# echo 0 > /sys/kernel/debug/failslab/verbose
1590
+ *
1591
+ * void client(const struct test_opts *opts)
1592
+ * {
1593
+ * char buf[16];
1594
+ * int f, s, i;
1595
+ *
1596
+ * f = open("/proc/self/fail-nth", O_WRONLY);
1597
+ *
1598
+ * for (i = 1; i < 32; i++) {
1599
+ * control_writeulong(CONTROL_CONTINUE);
1600
+ *
1601
+ * s = vsock_stream_connect(opts->peer_cid, opts->peer_port);
1602
+ * enable_so_zerocopy_check(s);
1603
+ *
1604
+ * sprintf(buf, "%d", i);
1605
+ * write(f, buf, strlen(buf));
1606
+ *
1607
+ * send(s, &(char){ 0 }, 1, MSG_ZEROCOPY);
1608
+ *
1609
+ * write(f, "0", 1);
1610
+ * close(s);
1611
+ * }
1612
+ *
1613
+ * control_writeulong(CONTROL_DONE);
1614
+ * close(f);
1615
+ * }
1616
+ *
1617
+ * void server(const struct test_opts *opts)
1618
+ * {
1619
+ * int fd;
1620
+ *
1621
+ * while (control_readulong() == CONTROL_CONTINUE) {
1622
+ * fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
1623
+ * vsock_wait_remote_close(fd);
1624
+ * close(fd);
1625
+ * }
1626
+ * }
1627
+ *
1628
+ * Refer to Documentation/fault-injection/fault-injection.rst.
1629
+ */
1630
+ #define MAX_PAGE_ORDER 10 /* usually */
1631
+ #define PAGE_SIZE 4096
1632
+
1633
+ /* Test for a memory leak. User is expected to run kmemleak scan, see README. */
1634
+ static void test_stream_msgzcopy_leak_zcskb_client (const struct test_opts * opts )
1635
+ {
1636
+ size_t optmem_max , ctl_len , chunk_size ;
1637
+ struct msghdr msg = { 0 };
1638
+ struct iovec iov ;
1639
+ char * chunk ;
1640
+ int fd , res ;
1641
+ FILE * f ;
1642
+
1643
+ f = fopen ("/proc/sys/net/core/optmem_max" , "r" );
1644
+ if (!f ) {
1645
+ perror ("fopen(optmem_max)" );
1646
+ exit (EXIT_FAILURE );
1647
+ }
1648
+
1649
+ if (fscanf (f , "%zu" , & optmem_max ) != 1 ) {
1650
+ fprintf (stderr , "fscanf(optmem_max) failed\n" );
1651
+ exit (EXIT_FAILURE );
1652
+ }
1653
+
1654
+ fclose (f );
1655
+
1656
+ fd = vsock_stream_connect (opts -> peer_cid , opts -> peer_port );
1657
+ if (fd < 0 ) {
1658
+ perror ("connect" );
1659
+ exit (EXIT_FAILURE );
1660
+ }
1661
+
1662
+ enable_so_zerocopy_check (fd );
1663
+
1664
+ ctl_len = optmem_max - 1 ;
1665
+ if (ctl_len > PAGE_SIZE << MAX_PAGE_ORDER ) {
1666
+ fprintf (stderr , "Try with net.core.optmem_max = 100000\n" );
1667
+ exit (EXIT_FAILURE );
1668
+ }
1669
+
1670
+ chunk_size = CMSG_SPACE (ctl_len );
1671
+ chunk = malloc (chunk_size );
1672
+ if (!chunk ) {
1673
+ perror ("malloc" );
1674
+ exit (EXIT_FAILURE );
1675
+ }
1676
+ memset (chunk , 0 , chunk_size );
1677
+
1678
+ iov .iov_base = & (char ){ 0 };
1679
+ iov .iov_len = 1 ;
1680
+
1681
+ msg .msg_iov = & iov ;
1682
+ msg .msg_iovlen = 1 ;
1683
+ msg .msg_control = chunk ;
1684
+ msg .msg_controllen = ctl_len ;
1685
+
1686
+ errno = 0 ;
1687
+ res = sendmsg (fd , & msg , MSG_ZEROCOPY );
1688
+ if (res >= 0 || errno != ENOMEM ) {
1689
+ fprintf (stderr , "Expected ENOMEM, got errno=%d res=%d\n" ,
1690
+ errno , res );
1691
+ exit (EXIT_FAILURE );
1692
+ }
1693
+
1694
+ close (fd );
1695
+ }
1696
+
1697
+ static void test_stream_msgzcopy_leak_zcskb_server (const struct test_opts * opts )
1698
+ {
1699
+ int fd ;
1700
+
1701
+ fd = vsock_stream_accept (VMADDR_CID_ANY , opts -> peer_port , NULL );
1702
+ if (fd < 0 ) {
1703
+ perror ("accept" );
1704
+ exit (EXIT_FAILURE );
1705
+ }
1706
+
1707
+ vsock_wait_remote_close (fd );
1708
+ close (fd );
1709
+ }
1710
+
1564
1711
static struct test_case test_cases [] = {
1565
1712
{
1566
1713
.name = "SOCK_STREAM connection reset" ,
@@ -1701,6 +1848,11 @@ static struct test_case test_cases[] = {
1701
1848
.run_client = test_stream_msgzcopy_leak_errq_client ,
1702
1849
.run_server = test_stream_msgzcopy_leak_errq_server ,
1703
1850
},
1851
+ {
1852
+ .name = "SOCK_STREAM MSG_ZEROCOPY leak completion skb" ,
1853
+ .run_client = test_stream_msgzcopy_leak_zcskb_client ,
1854
+ .run_server = test_stream_msgzcopy_leak_zcskb_server ,
1855
+ },
1704
1856
{},
1705
1857
};
1706
1858
0 commit comments