/* Compare partial and full peek. */ if (memcmp(buf_half, buf_peek, sizeof(buf_half))) {
fprintf(stderr, "Partial peek data mismatch\n"); exit(EXIT_FAILURE);
}
if (seqpacket) { /* This type of socket supports MSG_TRUNC flag, *socheckitwithMSG_PEEK.Wemustgetlength *ofthemessage.
*/
recv_buf(fd, buf_half, sizeof(buf_half), MSG_PEEK | MSG_TRUNC, sizeof(buf_peek));
}
/* Compare full peek and normal read. */ if (memcmp(buf_peek, buf_normal, sizeof(buf_peek))) {
fprintf(stderr, "Full peek data mismatch\n"); exit(EXIT_FAILURE);
}
staticvoid test_seqpacket_msg_bounds_client(conststruct test_opts *opts)
{ unsignedlong curr_hash;
size_t max_msg_size; int page_size; int msg_count; int fd;
if (read_overhead_ns > READ_OVERHEAD_NSEC) {
fprintf(stderr, "too much time in read(2), %lu > %i ns\n",
read_overhead_ns, READ_OVERHEAD_NSEC); exit(EXIT_FAILURE);
}
control_writeln("WAITDONE");
close(fd);
}
staticvoid test_seqpacket_timeout_server(conststruct test_opts *opts)
{ int fd;
if (getsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,
&sock_buf_size, &len)) {
perror("getsockopt"); exit(EXIT_FAILURE);
}
sock_buf_size++;
/* size_t can be < unsigned long long */
buf_size = (size_t)sock_buf_size; if (buf_size != sock_buf_size) {
fprintf(stderr, "Returned BUFFER_SIZE too large\n"); exit(EXIT_FAILURE);
}
data = malloc(buf_size); if (!data) {
perror("malloc"); exit(EXIT_FAILURE);
}
send_buf(fd, data, buf_size, 0, -EMSGSIZE);
control_writeln("CLISENT");
free(data);
close(fd);
}
staticvoid test_seqpacket_bigmsg_server(conststruct test_opts *opts)
{ int fd;
for (i = 0; i < buf_size; i++) { if (valid_buf[i] != BUF_PATTERN_2) {
fprintf(stderr, "invalid pattern for 'valid_buf' at %i, expected %hhX, got %hhX\n",
i, BUF_PATTERN_2, valid_buf[i]); exit(EXIT_FAILURE);
}
}
/* At this point, server sent 1 byte. */
fds.fd = fd;
poll_flags = POLLIN | POLLRDNORM;
fds.events = poll_flags;
/* Try to wait for 1 sec. */ if (poll(&fds, 1, 1000) < 0) {
perror("poll"); exit(EXIT_FAILURE);
}
/* poll() must return nothing. */ if (fds.revents) {
fprintf(stderr, "Unexpected poll result %hx\n",
fds.revents); exit(EXIT_FAILURE);
}
/* Tell server to send rest of data. */
control_writeln("CLNSENT");
/* Poll for data. */ if (poll(&fds, 1, 10000) < 0) {
perror("poll"); exit(EXIT_FAILURE);
}
/* Only these two bits are expected. */ if (fds.revents != poll_flags) {
fprintf(stderr, "Unexpected poll result %hx\n",
fds.revents); exit(EXIT_FAILURE);
}
/* Use MSG_DONTWAIT, if call is going to wait, EAGAIN *willbereturned.
*/
recv_buf(fd, buf, sizeof(buf), MSG_DONTWAIT, RCVLOWAT_BUF_SIZE);
if (stream) { /* For SOCK_STREAM we must continue reading. */
expected_ret = sizeof(data);
} else { /* For SOCK_SEQPACKET socket's queue must be empty. */
expected_ret = -EAGAIN;
}
/* When the other peer calls shutdown(SHUT_RD), there is a chance that *thesend()callcouldoccurbeforethemessagecarryingtheclose *informationarrivesoverthetransport.Insuchcases,thesend() *mightstillsucceed.Toavoidthisrace,let'sretrythesend()call *afewtimes,ensuringthetestismorereliable.
*/
timeout_begin(TIMEOUT); while(1) {
res = send(fd, "A", 1, 0); if (res == -1 && errno != EINTR) break;
/* Sleep a little before trying again to avoid flooding the *otherpeerandfillingitsreceivebuffer,causing *false-negative.
*/
timeout_usleep(SEND_SLEEP_USEC);
timeout_check("send");
}
timeout_end();
if (client_fd < 0) {
perror("accept"); exit(EXIT_FAILURE);
}
/* Waiting for remote peer to close connection */
vsock_wait_remote_close(client_fd);
}
close(listen_fd);
}
staticvoid test_double_bind_connect_client(conststruct test_opts *opts)
{ int i, client_fd;
for (i = 0; i < 2; i++) { /* Wait until server is ready to accept a new connection */
control_expectln("LISTENING");
/* We use 'peer_port + 1' as "some" port for the 'bind()' *call.Itissafeforoverflow,butmustbeconsidered, *whenrunningmultipletestapplicationssimultaneously *where'peer-port'argumentdiffersby1.
*/
client_fd = vsock_bind_connect(opts->peer_cid, opts->peer_port,
opts->peer_port + 1, SOCK_STREAM);
close(client_fd);
}
}
#define MSG_BUF_IOCTL_LEN 64 staticvoid test_unsent_bytes_server(conststruct test_opts *opts, int type)
{ unsignedchar buf[MSG_BUF_IOCTL_LEN]; int client_fd;
/* SIOCOUTQ isn't guaranteed to instantly track sent data. Even though *the"RECEIVED"messagemeansthattheothersidehasreceivedthe *data,therecanbeadelayinourkernelbeforeupdatingthe"unsent *bytes"counter.vsock_wait_sent()willrepeatSIOCOUTQuntilit *returns0.
*/ if (!vsock_wait_sent(fd))
fprintf(stderr, "Test skipped, SIOCOUTQ not supported.\n");
close(fd);
}
staticvoid test_unread_bytes_server(conststruct test_opts *opts, int type)
{ unsignedchar buf[MSG_BUF_IOCTL_LEN]; int client_fd;
control_expectln("SENT"); /* The data has arrived but has not been read. The expected is *MSG_BUF_IOCTL_LEN.
*/ if (!vsock_ioctl_int(fd, SIOCINQ, MSG_BUF_IOCTL_LEN)) {
fprintf(stderr, "Test skipped, SIOCINQ not supported.\n"); goto out;
}
recv_buf(fd, buf, sizeof(buf), 0, sizeof(buf)); /* All data has been consumed, so the expected is 0. */
vsock_ioctl_int(fd, SIOCINQ, 0);
#define RCVLOWAT_CREDIT_UPD_BUF_SIZE (1024 * 128) /* This define is the same as in 'include/linux/virtio_vsock.h': *itisusedtodecidewhentosendcreditupdatemessageduring *readingfromrxqueueofasocket.Valueanditsusagein *kernelisimportantforthistest.
*/ #define VIRTIO_VSOCK_MAX_PKT_BUF_SIZE (1024 * 64)
if (low_rx_bytes_test) { /* Set new SO_RCVLOWAT here. This enables sending credit *updatewhennumberofbytesifourrxqueuebecome< *SO_RCVLOWATvalue.
*/
recv_buf_size = 1 + VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;
/* There is 128KB of data in the socket's rx queue, dequeue first *64KB,creditupdateissentif'low_rx_bytes_test'==true. *Otherwise,creditupdateissentin'if(!low_rx_bytes_test)'.
*/
recv_buf_size = VIRTIO_VSOCK_MAX_PKT_BUF_SIZE;
recv_buf(fd, buf, recv_buf_size, 0, recv_buf_size);
/* This 'poll()' will return once we receive last byte *sentbyclient.
*/ if (poll(&fds, 1, -1) < 0) {
perror("poll"); exit(EXIT_FAILURE);
}
if (fds.revents & POLLERR) {
fprintf(stderr, "'poll()' error\n"); exit(EXIT_FAILURE);
}
if (fds.revents & (POLLIN | POLLRDNORM)) {
recv_buf(fd, buf, recv_buf_size, MSG_DONTWAIT, recv_buf_size);
} else { /* These flags must be set, as there is at *least64KBofdatareadytoread.
*/
fprintf(stderr, "POLLIN | POLLRDNORM expected\n"); exit(EXIT_FAILURE);
}
/* The goal of test leak_acceptq is to stress the race between connect() and *close(listener).Implementationofclient/serverloopsboilsdownto: * *clientserver *------------ *write(CONTINUE) *expect(CONTINUE) *listen() *write(LISTENING) *expect(LISTENING) *connect()close()
*/ #define ACCEPTQ_LEAK_RACE_TIMEOUT 2/* seconds */
staticvoid test_stream_leak_acceptq_client(conststruct test_opts *opts)
{
time_t tout; int fd;
tout = current_nsec() + ACCEPTQ_LEAK_RACE_TIMEOUT * NSEC_PER_SEC; do {
control_writeulong(CONTROL_CONTINUE);
fd = vsock_stream_connect(opts->peer_cid, opts->peer_port); if (fd >= 0)
close(fd);
} while (current_nsec() < tout);
control_writeulong(CONTROL_DONE);
}
/* Test for a memory leak. User is expected to run kmemleak scan, see README. */ staticvoid test_stream_leak_acceptq_server(conststruct test_opts *opts)
{ int fd;
/* Test for a memory leak. User is expected to run kmemleak scan, see README. */ staticvoid test_stream_msgzcopy_leak_errq_client(conststruct test_opts *opts)
{ struct pollfd fds = { 0 }; int fd;
/* Test for a memory leak. User is expected to run kmemleak scan, see README. */ staticvoid test_stream_msgzcopy_leak_zcskb_client(conststruct test_opts *opts)
{
size_t optmem_max, ctl_len, chunk_size; struct msghdr msg = { 0 }; struct iovec iov; char *chunk; int fd, res;
FILE *f;
f = fopen("/proc/sys/net/core/optmem_max", "r"); if (!f) {
perror("fopen(optmem_max)"); exit(EXIT_FAILURE);
}
staticbool test_stream_transport_uaf(int cid)
{ int sockets[MAX_PORT_RETRIES]; struct sockaddr_vm addr;
socklen_t alen; int fd, i, c; bool ret;
/* Probe for a transport by attempting a local CID bind. Unavailable *transport(ormorespecifically:anunsupportedtransport/CID *combination)resultsinEADDRNOTAVAIL,othererrnosarefatal.
*/
fd = vsock_bind_try(cid, VMADDR_PORT_ANY, SOCK_STREAM); if (fd < 0) { if (errno != EADDRNOTAVAIL) {
perror("Unexpected bind() errno"); exit(EXIT_FAILURE);
}
/* Drain the autobind pool; see __vsock_bind_connectible(). */ for (i = 0; i < MAX_PORT_RETRIES; ++i)
sockets[i] = vsock_bind(cid, ++addr.svm_port, SOCK_STREAM);
close(fd);
/* Setting SOCK_NONBLOCK makes connect() return soon after *(re-)assigningthetransport.Wearenotconnectingtoanything *anyway,sothereisnopointenteringthemainloopin *vsock_connect();waitingfortimeout,checkingforsignals,etc.
*/
fd = socket(AF_VSOCK, SOCK_STREAM | SOCK_NONBLOCK, 0); if (fd < 0) {
perror("socket"); exit(EXIT_FAILURE);
}
/* Assign transport, while failing to autobind. Autobind pool was *drained,soEADDRNOTAVAILcomingfrom__vsock_bind_connectible()is *expected. * *OneexceptionisENODEVwhichisthrownbyvsock_assign_transport(), *i.e.beforevsock_auto_bind(),whentheonlytransportloadedis *vhost.
*/ if (!connect(fd, (struct sockaddr *)&addr, alen)) {
fprintf(stderr, "Unexpected connect() success\n"); exit(EXIT_FAILURE);
} if (errno == ENODEV && cid == VMADDR_CID_HOST) {
ret = false; goto cleanup;
} if (errno != EADDRNOTAVAIL) {
perror("Unexpected connect() errno"); exit(EXIT_FAILURE);
}
/* Reassign transport, triggering old transport release and *(potentially)unbindingofanunboundsocket. * *Vulnerablesystemmaycrashnow.
*/ for (c = VMADDR_CID_HYPERVISOR; c <= VMADDR_CID_HOST + 1; ++c) { if (c != cid) {
addr.svm_cid = c;
(void)connect(fd, (struct sockaddr *)&addr, alen);
}
}
ret = true;
cleanup:
close(fd); while (i--)
close(sockets[i]);
return ret;
}
/* Test attempts to trigger a transport release for an unbound socket. This can *leadtoareferencecountmishandling.
*/ staticvoid test_stream_transport_uaf_client(conststruct test_opts *opts)
{ bool tested = false; int cid, tr;
/* Print a warning if there is a G2H transport loaded. *ThisisonabesteffortbasisbecauseVMCIcanbeeitherG2HandH2G,andthereis *noeasywaytounderstandit. *ThebugwearetestingonlyappearswhenG2Htransportsarenotloaded. *Thisisbecause`vsock_assign_transport`,whenusingCID0,assignsaG2Htransport *tovsk->transport.IfnoneisavailableitissettoNULL,causingthenull-ptr-deref.
*/ if (tr & TRANSPORTS_G2H)
fprintf(stderr, "G2H Transport detected. This test will not fail.\n");
ret = pthread_create(&thread_id, NULL, test_stream_transport_change_thread, &pid); if (ret) {
fprintf(stderr, "pthread_create: %d\n", ret); exit(EXIT_FAILURE);
}
control_expectln("LISTENING");
tout = current_nsec() + TRANSPORT_CHANGE_TIMEOUT * NSEC_PER_SEC; do { struct sockaddr_vm sa = {
.svm_family = AF_VSOCK,
.svm_cid = opts->peer_cid,
.svm_port = opts->peer_port,
}; bool send_control = false; int s;
s = socket(AF_VSOCK, SOCK_STREAM, 0); if (s < 0) {
perror("socket"); exit(EXIT_FAILURE);
}
ret = connect(s, (struct sockaddr *)&sa, sizeof(sa)); /* The connect can fail due to signals coming from the thread, *orbecausethereceiverconnectionqueueisfull. *Ignoringalsothelattercasebecausethereisnoway *ofsynchronizingclient'sconnectandserver'sacceptwhen *connect(s)areconstantlybeinginterruptedbysignals.
*/ if (ret == -1 && (errno != EINTR && errno != ECONNRESET)) {
perror("connect"); exit(EXIT_FAILURE);
}
/* Notify the server if the connect() is successful or the *receiverconnectionqueueisfull,soitwilldoaccept() *todrainit.
*/ if (!ret || errno == ECONNRESET)
send_control = true;
/* Set CID to 0 cause a transport change. */
sa.svm_cid = 0;
/* There is a case where this will not fail: *ifthepreviousconnect()isinterruptedwhilethe *connectionrequestisalreadysent,thissecond *connect()willwaitfortheresponse.
*/
ret = connect(s, (struct sockaddr *)&sa, sizeof(sa)); if (!ret || errno == ECONNRESET)
send_control = true;
close(s);
if (send_control)
control_writeulong(CONTROL_CONTINUE);
} while (current_nsec() < tout);
control_writeulong(CONTROL_DONE);
ret = pthread_cancel(thread_id); if (ret) {
fprintf(stderr, "pthread_cancel: %d\n", ret); exit(EXIT_FAILURE);
}
ret = pthread_join(thread_id, NULL); if (ret) {
fprintf(stderr, "pthread_join: %d\n", ret); exit(EXIT_FAILURE);
}
if (signal(SIGUSR1, old_handler) == SIG_ERR) {
perror("signal"); exit(EXIT_FAILURE);
}
}
staticvoid test_stream_transport_change_server(conststruct test_opts *opts)
{ int s = vsock_stream_listen(VMADDR_CID_ANY, opts->peer_port);
/* Set the socket to be nonblocking because connects that have been interrupted *(EINTR)canfillthereceiver'sacceptqueueanyway,leadingtoconnectfailure. *Asoftoday(6.15)insuchsituationthereisnowaytounderstand,fromthe *clientside,iftheconnectionhasbeenqueuedintheserverornot.
*/ if (fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0) | O_NONBLOCK) < 0) {
perror("fcntl"); exit(EXIT_FAILURE);
}
control_writeln("LISTENING");
while (control_readulong() == CONTROL_CONTINUE) { /* Must accept the connection, otherwise the `listen` *queuewillfillupandnewconnectionswillfail. *Therecanbemorethanonequeuedconnection, *clearthemall.
*/ while (true) { int client = accept(s, NULL, NULL);
if (client < 0) { if (errno == EAGAIN) break;
perror("accept"); exit(EXIT_FAILURE);
}
close(client);
}
}
close(s);
}
staticvoid test_stream_linger_client(conststruct test_opts *opts)
{ int fd;
staticvoid usage(void)
{
fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--peer-port=<port>] [--list] [--skip=<test_id>]\n" "\n" " Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n" " Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n" "\n" "Run vsock.ko tests. Must be launched in both guest\n" "and host. One side must use --mode=client and\n" "the other side must use --mode=server.\n" "\n" "A TCP control socket connection is used to coordinate tests\n" "between the client and the server. The server requires a\n" "listen address and the client requires an address to\n" "connect to.\n" "\n" "The CID of the other side must be given with --peer-cid=<cid>.\n" "During the test, two AF_VSOCK ports will be used: the port\n" "specified with --peer-port=<port> (or the default port)\n" "and the next one.\n" "\n" "Options:\n" " --help This help message\n" " --control-host <host> Server IP address to connect to\n" " --control-port <port> Server port to listen on/connect to\n" " --mode client|server Server or client mode\n" " --peer-cid <cid> CID of the other side\n" " --peer-port <port> AF_VSOCK port used for the test [default: %d]\n" " --list List of tests that will be executed\n" " --pick <test_id> Test ID to execute selectively;\n" " use multiple --pick options to select more tests\n" " --skip <test_id> Test ID to skip;\n" " use multiple --skip options to skip more tests\n",
DEFAULT_PEER_PORT
); exit(EXIT_FAILURE);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.