diff --git a/SOURCES/kvm-tcp_emu-fix-unsafe-snprintf-usages.patch b/SOURCES/kvm-tcp_emu-fix-unsafe-snprintf-usages.patch new file mode 100644 index 0000000..42c2abf --- /dev/null +++ b/SOURCES/kvm-tcp_emu-fix-unsafe-snprintf-usages.patch @@ -0,0 +1,150 @@ +From 901de585a893830992a137f6e191434b2f533428 Mon Sep 17 00:00:00 2001 +From: jmaloy +Date: Thu, 13 Feb 2020 21:08:18 +0100 +Subject: [PATCH 2/2] tcp_emu: fix unsafe snprintf() usages +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Message-id: <20200213210818.9090-3-jmaloy@redhat.com> +Patchwork-id: 93832 +O-Subject: [RHEL-7.8 qemu-kvm PATCH 2/2] tcp_emu: fix unsafe snprintf() usages +Bugzilla: 1798970 +RH-Acked-by: Philippe Mathieu-Daudé +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Stefan Hajnoczi + +From: Marc-André Lureau + +Various calls to snprintf() assume that snprintf() returns "only" the +number of bytes written (excluding terminating NUL). + +https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04 + +"Upon successful completion, the snprintf() function shall return the +number of bytes that would be written to s had n been sufficiently +large excluding the terminating null byte." + +Before patch ce131029, if there isn't enough room in "m_data" for the +"DCC ..." message, we overflow "m_data". + +After the patch, if there isn't enough room for the same, we don't +overflow "m_data", but we set "m_len" out-of-bounds. The next time an +access is bounded by "m_len", we'll have a buffer overflow then. + +Use slirp_fmt*() to fix potential OOB memory access. + +Reported-by: Laszlo Ersek +Signed-off-by: Marc-André Lureau +Reviewed-by: Samuel Thibault +Message-Id: <20200127092414.169796-7-marcandre.lureau@redhat.com> +(cherry picked from commit 68ccb8021a838066f0951d4b2817eb6b6f10a843) + +Manually re-adapted since the cherry-pick didn't apply cleanly. + +Signed-off-by: Jon Maloy +Signed-off-by: Miroslav Rezanina +--- + slirp/tcp_subr.c | 44 +++++++++++++++++++++----------------------- + 1 file changed, 21 insertions(+), 23 deletions(-) + +diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c +index e83575e..8dae0cc 100644 +--- a/slirp/tcp_subr.c ++++ b/slirp/tcp_subr.c +@@ -610,8 +610,7 @@ tcp_emu(struct socket *so, struct mbuf *m) + NTOHS(n1); + NTOHS(n2); + m_inc(m, snprintf(NULL, 0, "%d,%d\r\n", n1, n2) + 1); +- m->m_len = snprintf(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2); +- assert(m->m_len < M_ROOM(m)); ++ m->m_len = slirp_fmt(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2); + } else { + *eol = '\r'; + } +@@ -651,9 +650,9 @@ tcp_emu(struct socket *so, struct mbuf *m) + n4 = (laddr & 0xff); + + m->m_len = bptr - m->m_data; /* Adjust length */ +- m->m_len += snprintf(bptr, M_FREEROOM(m), +- "ORT %d,%d,%d,%d,%d,%d\r\n%s", +- n1, n2, n3, n4, n5, n6, x==7?buff:""); ++ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), ++ "ORT %d,%d,%d,%d,%d,%d\r\n%s", ++ n1, n2, n3, n4, n5, n6, x == 7 ? buff : ""); + return 1; + } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) { + /* +@@ -684,10 +683,9 @@ tcp_emu(struct socket *so, struct mbuf *m) + n4 = (laddr & 0xff); + + m->m_len = bptr - m->m_data; /* Adjust length */ +- m->m_len += snprintf(bptr, M_FREEROOM(m), +- "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s", +- n1, n2, n3, n4, n5, n6, x==7?buff:""); +- ++ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), ++ "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s", ++ n1, n2, n3, n4, n5, n6, x == 7 ? buff : ""); + return 1; + } + +@@ -710,8 +708,8 @@ tcp_emu(struct socket *so, struct mbuf *m) + if (m->m_data[m->m_len-1] == '\0' && lport != 0 && + (so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr, + htons(lport), SS_FACCEPTONCE)) != NULL) +- m->m_len = snprintf(m->m_data, M_ROOM(m), +- "%d", ntohs(so->so_fport)) + 1; ++ m->m_len = slirp_fmt0(m->m_data, M_ROOM(m), ++ "%d", ntohs(so->so_fport)); + return 1; + + case EMU_IRC: +@@ -731,10 +729,10 @@ tcp_emu(struct socket *so, struct mbuf *m) + return 1; + } + m->m_len = bptr - m->m_data; /* Adjust length */ +- m->m_len += snprintf(bptr, M_FREEROOM(m), +- "DCC CHAT chat %lu %u%c\n", +- (unsigned long)ntohl(so->so_faddr.s_addr), +- ntohs(so->so_fport), 1); ++ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), ++ "DCC CHAT chat %lu %u%c\n", ++ (unsigned long)ntohl(so->so_faddr.s_addr), ++ ntohs(so->so_fport), 1); + } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) { + if ((so = tcp_listen(slirp, INADDR_ANY, 0, + htonl(laddr), htons(lport), +@@ -742,10 +740,10 @@ tcp_emu(struct socket *so, struct mbuf *m) + return 1; + } + m->m_len = bptr - m->m_data; /* Adjust length */ +- m->m_len += snprintf(bptr, M_FREEROOM(m), +- "DCC SEND %s %lu %u %u%c\n", buff, +- (unsigned long)ntohl(so->so_faddr.s_addr), +- ntohs(so->so_fport), n1, 1); ++ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), ++ "DCC SEND %s %lu %u %u%c\n", buff, ++ (unsigned long)ntohl(so->so_faddr.s_addr), ++ ntohs(so->so_fport), n1, 1); + } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) { + if ((so = tcp_listen(slirp, INADDR_ANY, 0, + htonl(laddr), htons(lport), +@@ -753,10 +751,10 @@ tcp_emu(struct socket *so, struct mbuf *m) + return 1; + } + m->m_len = bptr - m->m_data; /* Adjust length */ +- m->m_len += snprintf(bptr, M_FREEROOM(m), +- "DCC MOVE %s %lu %u %u%c\n", buff, +- (unsigned long)ntohl(so->so_faddr.s_addr), +- ntohs(so->so_fport), n1, 1); ++ m->m_len += slirp_fmt(bptr, M_FREEROOM(m), ++ "DCC MOVE %s %lu %u %u%c\n", buff, ++ (unsigned long)ntohl(so->so_faddr.s_addr), ++ ntohs(so->so_fport), n1, 1); + } + return 1; + +-- +1.8.3.1 + diff --git a/SOURCES/kvm-util-add-slirp_fmt-helpers.patch b/SOURCES/kvm-util-add-slirp_fmt-helpers.patch new file mode 100644 index 0000000..e888d28 --- /dev/null +++ b/SOURCES/kvm-util-add-slirp_fmt-helpers.patch @@ -0,0 +1,140 @@ +From a90900d27423b09f268774dd664fb161a44c1c24 Mon Sep 17 00:00:00 2001 +From: jmaloy +Date: Thu, 13 Feb 2020 21:08:17 +0100 +Subject: [PATCH 1/2] util: add slirp_fmt() helpers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Message-id: <20200213210818.9090-2-jmaloy@redhat.com> +Patchwork-id: 93831 +O-Subject: [RHEL-7.8 qemu-kvm PATCH 1/2] util: add slirp_fmt() helpers +Bugzilla: 1798970 +RH-Acked-by: Maxim Levitsky +RH-Acked-by: Eduardo Habkost +RH-Acked-by: Stefan Hajnoczi + +From: Marc-André Lureau + +Various calls to snprintf() in libslirp assume that snprintf() returns +"only" the number of bytes written (excluding terminating NUL). + +https://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04 + +"Upon successful completion, the snprintf() function shall return the +number of bytes that would be written to s had n been sufficiently +large excluding the terminating null byte." + +Introduce slirp_fmt() that handles several pathological cases the +way libslirp usually expect: + +- treat error as fatal (instead of silently returning -1) + +- fmt0() will always \0 end + +- return the number of bytes actually written (instead of what would + have been written, which would usually result in OOB later), including + the ending \0 for fmt0() + +- warn if truncation happened (instead of ignoring) + + Other less common cases can still be handled with strcpy/snprintf() etc. + +Signed-off-by: Marc-André Lureau +Reviewed-by: Samuel Thibault +Message-Id: <20200127092414.169796-2-marcandre.lureau@redhat.com> + +Manually re-adapted from 30648c03b27fb8d9611b723184216cd3174b6775 +since cerry-pick cannot be used here. There is no util.c file in this +code version, so we add the two new functions as static functions in +the file where they are going to be used. + +Signed-off-by: Jon Maloy +Signed-off-by: Miroslav Rezanina +--- + slirp/tcp_subr.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 65 insertions(+) + +diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c +index 19e2245..e83575e 100644 +--- a/slirp/tcp_subr.c ++++ b/slirp/tcp_subr.c +@@ -44,6 +44,9 @@ + /* Don't do rfc1323 performance enhancements */ + #define TCP_DO_RFC1323 0 + ++static int slirp_fmt(char *str, size_t size, const char *format, ...); ++static int slirp_fmt0(char *str, size_t size, const char *format, ...); ++ + /* + * Tcp initialization + */ +@@ -935,3 +938,65 @@ int tcp_ctl(struct socket *so) + sb->sb_wptr += sb->sb_cc; + return 0; + } ++ ++static int slirp_vsnprintf(char *str, size_t size, ++ const char *format, va_list args) ++{ ++ int rv = vsnprintf(str, size, format, args); ++ ++ if (rv < 0) { ++ g_error("vsnprintf() failed: %s", g_strerror(errno)); ++ } ++ ++ return rv; ++} ++ ++/* ++ * A snprintf()-like function that: ++ * - returns the number of bytes written (excluding optional \0-ending) ++ * - dies on error ++ * - warn on truncation ++ */ ++static int slirp_fmt(char *str, size_t size, const char *format, ...) ++{ ++ va_list args; ++ int rv; ++ ++ va_start(args, format); ++ rv = slirp_vsnprintf(str, size, format, args); ++ va_end(args); ++ ++ if (rv > size) { ++ g_critical("vsnprintf() truncation"); ++ } ++ ++ return MIN(rv, size); ++} ++ ++/* ++ * A snprintf()-like function that: ++ * - always \0-end (unless size == 0) ++ * - returns the number of bytes actually written, including \0 ending ++ * - dies on error ++ * - warn on truncation ++ */ ++static int slirp_fmt0(char *str, size_t size, const char *format, ...) ++{ ++ va_list args; ++ int rv; ++ ++ va_start(args, format); ++ rv = slirp_vsnprintf(str, size, format, args); ++ va_end(args); ++ ++ if (rv >= size) { ++ g_critical("vsnprintf() truncation"); ++ if (size > 0) ++ str[size - 1] = '\0'; ++ rv = size; ++ } else { ++ rv += 1; /* include \0 */ ++ } ++ ++ return rv; ++} +-- +1.8.3.1 + diff --git a/SPECS/qemu-kvm.spec b/SPECS/qemu-kvm.spec index 78e86ee..3033163 100644 --- a/SPECS/qemu-kvm.spec +++ b/SPECS/qemu-kvm.spec @@ -76,7 +76,7 @@ Obsoletes: %1 < %{obsoletes_version} \ Summary: QEMU is a machine emulator and virtualizer Name: %{pkgname}%{?pkgsuffix} Version: 1.5.3 -Release: 173%{?dist} +Release: 173%{?dist}.1 # Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped Epoch: 10 License: GPLv2 and GPLv2+ and CC-BY @@ -4025,6 +4025,10 @@ Patch1983: kvm-tcp_emu-Fix-oob-access.patch Patch1984: kvm-slirp-use-correct-size-while-emulating-IRC-commands.patch # For bz#1791560 - CVE-2020-7039 qemu-kvm: QEMU: slirp: OOB buffer access while emulating tcp protocols in tcp_emu() [rhel-7.8] Patch1985: kvm-slirp-use-correct-size-while-emulating-commands.patch +# For bz#1798970 - CVE-2020-8608 qemu-kvm: QEMU: Slirp: potential OOB access due to unsafe snprintf() usages [rhel-7.8.z] +Patch1986: kvm-util-add-slirp_fmt-helpers.patch +# For bz#1798970 - CVE-2020-8608 qemu-kvm: QEMU: Slirp: potential OOB access due to unsafe snprintf() usages [rhel-7.8.z] +Patch1987: kvm-tcp_emu-fix-unsafe-snprintf-usages.patch BuildRequires: zlib-devel @@ -6188,6 +6192,8 @@ tar -xf %{SOURCE21} %patch1983 -p1 %patch1984 -p1 %patch1985 -p1 +%patch1986 -p1 +%patch1987 -p1 %build buildarch="%{kvm_target}-softmmu" @@ -6633,6 +6639,12 @@ sh %{_sysconfdir}/sysconfig/modules/kvm.modules &> /dev/null || : %{_mandir}/man8/qemu-nbd.8* %changelog +* Wed Mar 04 2020 Miroslav Rezanina - 1.5.3-173.el7_8.1 +- kvm-util-add-slirp_fmt-helpers.patch [bz#1798970] +- kvm-tcp_emu-fix-unsafe-snprintf-usages.patch [bz#1798970] +- Resolves: bz#1798970 + (CVE-2020-8608 qemu-kvm: QEMU: Slirp: potential OOB access due to unsafe snprintf() usages [rhel-7.8.z]) + * Thu Jan 23 2020 Miroslav Rezanina - 1.5.3-173.el7 - kvm-tcp_emu-Fix-oob-access.patch [bz#1791560] - kvm-slirp-use-correct-size-while-emulating-IRC-commands.patch [bz#1791560]