Details
-
Bug
-
Resolution: Fixed
-
P1: Critical
-
6.4.2
-
None
-
-
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 |
465584,1 | Only compare in-bounds areas of number strings | dev | qt/qttools | Status: ABANDONED | -2 | 0 |
469496,3 | lupdate: Fix assert when applying number heuristics | dev | qt/qttools | Status: MERGED | +2 | 0 |
470275,2 | lupdate: Fix assert when applying number heuristics | 6.5 | qt/qttools | Status: MERGED | +2 | 0 |