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

qdrawhelper_p.h: Duff's devices do eight steps when asked to do zero steps (buffer overflow)

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P3: Somewhat important
    • 5.13.0 Alpha 1
    • 4.7.1
    • GUI: Painting
    • None
    • Windows XP
    • 185f9e0758cd7ee649f42b5c788fdfff6031d83c (qt/qtbase/dev)

    Description

      There are four instances of "Duff's device" in qdrawhelper_p.h. Three of them are labeled as such. I will show the fourth as an example:

      template <class T>
      inline void qt_memfill(T *dest, T value, int count)
      {
      int n = (count + 7) / 8;
      switch (count & 0x07)
      {
      case 0: do { *dest++ = value;
      case 7: *dest++ = value;
      case 6: *dest++ = value;
      case 5: *dest++ = value;
      case 4: *dest++ = value;
      case 3: *dest++ = value;
      case 2: *dest++ = value;
      case 1: *dest++ = value;
      } while (--n > 0);
      }
      }

      If this is called with count==0, then because (0 & 0x07)==0, the switch will jump to case 0 and thus fill eight elements, as if count had been 8. The do-while doesn't prevent this, of course, because do-while always executes its body at least once.

      The same problem (effectively a buffer overflow) exists in all four Duff's devices in this file.

      This has occassionally caused access violations in an older version of our product, using Qt 4.5.1, when a buffer was located immediately before a page boundary and then overflowed into an unassigned address range. We didn't observe any other effects, but a buffer overflow usually does no good.

      The crash we observed was in gui\painting\qblendfunctions.cpp, in the function:
      static void qt_blend_rgb16_on_rgb16(uchar *dst, int dbpl,
      const uchar *src, int sbpl,
      int w, int h,
      int const_alpha)
      in the line calling:
      QT_MEMCPY_USHORT(dst, src, w);
      (which contains Duff's device) with w==0.

      The call to func = qt_blend_rgb16_on_rgb16 originated from:
      void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
      const QImage &img,
      SrcOverBlendFunc func,
      const QRect &clip,
      int alpha,
      const QRect &sr)

      We are now using Qt 4.6.2, and in that version, QRasterPaintEnginePrivate::drawImage avoids calling func with w==0, because the code from Qt 4.5.1:
      if (iw < 0)
      return;
      has been changed to:
      if (iw <= 0)
      return;
      somewhere between those two versions of Qt.

      I don't know if this was done to fix that particular crash, or if it was just an optimization.

      This issue in qt_memfill has been fixed with the commit 500f28f04a08e37525e3d1deb5cfe6e908c66b15, however it would be safer to check (count<=0) than just (!count).
      A negative count would cause a buffer overflow of 1 to 8 bytes depending on the last 3 bits of count. While all current callers might be avoiding this, I would not rely on it in the future. The checks for ==0 and <=0 should perform equally fast.

      the other three Duff's devices still have the same problem:

      template <class DST, class SRC>
      inline void qt_memconvert(DST *dest, const SRC *src, int count)
      See the else branch.

      #define QT_MEMCPY_REV_UINT(dest, src, length)

      #define QT_MEMCPY_USHORT(dest, src, length)

      I therefore kindly ask you to re-open the case and
      1. fix the remaining three instances of Duff's device, and
      2. consider checking (count<=0) instead of (!count) in qt_memfill.

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            thiago Thiago Macieira
            mawiesma Martin Wiesmann (closed Nokia identity) (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes