Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-23584

Buffer overrun in function QGLGlyphCache::cacheGlyphs(...)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P1: Critical
    • 4.8.3
    • 4.8.0
    • GUI: OpenGL
    • None
    • 9520674b0f02aac55da6d98a6c56c74ce240cca6

    Description

      I discovered buffer overrun in function QGLGlyphCache::cacheGlyphs(...) in qpaintengine_opengl.cpp.
      There present two equal if statement:

      if (font_tex->x_offset + glyph_width + x_margin > font_tex->width)
      

      First if statement contain logic for enlarging size of memory available for texture with glyphs.

      if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
      	int strip_height = qt_next_power_of_two(qRound(fontEngine->ascent().toReal() + fontEngine->descent().toReal())+2);
      	font_tex->x_offset = x_margin;
      	font_tex->y_offset += strip_height;
      	if (font_tex->y_offset >= font_tex->height) {
      		...
      		font_tex->height = qt_next_power_of_two(font_tex->height + strip_height);
      		allocTexture(font_tex);
      		...
      	}
      }
      

      Second if statement contain logic for enlarging x_offset and y_offset:

      if (font_tex->x_offset + glyph_width + x_margin > font_tex->width) {
      	font_tex->x_offset = x_margin;
      	font_tex->y_offset += glyph_height + y_margin;
      }...
      

      When x_offset and y_offset is enlarged in this if statement we have to be sure that texture has enough memory. But between two this statement value of glyph_width may be changed (in our case its value grow up):

      QImage glyph_im(fontEngine->alphaMapForGlyph(glyphs[i]));
      glyph_width = glyph_im.width();
      Q_ASSERT(glyph_width >= 0);
      // pad the glyph width to an even number
      if (glyph_width%2 != 0)
      	++glyph_width;
      

      This change in glyph_width lead to situation when x_offset and y_offset is changed but size of memory for texture wasn't enlarged. When new glyph is added into texture we get buffer overrun on copying glyph's data into texture:

      if (!glyph_im.isNull()) {
      	...
      	int cacheLineStart = (font_tex->x_offset + font_tex->y_offset*font_tex->width)*2;
      	for (int y=0; y<glyph_im.height(); ++y) {
      		...
      		// update cache
      		memcpy(font_tex->data+cacheLineStart, tex_data+lineStart, glyph_width*2);
      		cacheLineStart += font_tex->width*2;
      	}
      	...
      }
      

      On the attached image You can see how memcpy overwrote inner value of qt_font_textures:
      On image y_offset equal to 14 while height only 16. With this value we need only two cycle iteration to achieve cacheLineStart greater than texture's memory size.

      If in first if statement we take account of that glyph_width may be changed on greater value we can use the greatest value at once for avoid buffer overrun. I attached patch which allowed us to solve this problem.

      Attachments

        1. buffer_overrun.PNG
          buffer_overrun.PNG
          163 kB
        2. GLGlyphCacheCrash.zip
          1 kB
        3. glyph.patch
          3 kB
        For Gerrit Dashboard: QTBUG-23584
        # Subject Branch Project Status CR V

        Activity

          People

            jiang Jiang Jiang
            isaev ilya Isaev Ilya
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes