Message ID | 4D6DC06B.6070308@cn.fujitsu.com |
---|---|
State | New |
Headers | show |
On Wed, Mar 2, 2011 at 3:58 AM, Wen Congyang <wency@cn.fujitsu.com> wrote: > This bug is reported by Stefan Weil: > ======== > Commit bc2429b9174ac2d3c56b7fd35884b0d89ec7fb02 introduced > a severe bug (heap corruption). > > bitmap_clear was called with a wrong argument > which caused out-of-bound writes to width_mask. > > This bug was detected with QEMU running on windows. > It also occurs with wine: > > *** stack smashing detected ***: terminated > wine: Unhandled illegal instruction at address 0x6115c7 (thread 0009), starting debugger... > > The bug is not windows specific! > ======== > > The third argument of bitmap_clear() is number of bits to be cleared, but we pass > the end bits to be cleared to bitmap_clear(). > > Signed-off-by: Wen Congyang <wency@cn.fujitsu.com> > Reported-by: Stefan Weil <weil@mail.berlios.de> Acked-by: Corentin Chary <corentin.chary@gmail.com> > --- > ui/vnc.c | 6 +++--- > 1 files changed, 3 insertions(+), 3 deletions(-) > > diff --git a/ui/vnc.c b/ui/vnc.c > index e3761b0..e7d0b5b 100644 > --- a/ui/vnc.c > +++ b/ui/vnc.c > @@ -2390,6 +2390,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd) > unsigned long width_mask[VNC_DIRTY_WORDS]; > VncState *vs; > int has_dirty = 0; > + const size_t width = ds_get_width(vd->ds) / 16; > > struct timeval tv = { 0, 0 }; > > @@ -2403,9 +2404,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd) > * Check and copy modified bits from guest to server surface. > * Update server dirty map. > */ > - bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); > - bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), > - VNC_DIRTY_WORDS * BITS_PER_LONG); > + bitmap_set(width_mask, 0, width); > + bitmap_clear(width_mask, width, VNC_DIRTY_WORDS * BITS_PER_LONG - width); > cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds); > guest_row = vd->guest.ds->data; > server_row = vd->server->data; > -- > 1.7.1 > >
Am 02.03.2011 11:57, schrieb Corentin Chary: > On Wed, Mar 2, 2011 at 3:58 AM, Wen Congyang <wency@cn.fujitsu.com> wrote: >> This bug is reported by Stefan Weil: >> ======== >> Commit bc2429b9174ac2d3c56b7fd35884b0d89ec7fb02 introduced >> a severe bug (heap corruption). >> >> bitmap_clear was called with a wrong argument >> which caused out-of-bound writes to width_mask. >> >> This bug was detected with QEMU running on windows. >> It also occurs with wine: >> >> *** stack smashing detected ***: terminated >> wine: Unhandled illegal instruction at address 0x6115c7 (thread >> 0009), starting debugger... >> >> The bug is not windows specific! >> ======== >> >> The third argument of bitmap_clear() is number of bits to be cleared, >> but we pass >> the end bits to be cleared to bitmap_clear(). >> >> Signed-off-by: Wen Congyang <wency@cn.fujitsu.com> >> Reported-by: Stefan Weil <weil@mail.berlios.de> > > Acked-by: Corentin Chary <corentin.chary@gmail.com> > No. I dont't think that the third parameter of bitmap_clear is ok like that. See my patch for the correct value. My own patch is also incomplete, so I'll send an update. Stefan >> --- >> ui/vnc.c | 6 +++--- >> 1 files changed, 3 insertions(+), 3 deletions(-) >> >> diff --git a/ui/vnc.c b/ui/vnc.c >> index e3761b0..e7d0b5b 100644 >> --- a/ui/vnc.c >> +++ b/ui/vnc.c >> @@ -2390,6 +2390,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd) >> unsigned long width_mask[VNC_DIRTY_WORDS]; >> VncState *vs; >> int has_dirty = 0; >> + const size_t width = ds_get_width(vd->ds) / 16; >> >> struct timeval tv = { 0, 0 }; >> >> @@ -2403,9 +2404,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd) >> * Check and copy modified bits from guest to server surface. >> * Update server dirty map. >> */ >> - bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); >> - bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), >> - VNC_DIRTY_WORDS * BITS_PER_LONG); >> + bitmap_set(width_mask, 0, width); >> + bitmap_clear(width_mask, width, VNC_DIRTY_WORDS * BITS_PER_LONG - width); >> cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds); >> guest_row = vd->guest.ds->data; >> server_row = vd->server->data; >> -- >> 1.7.1 >> >> > > >
On 2 March 2011 18:36, Stefan Weil <weil@mail.berlios.de> wrote: > No. I dont't think that the third parameter of bitmap_clear is > ok like that. See my patch for the correct value. Wen's patch: + const size_t width = ds_get_width(vd->ds) / 16; [...] - bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); - bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), - VNC_DIRTY_WORDS * BITS_PER_LONG); + bitmap_set(width_mask, 0, width); + bitmap_clear(width_mask, width, VNC_DIRTY_WORDS * BITS_PER_LONG - width); Your patch: bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), - VNC_DIRTY_WORDS * BITS_PER_LONG); + (VNC_MAX_WIDTH - ds_get_width(vd->ds)) / 16); Since ui/vnc.h has: #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * BITS_PER_LONG)) the third parameter to bitmap_clear is the same value in both cases, isn't it? Or is this a rounding bug? -- PMM
Am 02.03.2011 19:47, schrieb Peter Maydell: > On 2 March 2011 18:36, Stefan Weil <weil@mail.berlios.de> wrote: >> No. I dont't think that the third parameter of bitmap_clear is >> ok like that. See my patch for the correct value. > > Wen's patch: > > + const size_t width = ds_get_width(vd->ds) / 16; > [...] > - bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); > - bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), > - VNC_DIRTY_WORDS * BITS_PER_LONG); > + bitmap_set(width_mask, 0, width); > + bitmap_clear(width_mask, width, VNC_DIRTY_WORDS * BITS_PER_LONG - > width); > > Your patch: > > bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), > - VNC_DIRTY_WORDS * BITS_PER_LONG); > + (VNC_MAX_WIDTH - ds_get_width(vd->ds)) / 16); > > Since ui/vnc.h has: > > #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * BITS_PER_LONG)) > > the third parameter to bitmap_clear is the same value in > both cases, isn't it? Or is this a rounding bug? > > -- PMM Because of rounding effects, both values can be different. The part missing in my patch is correct handling of another rounding effect: VNC_DIRTY_WORDS is exact for 32 bit long values (and the "old" code which used uint32_t until some weeks ago), where VNC_DIRTY_WORDS = 2560/16/32 = 5. For 64 bit values, VNC_DIRTY_WORDS = 2560/16/64 = 2 (rounded)! Stefan W.
Am 02.03.2011 23:01, schrieb Stefan Weil: > Am 02.03.2011 19:47, schrieb Peter Maydell: >> On 2 March 2011 18:36, Stefan Weil <weil@mail.berlios.de> wrote: >>> No. I dont't think that the third parameter of bitmap_clear is >>> ok like that. See my patch for the correct value. >> >> Wen's patch: >> >> + const size_t width = ds_get_width(vd->ds) / 16; >> [...] >> - bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); >> - bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), >> - VNC_DIRTY_WORDS * BITS_PER_LONG); >> + bitmap_set(width_mask, 0, width); >> + bitmap_clear(width_mask, width, VNC_DIRTY_WORDS * BITS_PER_LONG >> - width); >> >> Your patch: >> >> bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), >> - VNC_DIRTY_WORDS * BITS_PER_LONG); >> + (VNC_MAX_WIDTH - ds_get_width(vd->ds)) / 16); >> >> Since ui/vnc.h has: >> >> #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * BITS_PER_LONG)) >> >> the third parameter to bitmap_clear is the same value in >> both cases, isn't it? Or is this a rounding bug? >> >> -- PMM > > Because of rounding effects, both values can be different. > > The part missing in my patch is correct handling of another > rounding effect: > > VNC_DIRTY_WORDS is exact for 32 bit long values (and the > "old" code which used uint32_t until some weeks ago), where > VNC_DIRTY_WORDS = 2560/16/32 = 5. > > For 64 bit values, VNC_DIRTY_WORDS = 2560/16/64 = 2 (rounded)! > > Stefan W. Is bitmap_clear() really needed here? Meanwhile I think it is not, so this might be a new patch variant...
On 2 March 2011 22:01, Stefan Weil <weil@mail.berlios.de> wrote: > The part missing in my patch is correct handling of another > rounding effect: > > VNC_DIRTY_WORDS is exact for 32 bit long values (and the > "old" code which used uint32_t until some weeks ago), where > VNC_DIRTY_WORDS = 2560/16/32 = 5. > > For 64 bit values, VNC_DIRTY_WORDS = 2560/16/64 = 2 (rounded)! Yes, I noticed that after I posted. Given that we have arrays like unsigned long dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; rounding down rather than up looks a bit suspicious... (Can we just make VNC_MAX_WIDTH a multiple of 64, or is it baked into the VNC protocol?) -- PMM
At 03/03/2011 06:27 AM, Stefan Weil Write: > Am 02.03.2011 23:01, schrieb Stefan Weil: >> Am 02.03.2011 19:47, schrieb Peter Maydell: >>> On 2 March 2011 18:36, Stefan Weil <weil@mail.berlios.de> wrote: >>>> No. I dont't think that the third parameter of bitmap_clear is >>>> ok like that. See my patch for the correct value. >>> >>> Wen's patch: >>> >>> + const size_t width = ds_get_width(vd->ds) / 16; >>> [...] >>> - bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); >>> - bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), >>> - VNC_DIRTY_WORDS * BITS_PER_LONG); >>> + bitmap_set(width_mask, 0, width); >>> + bitmap_clear(width_mask, width, VNC_DIRTY_WORDS * BITS_PER_LONG >>> - width); >>> >>> Your patch: >>> >>> bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), >>> - VNC_DIRTY_WORDS * BITS_PER_LONG); >>> + (VNC_MAX_WIDTH - ds_get_width(vd->ds)) / 16); >>> >>> Since ui/vnc.h has: >>> >>> #define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * BITS_PER_LONG)) >>> >>> the third parameter to bitmap_clear is the same value in >>> both cases, isn't it? Or is this a rounding bug? >>> >>> -- PMM >> >> Because of rounding effects, both values can be different. >> >> The part missing in my patch is correct handling of another >> rounding effect: >> >> VNC_DIRTY_WORDS is exact for 32 bit long values (and the >> "old" code which used uint32_t until some weeks ago), where >> VNC_DIRTY_WORDS = 2560/16/32 = 5. >> >> For 64 bit values, VNC_DIRTY_WORDS = 2560/16/64 = 2 (rounded)! >> >> Stefan W. > > > Is bitmap_clear() really needed here? Meanwhile I think it is not, > so this might be a new patch variant... I do not know why we call bitmap_clear() hear. I only know it is the same as the old code: -static inline void vnc_set_bits(uint32_t *d, int n, int nb_words) -{ - int j; - - j = 0; - while (n >= 32) { - d[j++] = -1; - n -= 32; - } - if (n > 0) - d[j++] = (1 << n) - 1; - while (j < nb_words) <=== bitmap_clear() - d[j++] = 0; -} - vnc_set_bits(width_mask, (ds_get_width(vd->ds) / 16), VNC_DIRTY_WORDS); + bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); + bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), + VNC_DIRTY_WORDS * BITS_PER_LONG); > > >
======== Commit bc2429b9174ac2d3c56b7fd35884b0d89ec7fb02 introduced a severe bug (heap corruption). bitmap_clear was called with a wrong argument which caused out-of-bound writes to width_mask. This bug was detected with QEMU running on windows. It also occurs with wine: *** stack smashing detected ***: terminated wine: Unhandled illegal instruction at address 0x6115c7 (thread 0009), starting debugger... The bug is not windows specific! ======== The third argument of bitmap_clear() is number of bits to be cleared, but we pass the end bits to be cleared to bitmap_clear(). Signed-off-by: Wen Congyang <wency@cn.fujitsu.com> Reported-by: Stefan Weil <weil@mail.berlios.de> --- ui/vnc.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index e3761b0..e7d0b5b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2390,6 +2390,7 @@ static int vnc_refresh_server_surface(VncDisplay *vd) unsigned long width_mask[VNC_DIRTY_WORDS]; VncState *vs; int has_dirty = 0; + const size_t width = ds_get_width(vd->ds) / 16; struct timeval tv = { 0, 0 }; @@ -2403,9 +2404,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd) * Check and copy modified bits from guest to server surface. * Update server dirty map. */ - bitmap_set(width_mask, 0, (ds_get_width(vd->ds) / 16)); - bitmap_clear(width_mask, (ds_get_width(vd->ds) / 16), - VNC_DIRTY_WORDS * BITS_PER_LONG); + bitmap_set(width_mask, 0, width); + bitmap_clear(width_mask, width, VNC_DIRTY_WORDS * BITS_PER_LONG - width); cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds); guest_row = vd->guest.ds->data; server_row = vd->server->data;