From mboxrd@z Thu Jan 1 00:00:00 1970 Received: by passt.top (Postfix, from userid 1000) id A4A3E5A026A; Tue, 8 Nov 2022 09:54:19 +0100 (CET) From: Stefano Brivio To: passt-dev@passt.top Subject: [PATCH 1/2] tap: Keep stream consistent if qemu length descriptor spans two recv() calls Date: Tue, 8 Nov 2022 09:54:18 +0100 Message-Id: <20221108085419.3220900-2-sbrivio@redhat.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221108085419.3220900-1-sbrivio@redhat.com> References: <20221108085419.3220900-1-sbrivio@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: EULYDIWTWTEFDSW7EEYEBBJSX6SJ232O X-Message-ID-Hash: EULYDIWTWTEFDSW7EEYEBBJSX6SJ232O X-MailFrom: sbrivio@passt.top X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.3 Precedence: list List-Id: Development discussion and patches for passt Archived-At: <> Archived-At: List-Archive: <> List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: I got all paranoid after triggering a divide-by-zero general protection fault in passt with a qemu version without the virtio_net TX hang fix, while flooding UDP. I start thinking this was actually coming from some random changes I was playing with, but before reaching this conclusion I reviewed once more the relatively short path in tap_handler_passt() before we start using packet_*() functions, and found this. Never observed in practice, but artificially reproduced with changes in qemu's socket interface: if we don't receive from qemu a complete length descriptor in one recv() call, or if we receive a partial one at the end of one call, we currently disregard the rest, which would make the stream inconsistent. Nothing really bad happens, except that from that point on we would disregard all the packets we get until, if ever, we get the stream back in sync by chance. Force reading a complete packet length descriptor with a blocking recv(), if needed -- not just a complete packet later. Signed-off-by: Stefano Brivio --- tap.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tap.c b/tap.c index f8314ef..11ac732 100644 --- a/tap.c +++ b/tap.c @@ -747,14 +747,26 @@ redo: return -ECONNRESET; } - while (n > (ssize_t)sizeof(uint32_t)) { - ssize_t len = ntohl(*(uint32_t *)p); + while (n > 0) { + ssize_t len; + + /* Force receiving at least a complete length descriptor to + * avoid an inconsistent stream. + */ + if (n < (ssize_t)sizeof(uint32_t)) { + rem = recv(c->fd_tap, p + n, + (ssize_t)sizeof(uint32_t) - n, 0); + if ((n += rem) != (ssize_t)sizeof(uint32_t)) + return 0; + } + + len = ntohl(*(uint32_t *)p); p += sizeof(uint32_t); n -= sizeof(uint32_t); /* At most one packet might not fit in a single read, and this - * needs to be blocking. + * also needs to be blocking. */ if (len > n) { rem = recv(c->fd_tap, p + n, len - n, 0); -- 2.35.1