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

Out-of-bounds string read in lupdate translationAttempt

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P1: Critical
    • 6.5.1, 6.6.0
    • 6.4.2
    • Tools: Linguist
    • None
    • All
    • 811b7c2f7 (dev), 5b954c016 (6.5)

    Description

      When lupdate -no-obsolete with assertions enabled, an assertion can fail:

      /* elided abort frames */
      frame #6: 0x0000000101a19b87 libQt6Core_debug.6.dylib`qt_assert(assertion="i >= 0 && i < size()", file="qt6/include/QtCore/qstring.h", line=1357) at qglobal.cpp:3276:41
      frame #7: 0x00000001000804b3 lupdate`QString::operator[](this=0x000060700018a560, i=2) at qstring.h:1357:3
      frame #8: 0x0000000100277e03 lupdate`translationAttempt(oldTranslation=0x000060b00005bf20, oldSource=0x000060b00005bf08, newSource=0x00007ffeefbf7dd0) at merge.cpp:109:38
      frame #9: 0x00000001002764b6 lupdate`applyNumberHeuristic(tor=0x00007ffeefbfad40) at merge.cpp:210:40
      frame #10: 0x000000010027dc83 lupdate`merge(tor=0x00007ffeefbfa5d0, virginTor=0x00007ffeefbfdd90, aliens=0x00007ffeefbf9f80, options=(i = 627), err=0x00007ffeefbfa380) at merge.cpp:499:66
      frame #11: 0x000000010024b6b8 lupdate`updateTsFiles(fetchedTor=0x00007ffeefbfdd90, tsFileNames=0x00007ffeefbfba90, alienFiles=0x00007ffeefbfbc90, sourceLanguage=0x00007ffeefbfbd10, targetLanguage=0x00007ffeefbfbcd0, options=(i = 627), fail=0x00007ffeefbfdd40) at main.cpp:419:26
      frame #12: 0x000000010024250d lupdate`main(argc=7, argv=0x00007ffeefbff600) at main.cpp:1064:9

      An example of a frame variables state at the assertion is:

      oldTranslation = "%1MiB"
      oldSource = "%1MiB"
      newSource = "%1KiB"
      oldNumbers = [ u"1M" ]
      newNumbers = [ u"1K" ]
      met = [ false ]
      matchedYet = [ 2 ]
      p = 1
      i = 3
      j = 5
      k = 0
      ell = -272664080
      best = 1
      m = 0
      n = 0
      pass = 2
      attempt = "%1Mi"

      As can be seen, oldNumbers[0][2] is not a valid index. Since matchedYet is only cleared at the end of each iteration if best != p, and there is no bounds check prior to reading from oldNumbers[k], and the whole string was exhausted during a previous iteration, the overread occurs.

      When assertions are disabled, this code will compare against the null terminator, so whilst it is violating the contract of QString, it is deterministic what will happen (the character will not match and the value of matchedYet[k] will reset to 0).

      It seems to me like the end of the i-loop should be resetting all matchedYet[k] where oldNumbers[k].size() == matchedYet[k], since otherwise it is guaranteed to break on the next loop.

      This particular example of a number match is failing only because the data in oldNumbers and newNumbers is copying the character after the number too, which seems like a separate defect.

      Attachments

        For Gerrit Dashboard: QTBUG-111775
        # Subject Branch Project Status CR V

        Activity

          People

            jbornema Joerg Bornemann
            csnover C S
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes