Skip to content

Commit

Permalink
app-emulation/qemu: various fixes/updates
Browse files Browse the repository at this point in the history
Sync in the updates from the 9999 ebuild:
 - updated seabios pin
 - add new targets
 - add sanity checks for targets

Add fix from upstream for blockcommit crashes #558396.

Add fix from upstream for CVE-2015-5225 #558416.

Add fix posted upstream (but not yet merged) for e1000 infinite loop #559656.
  • Loading branch information
vapier committed Sep 7, 2015
1 parent 479c793 commit fec6672
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 3 deletions.
86 changes: 86 additions & 0 deletions app-emulation/qemu/files/qemu-2.4.0-CVE-2015-5225.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
https://bugs.gentoo.org/558416

fix from upstream git

From eb8934b0418b3b1d125edddc4fc334a54334a49b Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <[email protected]>
Date: Mon, 17 Aug 2015 19:56:53 +0200
Subject: [PATCH] vnc: fix memory corruption (CVE-2015-5225)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The _cmp_bytes variable added by commit "bea60dd ui/vnc: fix potential
memory corruption issues" can become negative. Result is (possibly
exploitable) memory corruption. Reason for that is it uses the stride
instead of bytes per scanline to apply limits.

For the server surface is is actually fine. vnc creates that itself,
there is never any padding and thus scanline length always equals stride.

For the guest surface scanline length and stride are typically identical
too, but it doesn't has to be that way. So add and use a new variable
(guest_ll) for the guest scanline length. Also rename min_stride to
line_bytes to make more clear what it actually is. Finally sprinkle
in an assert() to make sure we never use a negative _cmp_bytes again.

Reported-by: 范祚至(库特) <[email protected]>
Reviewed-by: P J P <[email protected]>
Signed-off-by: Gerd Hoffmann <[email protected]>
---
ui/vnc.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/ui/vnc.c b/ui/vnc.c
index e26973a..caf82f5 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2872,7 +2872,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
pixman_image_get_width(vd->server));
int height = MIN(pixman_image_get_height(vd->guest.fb),
pixman_image_get_height(vd->server));
- int cmp_bytes, server_stride, min_stride, guest_stride, y = 0;
+ int cmp_bytes, server_stride, line_bytes, guest_ll, guest_stride, y = 0;
uint8_t *guest_row0 = NULL, *server_row0;
VncState *vs;
int has_dirty = 0;
@@ -2891,17 +2891,21 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
* Update server dirty map.
*/
server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
- server_stride = guest_stride = pixman_image_get_stride(vd->server);
+ server_stride = guest_stride = guest_ll =
+ pixman_image_get_stride(vd->server);
cmp_bytes = MIN(VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES,
server_stride);
if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
int width = pixman_image_get_width(vd->server);
tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
} else {
+ int guest_bpp =
+ PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb));
guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
guest_stride = pixman_image_get_stride(vd->guest.fb);
+ guest_ll = pixman_image_get_width(vd->guest.fb) * ((guest_bpp + 7) / 8);
}
- min_stride = MIN(server_stride, guest_stride);
+ line_bytes = MIN(server_stride, guest_ll);

for (;;) {
int x;
@@ -2932,9 +2936,10 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
continue;
}
- if ((x + 1) * cmp_bytes > min_stride) {
- _cmp_bytes = min_stride - x * cmp_bytes;
+ if ((x + 1) * cmp_bytes > line_bytes) {
+ _cmp_bytes = line_bytes - x * cmp_bytes;
}
+ assert(_cmp_bytes >= 0);
if (memcmp(server_ptr, guest_ptr, _cmp_bytes) == 0) {
continue;
}
--
2.5.0

124 changes: 124 additions & 0 deletions app-emulation/qemu/files/qemu-2.4.0-block-mirror-crash.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
https://bugs.gentoo.org/558396

fix from upstream git

From e424aff5f307227b1c2512bbb8ece891bb895cef Mon Sep 17 00:00:00 2001
From: Kevin Wolf <[email protected]>
Date: Thu, 13 Aug 2015 10:41:50 +0200
Subject: [PATCH] mirror: Fix coroutine reentrance

This fixes a regression introduced by commit dcfb3beb ("mirror: Do zero
write on target if sectors not allocated"), which was reported to cause
aborts with the message "Co-routine re-entered recursively".

The cause for this bug is the following code in mirror_iteration_done():

if (s->common.busy) {
qemu_coroutine_enter(s->common.co, NULL);
}

This has always been ugly because - unlike most places that reenter - it
doesn't have a specific yield that it pairs with, but is more
uncontrolled. What we really mean here is "reenter the coroutine if
it's in one of the four explicit yields in mirror.c".

This used to be equivalent with s->common.busy because neither
mirror_run() nor mirror_iteration() call any function that could yield.
However since commit dcfb3beb this doesn't hold true any more:
bdrv_get_block_status_above() can yield.

So what happens is that bdrv_get_block_status_above() wants to take a
lock that is already held, so it adds itself to the queue of waiting
coroutines and yields. Instead of being woken up by the unlock function,
however, it gets woken up by mirror_iteration_done(), which is obviously
wrong.

In most cases the code actually happens to cope fairly well with such
cases, but in this specific case, the unlock must already have scheduled
the coroutine for wakeup when mirror_iteration_done() reentered it. And
then the coroutine happened to process the scheduled restarts and tried
to reenter itself recursively.

This patch fixes the problem by pairing the reenter in
mirror_iteration_done() with specific yields instead of abusing
s->common.busy.

Cc: [email protected]
Signed-off-by: Kevin Wolf <[email protected]>
Reviewed-by: Paolo Bonzini <[email protected]>
Reviewed-by: Stefan Hajnoczi <[email protected]>
Reviewed-by: Jeff Cody <[email protected]>
Message-id: [email protected]
Signed-off-by: Jeff Cody <[email protected]>
---
block/mirror.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/block/mirror.c b/block/mirror.c
index 0841964..9474443 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -60,6 +60,7 @@ typedef struct MirrorBlockJob {
int sectors_in_flight;
int ret;
bool unmap;
+ bool waiting_for_io;
} MirrorBlockJob;

typedef struct MirrorOp {
@@ -114,11 +115,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
qemu_iovec_destroy(&op->qiov);
g_slice_free(MirrorOp, op);

- /* Enter coroutine when it is not sleeping. The coroutine sleeps to
- * rate-limit itself. The coroutine will eventually resume since there is
- * a sleep timeout so don't wake it early.
- */
- if (s->common.busy) {
+ if (s->waiting_for_io) {
qemu_coroutine_enter(s->common.co, NULL);
}
}
@@ -203,7 +200,9 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
/* Wait for I/O to this cluster (from a previous iteration) to be done. */
while (test_bit(next_chunk, s->in_flight_bitmap)) {
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
+ s->waiting_for_io = true;
qemu_coroutine_yield();
+ s->waiting_for_io = false;
}

do {
@@ -239,7 +238,9 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
*/
while (nb_chunks == 0 && s->buf_free_count < added_chunks) {
trace_mirror_yield_buf_busy(s, nb_chunks, s->in_flight);
+ s->waiting_for_io = true;
qemu_coroutine_yield();
+ s->waiting_for_io = false;
}
if (s->buf_free_count < nb_chunks + added_chunks) {
trace_mirror_break_buf_busy(s, nb_chunks, s->in_flight);
@@ -337,7 +338,9 @@ static void mirror_free_init(MirrorBlockJob *s)
static void mirror_drain(MirrorBlockJob *s)
{
while (s->in_flight > 0) {
+ s->waiting_for_io = true;
qemu_coroutine_yield();
+ s->waiting_for_io = false;
}
}

@@ -510,7 +513,9 @@ static void coroutine_fn mirror_run(void *opaque)
if (s->in_flight == MAX_IN_FLIGHT || s->buf_free_count == 0 ||
(cnt == 0 && s->in_flight > 0)) {
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
+ s->waiting_for_io = true;
qemu_coroutine_yield();
+ s->waiting_for_io = false;
continue;
} else if (cnt != 0) {
delay_ns = mirror_iteration(s);
--
2.5.0

39 changes: 39 additions & 0 deletions app-emulation/qemu/files/qemu-2.4.0-e1000-loop.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
https://bugs.gentoo.org/559656

https://lists.gnu.org/archive/html/qemu-devel/2015-09/msg01199.html

From: Stefan Hajnoczi <[email protected]>
Subject: [PATCH] e1000: Avoid infinite loop in processing transmit descriptor
Newsgroups: gmane.comp.emulators.qemu
Date: 2015-09-04 16:21:06 GMT (2 days, 12 hours and 51 minutes ago)
From: P J P <[email protected]>

While processing transmit descriptors, it could lead to an infinite
loop if 'bytes' was to become zero; Add a check to avoid it.

[The guest can force 'bytes' to 0 by setting the hdr_len and mss
descriptor fields to 0.
--Stefan]

Signed-off-by: P J P <[email protected]>
Signed-off-by: Stefan Hajnoczi <[email protected]>
---
hw/net/e1000.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 5c6bcd0..09c9e9d 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -740,7 +740,8 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
memmove(tp->data, tp->header, tp->hdr_len);
tp->size = tp->hdr_len;
}
- } while (split_size -= bytes);
+ split_size -= bytes;
+ } while (bytes && split_size);
} else if (!tp->tse && tp->cptse) {
// context descriptor TSE is not set, while data descriptor TSE is set
DBGOUT(TXERR, "TCP segmentation error\n");
--
2.4.3
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ virtfs +vnc vte xattr xen xfs"
COMMON_TARGETS="aarch64 alpha arm cris i386 m68k microblaze microblazeel mips
mips64 mips64el mipsel or32 ppc ppc64 s390x sh4 sh4eb sparc sparc64 unicore32
x86_64"
IUSE_SOFTMMU_TARGETS="${COMMON_TARGETS} lm32 moxie ppcemb xtensa xtensaeb"
IUSE_USER_TARGETS="${COMMON_TARGETS} armeb mipsn32 mipsn32el ppc64abi32 sparc32plus"
IUSE_SOFTMMU_TARGETS="${COMMON_TARGETS} lm32 moxie ppcemb tricore xtensa xtensaeb"
IUSE_USER_TARGETS="${COMMON_TARGETS} armeb mipsn32 mipsn32el ppc64abi32 ppc64le sparc32plus"

use_softmmu_targets=$(printf ' qemu_softmmu_targets_%s' ${IUSE_SOFTMMU_TARGETS})
use_user_targets=$(printf ' qemu_user_targets_%s' ${IUSE_USER_TARGETS})
Expand Down Expand Up @@ -134,7 +134,7 @@ USER_LIB_DEPEND="${COMMON_LIB_DEPEND}"
X86_FIRMWARE_DEPEND="
>=sys-firmware/ipxe-1.0.0_p20130624
pin-upstream-blobs? (
~sys-firmware/seabios-1.7.5
~sys-firmware/seabios-1.8.2
~sys-firmware/sgabios-0.1_pre8
~sys-firmware/vgabios-0.7a
)
Expand Down Expand Up @@ -268,7 +268,29 @@ pkg_setup() {
enewgroup kvm 78
}

# Sanity check to make sure target lists are kept up-to-date.
check_targets() {
local var=$1 mak=$2
local detected sorted

pushd "${S}"/default-configs >/dev/null || die

detected=$(echo $(printf '%s\n' *-${mak}.mak | sed "s:-${mak}.mak::" | sort -u))
sorted=$(echo $(printf '%s\n' ${!var} | sort -u))
if [[ ${sorted} != "${detected}" ]] ; then
eerror "The ebuild needs to be kept in sync."
eerror "${var}: ${sorted}"
eerror "$(printf '%-*s' ${#var} configure): ${detected}"
die "sync ${var} to the list of targets"
fi

popd >/dev/null
}

src_prepare() {
check_targets IUSE_SOFTMMU_TARGETS softmmu
check_targets IUSE_USER_TARGETS linux-user

# Alter target makefiles to accept CFLAGS set via flag-o
sed -i -r \
-e 's/^(C|OP_C|HELPER_C)FLAGS=/\1FLAGS+=/' \
Expand All @@ -278,6 +300,9 @@ src_prepare() {
use nls || rm -f po/*.po

epatch "${FILESDIR}"/qemu-1.7.0-cflags.patch
epatch "${FILESDIR}"/${P}-block-mirror-crash.patch #558396
epatch "${FILESDIR}"/${P}-CVE-2015-5225.patch #558416
epatch "${FILESDIR}"/${PN}-2.4.0-e1000-loop.patch #559656
[[ -n ${BACKPORTS} ]] && \
EPATCH_FORCE=yes EPATCH_SUFFIX="patch" EPATCH_SOURCE="${S}/patches" \
epatch
Expand Down

0 comments on commit fec6672

Please sign in to comment.