Title: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 06:42:58 PM Bug 1. Here is what I apperently did to cause the bug:
Step 1. Open bitcoin 0.5, Close bitcoin 0.5 Step 2. Open bitcoin 0.4, Close bitcoin 0.4 Step 3. Open bitcoin 0.5. It now shows: "Last received block was generated %n seconds ago." when holding mouse cursor over the green check mark in the lower right corner. Bug 2 or a feature request: Click on the tray icon should open the bitcoin gui and make it come to front. Possible breakdown: I noticed how the blocks seem to be coming in today at a much higher rate than 10 minutes ? Maybe I just imagined it, but there is also some evidence which could suggest the maximum difficult has already been overrun by computer processing power: According to this link, the maximum difficult is: https://en.bitcoin.it/wiki/Difficulty 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / 0x00000000000404CB000000000000000000000000000000000000000000000000 = 16307.420938523983 Currently the maximum difficult according to this link is 1192497.7500895 http://blockexplorer.com/q/getdifficulty The current difficulty therefore seems to exceed the theoretical maximum difficulty ?!? Does this mean bitcoin is already broken ?! ;) (and would need to change to a different hashing method ?!?) Also what could explain bug 1 it was working before, but now it seems to display %n, maybe it's a C string routine attack ? Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: SgtSpike on November 22, 2011, 06:45:52 PM You completely misread that link regarding difficulty. Reread this:
Code: The highest possible target (difficulty 1) is defined as 0x1d00ffff, which gives us a hex target of It doesn't say max difficulty is 16307. That's just an example. Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: casascius on November 22, 2011, 06:47:26 PM According to this link, the maximum difficult is: https://en.bitcoin.it/wiki/Difficulty 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / 0x00000000000404CB000000000000000000000000000000000000000000000000 = 16307.420938523983 This isn't what the article appears to say. It appears to say that difficulty 16307 corresponds to an example encoding they gave of 0x1b0404cb, not that this was the maximum. An off the cuff calculation would say the maximum difficulty is something around 2^224, which is a fair bit bigger than 16307. Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: DeathAndTaxes on November 22, 2011, 06:52:32 PM When you have 10 posts ... read more and post less. :D
Block time AVERAGES 10 minutes. I have seen <1 minute blocks and I have seen an over 3 hour block. I am sure those aren't the upper and lower bounds of what has been observed either. The network is actually running slightly slower than expected (slightly lower than 10 minutes per block) over the last 120 blocks indicating hashing power is declining not increasing. http://dot-bit.org/tools/nextDifficulty.php Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 06:54:51 PM Ok, thanks guys in pointing out my misread ;) :)
Ok update on the bug 1: It now displays: "Last received block was generated 29 seconds ago." I do wonder why it would first display %n and not simply zero (or garbage for that matter if it was not initialized) ? I worry a little bit that it might cause some kind of attack, but since this is probably used only locally and might not be influenced by network traffic perhaps the threat is low to non-existent (code will have to be examined to see if it is exploitable ;)), but it does worry me just a little bit, not so much to get my coins stolen since I have none, but more for a buffer overrun possibility leading to a system attack, destroying my software installation ! ;) Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 07:11:28 PM The bug seems to be coming from this piece of code from the file src\qt\bitcoingui.cpp:
Line 405: void BitcoinGUI::setNumBlocks(int count) { if(!clientModel) return; int initTotal = clientModel->getNumBlocksAtStartup(); int total = clientModel->getNumBlocksOfPeers(); QString tooltip; if(count < total) { progressBarLabel->setVisible(true); progressBar->setVisible(true); progressBar->setMaximum(total - initTotal); progressBar->setValue(count - initTotal); tooltip = tr("Downloaded %1 of %2 blocks of transaction history.").arg(count).arg(total); } else { progressBarLabel->setVisible(false); progressBar->setVisible(false); tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); } QDateTime now = QDateTime::currentDateTime(); QDateTime lastBlockDate = clientModel->getLastBlockDate(); int secs = lastBlockDate.secsTo(now); QString text; // Represent time from last generated block in human readable text if(secs < 60) { text = tr("%n second(s) ago","",secs); } else if(secs < 60*60) { text = tr("%n minute(s) ago","",secs/60); } else if(secs < 24*60*60) { text = tr("%n hour(s) ago","",secs/(60*60)); } else { text = tr("%n day(s) ago","",secs/(60*60*24)); } // Set icon state: spinning if catching up, tick otherwise if(secs < 30*60) { tooltip = tr("Up to date") + QString("\n") + tooltip; labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); } else { tooltip = tr("Catching up...") + QString("\n") + tooltip; labelBlocksIcon->setMovie(syncIconMovie); syncIconMovie->start(); } tooltip += QString("\n"); tooltip += tr("Last received block was generated %1.").arg(text); labelBlocksIcon->setToolTip(tooltip); progressBarLabel->setToolTip(tooltip); progressBar->setToolTip(tooltip); } For now I have not found the "tr()" routine which apperently does something with these parameters and could be the source of the bug. It seems to be passed a "secs" parameter which might be zero, garbage or not initialized. It seems to come from: int secs = lastBlockDate.secsTo(now); Perhaps lastBlockDate is also not initialized or contains garbage I don't know yet. Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 07:17:22 PM The tr routine seems to be a QT routine (gui framework), which bitcoin switched to in 0.5:
http://doc.qt.nokia.com/qq/qq03-swedish-chef.html Here is short description of it from that link: " Qt's tr() mechanism for internationalization is easy to grasp, easy to use, and easy to misuse. This article gives some tips to ensure that all of an application's user-visible strings go through tr(), and that lupdate finds them all. Monolingual application developers are not left out: This article also talks about QRegExp, <BLINK>XML</BLINK>, and Swedish. " ^ Sounds kinda complex, so perhaps there is some juicy bug in it to exploit ;) :) Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 07:42:25 PM QT is apperently Nokia technology, it also seems to encompass much more than just a GUI.
http://qt.nokia.com/downloads/sdk-windows-cpp The framework seems rather large, the download seems to be 15 MB but could expand into multiple gigabytes, I am not sure what the installer is going to do, maybe it will download even more. The minimum size to install seems to be 200 MB for just the sources, also I am not sure what the installer is going to do to my system, so I am going to abort for now ;) However there is another possibility to try and find the TR function which is apperently a static function/routine somewhere... by using an online repository: http://qt.gitorious.org/qt/qt/trees/4.8/src It doesn't seem to have a source-search functionality, but perhaps I will get lucky and find the TR function somewhere ;) It seems rather large to search manually. So for now my search stops here ;) (Also I am not sure which version of QT bitcoin 0.5 was compiled with, but for now I will assume the latest version ;) :)) Perhaps I don't need to search the source, reading documentation about TR now: http://doc.qt.nokia.com/stable/i18n-source-translation.html Ok read it, my guess is TR stands for "translation". It's a way for the programmer to indicate that the parameters passed to TR can/could be replaced by a special source processing tool called "lupdate". lupdate apperently searches some translation files and replaces the TR function calls with language translation strings. Still a bit vague what it exactly does, so further examination of TR or perhaps even lupdate required. Looking back at the code, perhaps the problem is with this line and not so much the secs, since that seems to be initialized properly: tr("Last received block was generated %1.").arg(text); There is another thing at play here: the .arg(text) specifier... not exactly sure what this does... the %1 looks like a parameter index specifier. It's probably some kind of macro/functionality or indicator to replace %1 with text argument. My best bet is this piece of code is executed to create the text string at runtime: else { text = tr("%n day(s) ago","",secs/(60*60*24)); } To me it seems kinda of mysterious why text is not simply replaced by some value... the calculation: secs/(60*60*24) seems to produce a somewhat valid integer, even if secs would contain garbage... perhaps secs contains zero by default which could explain that zero is passed to tr, maybe tr has an issue with an integer of zero. Then again it could also be a massive overflow, or perhaps a lucky garbage situation to trigger the massive overflow or out of range situation causing TR to somehow fail. Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 08:02:36 PM Time to dive into this a little bit deeper, the documentation gives a hint where this TR might be found, though many overrides could also apply:
" QString QObject::tr ( const char * sourceText, const char * disambiguation = 0, int n = -1 ) [static] Returns a translated version of sourceText, optionally based on a disambiguation string and value of n for strings containing plurals; otherwise returns sourceText itself if no appropriate translated string is available. Example: void MainWindow::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); ... If the same sourceText is used in different roles within the same context, an additional identifying string may be passed in disambiguation (0 by default). In Qt 4.4 and earlier, this was the preferred way to pass comments to translators. Example: MyWindow::MyWindow() { QLabel *senderLabel = new QLabel(tr("Name:")); QLabel *recipientLabel = new QLabel(tr("Name:", "recipient")); ... See Writing Source Code for Translation for a detailed description of Qt's translation mechanisms in general, and the Disambiguation section for information on disambiguation. Warning: This method is reentrant only if all translators are installed before calling this method. Installing or removing translators while performing translations is not supported. Doing so will probably result in crashes or other undesirable behavior. See also trUtf8(), QApplication::translate(), QTextCodec::setCodecForTr(), and Internationalization with Qt. " So it seems to be located in QObject classes. and QString seems to be the returned typed... so that class could also be inspected for vunerabilities ;) Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 08:15:43 PM qobject.h and qobject.cpp can be found here:
http://qt.gitorious.org/qt/qt/trees/4.8/src/corelib/kernel qobject.h has some interesting default values for the parameters: " 124 #ifdef qdoc 125 static QString tr(const char *sourceText, const char *comment = 0, int n = -1); 126 static QString trUtf8(const char *sourceText, const char *comment = 0, int n = -1); 127 virtual const QMetaObject *metaObject() const; 128 static const QMetaObject staticMetaObject; 129 #endif 130 #ifdef QT_NO_TRANSLATION 131 static QString tr(const char *sourceText, const char *, int) 132 { return QString::fromLatin1(sourceText); } 133 static QString tr(const char *sourceText, const char * = 0) 134 { return QString::fromLatin1(sourceText); } 135 #ifndef QT_NO_TEXTCODEC 136 static QString trUtf8(const char *sourceText, const char *, int) 137 { return QString::fromUtf8(sourceText); } 138 static QString trUtf8(const char *sourceText, const char * = 0) 139 { return QString::fromUtf8(sourceText); } 140 #endif 141 #endif //QT_NO_TRANSLATION " Now going to examine the cpp file ;) Hmm the CPP file does not seem to contain an implementation for TR, I am not sure why, perhaps TR is just a place holder/fake in a way and gets replaced by the lupdate tool. I am also not sure which define applies here, there seems to be some code inlined in the declaration, so that might be executed, very maybe this will be executed: return QString::fromLatin1(sourceText); But probably not... I guess it's time to take a look at lupdate, or maybe the TR is implemented somewhere else ? Hmm... My best bet for now is the lupdate generates some code for TR or replaces the calls to TR with something... the question is what is it replaced with ? ;) Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 08:31:24 PM So far it seems lupdate is a linux command line utility, I am not yet sure if it's available for windows.
It's description is as follows: " lupdate -------------------------------------------------------------------------------- lupdate(1) lupdate(1) NAME lupdate - update Qt Linguist translation files SYNOPSIS lupdate [ options ] project-file lupdate [ options ] source-files -ts ts-files DESCRIPTION This page documents the Qt Linguist Update tool for the Qt GUI toolkit. Lupdate reads a qmake/tmake project file (.pro file), finds the trans- latable strings in the specified source, header and interface files, and updates the translation files (.ts files) specified in it. The translation files are given to the translator who uses Qt Linguist to read the files and insert the translations. The .ts file format is a simple human-readable XML format that can be used with version control systems if required. OPTIONS -help Display the usage and exit. -noobsolete Drop all obsolete strings. -verbose Explain what is being done. -version Display the version of lupdate and exit. USAGE Here is an example .pro file that can be given to lupdate: HEADERS = funnydialog.h \ wackywidget.h SOURCES = funnydialog.cpp \ main.cpp \ wackywidget.cpp FORMS = fancybox.ui TRANSLATIONS = gnomovision_dk.ts \ gnomovision_fi.ts \ gnomovision_no.ts \ gnomovision_se.ts When running lupdate on this project file, the translatable strings in all the files listed in the HEADERS, SOURCES and FORMS entries will be put in the translation files listed in the TRANSLATIONS entry. Previous translations will be reused as far as possible, and translated strings that have vanished from the source files are marked obsolete. Lupdate can also be invoked with a list of C++ source files, .ui files and .ts files: lupdate *.cpp *.h *.ui -ts gnomovision_dk.ts SEE ALSO lrelease(1) and http://doc.trolltech.com/i18n.html Trolltech AS 18 October 2001 lupdate(1) " So this indeed seems to be some TR replacement tool... Bye, Skybuck. Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 08:35:34 PM I am first going to explore/investigate the .arg(text) specifier.
This is more likely the cause of the bug, so perhaps lupdate has nothing to do with it and can be ignored/skipped, but could still be interesting to see if lupdate does anything weird which could be exploited ;) But for now my best bet is it's actually QString.Arg(..?..) at fault which might cause the bug. I happened to see that .arg is probably a method from QString, so skipping lupdate and investigating QString might save some time and might be easier to do. So first a looksy at QString to see if it is the root cause of the bug ! ;) :) Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: SgtSpike on November 22, 2011, 08:40:30 PM Well at least this guy doesn't mind diving in headfirst!
Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 08:44:47 PM QString can be found here:
http://qt.gitorious.org/qt/qt/trees/4.8/src/corelib/tools Now it's going to get interesting ! ;) :) Diving into QString =D Examining QString.h first: One of these routine pairs seems to be at play, It's either these ones: 132 QString arg(qlonglong a, int fieldwidth=0, int base=10, 133 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 134 QString arg(qulonglong a, int fieldwidth=0, int base=10, 135 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 136 QString arg(long a, int fieldwidth=0, int base=10, 137 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 138 QString arg(ulong a, int fieldwidth=0, int base=10, 139 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 140 QString arg(int a, int fieldWidth = 0, int base = 10, 141 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 142 QString arg(uint a, int fieldWidth = 0, int base = 10, 143 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 144 QString arg(short a, int fieldWidth = 0, int base = 10, 145 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 146 QString arg(ushort a, int fieldWidth = 0, int base = 10, 147 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 148 QString arg(double a, int fieldWidth = 0, char fmt = 'g', int prec = -1, 149 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 150 QString arg(char a, int fieldWidth = 0, 151 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 152 QString arg(QChar a, int fieldWidth = 0, 153 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 154 QString arg(const QString &a, int fieldWidth = 0, 155 const QChar &fillChar = QLatin1Char(' ')) const Q_REQUIRED_RESULT; 156 QString arg(const QString &a1, const QString &a2) const Q_REQUIRED_RESULT; 157 QString arg(const QString &a1, const QString &a2, const QString &a3) const Q_REQUIRED_RESULT; 158 QString arg(const QString &a1, const QString &a2, const QString &a3, 159 const QString &a4) const Q_REQUIRED_RESULT; 160 QString arg(const QString &a1, const QString &a2, const QString &a3, 161 const QString &a4, const QString &a5) const Q_REQUIRED_RESULT; 162 QString arg(const QString &a1, const QString &a2, const QString &a3, 163 const QString &a4, const QString &a5, const QString &a6) const Q_REQUIRED_RESULT; 164 QString arg(const QString &a1, const QString &a2, const QString &a3, 165 const QString &a4, const QString &a5, const QString &a6, 166 const QString &a7) const Q_REQUIRED_RESULT; 167 QString arg(const QString &a1, const QString &a2, const QString &a3, 168 const QString &a4, const QString &a5, const QString &a6, 169 const QString &a7, const QString &a8) const Q_REQUIRED_RESULT; 170 QString arg(const QString &a1, const QString &a2, const QString &a3, 171 const QString &a4, const QString &a5, const QString &a6, 172 const QString &a7, const QString &a8, const QString &a9) const Q_REQUIRED_RESULT; Or one of these, this depends on the conditional used, which I don't know yet, for now I will assume above ones are used, but will keep a look out for the one below as well. 328 static QString fromAscii(const char *, int size = -1); 329 static QString fromLatin1(const char *, int size = -1); 330 static QString fromUtf8(const char *, int size = -1); 331 static QString fromLocal8Bit(const char *, int size = -1); 332 static QString fromUtf16(const ushort *, int size = -1); 333 static QString fromUcs4(const uint *, int size = -1); 334 static QString fromRawData(const QChar *, int size); From the looks of it from the first possibility it also seems: class QLatin1Char plays a roll, I am not exactly sure what this is, but it seems to be a forward declaration. It is indeed a forward declaration which is later declared further it's code is in the same file and is: 654 class Q_CORE_EXPORT QLatin1String 655 { 656 public: 657 inline explicit QLatin1String(const char *s) : chars(s) {} 658 inline QLatin1String &operator=(const QLatin1String &other) 659 { chars = other.chars; return *this; } 660 661 inline const char *latin1() const { return chars; } 662 663 inline bool operator==(const QString &s) const 664 { return s == *this; } 665 inline bool operator!=(const QString &s) const 666 { return s != *this; } 667 inline bool operator>(const QString &s) const 668 { return s < *this; } 669 inline bool operator<(const QString &s) const 670 { return s > *this; } 671 inline bool operator>=(const QString &s) const 672 { return s <= *this; } 673 inline bool operator<=(const QString &s) const 674 { return s >= *this; } 675 676 inline QT_ASCII_CAST_WARN bool operator==(const char *s) const 677 { return QString::fromAscii(s) == *this; } 678 inline QT_ASCII_CAST_WARN bool operator!=(const char *s) const 679 { return QString::fromAscii(s) != *this; } 680 inline QT_ASCII_CAST_WARN bool operator<(const char *s) const 681 { return QString::fromAscii(s) > *this; } 682 inline QT_ASCII_CAST_WARN bool operator>(const char *s) const 683 { return QString::fromAscii(s) < *this; } 684 inline QT_ASCII_CAST_WARN bool operator<=(const char *s) const 685 { return QString::fromAscii(s) >= *this; } 686 inline QT_ASCII_CAST_WARN bool operator>=(const char *s) const 687 { return QString::fromAscii(s) <= *this; } 688 private: 689 const char *chars; 690 }; Stars/asterixes (*) are always fun, perhaps it has a pointer bug somewhere, but for now I will assume this tiny little class is flawless... ;) Ok further examinating the arg routines, I am not sure what this is: const Q_REQUIRED_RESULT; It could be some return value, or perhaps a place holder ? or perhaps a directive ? so perhaps these are inlined routines and have no body/implementation, or maybe they do. So time to go investigate QString.cpp ! ;) :) Title: Re: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack. Post by: Skybuck on November 22, 2011, 09:20:32 PM QString.cpp is pretty big, it' about 8000 to 9000 line of code, an introductionary description of it is:
431 \class QString 432 \reentrant 433 434 \brief The QString class provides a Unicode character string. 435 436 \ingroup tools 437 \ingroup shared 438 \ingroup string-processing 439 440 QString stores a string of 16-bit \l{QChar}s, where each QChar 441 corresponds one Unicode 4.0 character. (Unicode characters 442 with code values above 65535 are stored using surrogate pairs, 443 i.e., two consecutive \l{QChar}s.) 444 445 \l{Unicode} is an international standard that supports most of the 446 writing systems in use today. It is a superset of US-ASCII (ANSI 447 X3.4-1986) and Latin-1 (ISO 8859-1), and all the US-ASCII/Latin-1 448 characters are available at the same code positions. 449 450 Behind the scenes, QString uses \l{implicit sharing} 451 (copy-on-write) to reduce memory usage and to avoid the needless 452 copying of data. This also helps reduce the inherent overhead of 453 storing 16-bit characters instead of 8-bit characters. 454 455 In addition to QString, Qt also provides the QByteArray class to 456 store raw bytes and traditional 8-bit '\\0'-terminated strings. 457 For most purposes, QString is the class you want to use. It is 458 used throughout the Qt API, and the Unicode support ensures that 459 your applications will be easy to translate if you want to expand 460 your application's market at some point. The two main cases where 461 QByteArray is appropriate are when you need to store raw binary 462 data, and when memory conservation is critical (e.g., with 463 \l{Qt for Embedded Linux}). (Above it was also a QCharRef class, which it might or might not use ;)) So from an exploit point of view, this is pretty good news. Unicode is still somewhat experimental/complex and always under development/expanding........ the file/class is pretty big... so a high likely hood of some remaining bugs here and there... Now the question is where are the bugs and did we get lucky for the .arg stuff ?! ;) :) =D (Also the link/web-based approach takes a minute or so to render it in html/ie9 so some patience is needed ;)) Things are starting to get a bit fuzzy, since I am not sure which exact version of arg function is being called so it's starting to seem that perhaps real world execution/tracing might be necessary to know for sure. But if I had to make a bet, I would place my bets on the finally arg function and routines which it calls. To me it seems highly likely that there is at least some kind of bug in these routines which causes %n to be displayed instead of something else like a number or so: 6374 struct ArgEscapeData 6375 { 6376 int min_escape; // lowest escape sequence number 6377 int occurrences; // number of occurrences of the lowest escape sequence number 6378 int locale_occurrences; // number of occurrences of the lowest escape sequence number that 6379 // contain 'L' 6380 int escape_len; // total length of escape sequences which will be replaced 6381 }; 6382 6383 static ArgEscapeData findArgEscapes(const QString &s) 6384 { 6385 const QChar *uc_begin = s.unicode(); 6386 const QChar *uc_end = uc_begin + s.length(); 6387 6388 ArgEscapeData d; 6389 6390 d.min_escape = INT_MAX; 6391 d.occurrences = 0; 6392 d.escape_len = 0; 6393 d.locale_occurrences = 0; 6394 6395 const QChar *c = uc_begin; 6396 while (c != uc_end) { 6397 while (c != uc_end && c->unicode() != '%') 6398 ++c; 6399 6400 if (c == uc_end) 6401 break; 6402 const QChar *escape_start = c; 6403 if (++c == uc_end) 6404 break; 6405 6406 bool locale_arg = false; 6407 if (c->unicode() == 'L') { 6408 locale_arg = true; 6409 if (++c == uc_end) 6410 break; 6411 } 6412 6413 if (c->digitValue() == -1) 6414 continue; 6415 6416 int escape = c->digitValue(); 6417 ++c; 6418 6419 if (c != uc_end && c->digitValue() != -1) { 6420 escape = (10 * escape) + c->digitValue(); 6421 ++c; 6422 } 6423 6424 if (escape > d.min_escape) 6425 continue; 6426 6427 if (escape < d.min_escape) { 6428 d.min_escape = escape; 6429 d.occurrences = 0; 6430 d.escape_len = 0; 6431 d.locale_occurrences = 0; 6432 } 6433 6434 ++d.occurrences; 6435 if (locale_arg) 6436 ++d.locale_occurrences; 6437 d.escape_len += c - escape_start; 6438 } 6439 return d; 6440 } 6441 6442 static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int field_width, 6443 const QString &arg, const QString &larg, const QChar &fillChar = QLatin1Char(' ')) 6444 { 6445 const QChar *uc_begin = s.unicode(); 6446 const QChar *uc_end = uc_begin + s.length(); 6447 6448 int abs_field_width = qAbs(field_width); 6449 int result_len = s.length() 6450 - d.escape_len 6451 + (d.occurrences - d.locale_occurrences) 6452 *qMax(abs_field_width, arg.length()) 6453 + d.locale_occurrences 6454 *qMax(abs_field_width, larg.length()); 6455 6456 QString result(result_len, Qt::Uninitialized); 6457 QChar *result_buff = (QChar*) result.unicode(); 6458 6459 QChar *rc = result_buff; 6460 const QChar *c = uc_begin; 6461 int repl_cnt = 0; 6462 while (c != uc_end) { 6463 /* We don't have to check if we run off the end of the string with c, 6464 because as long as d.occurrences > 0 we KNOW there are valid escape 6465 sequences. */ 6466 6467 const QChar *text_start = c; 6468 6469 while (c->unicode() != '%') 6470 ++c; 6471 6472 const QChar *escape_start = c++; 6473 6474 bool locale_arg = false; 6475 if (c->unicode() == 'L') { 6476 locale_arg = true; 6477 ++c; 6478 } 6479 6480 int escape = c->digitValue(); 6481 if (escape != -1) { 6482 if (c + 1 != uc_end && (c + 1)->digitValue() != -1) { 6483 escape = (10 * escape) + (c + 1)->digitValue(); 6484 ++c; 6485 } 6486 } 6487 6488 if (escape != d.min_escape) { 6489 memcpy(rc, text_start, (c - text_start)*sizeof(QChar)); 6490 rc += c - text_start; 6491 } 6492 else { 6493 ++c; 6494 6495 memcpy(rc, text_start, (escape_start - text_start)*sizeof(QChar)); 6496 rc += escape_start - text_start; 6497 6498 uint pad_chars; 6499 if (locale_arg) 6500 pad_chars = qMax(abs_field_width, larg.length()) - larg.length(); 6501 else 6502 pad_chars = qMax(abs_field_width, arg.length()) - arg.length(); 6503 6504 if (field_width > 0) { // left padded 6505 for (uint i = 0; i < pad_chars; ++i) 6506 (rc++)->unicode() = fillChar.unicode(); 6507 } 6508 6509 if (locale_arg) { 6510 memcpy(rc, larg.unicode(), larg.length()*sizeof(QChar)); 6511 rc += larg.length(); 6512 } 6513 else { 6514 memcpy(rc, arg.unicode(), arg.length()*sizeof(QChar)); 6515 rc += arg.length(); 6516 } 6517 6518 if (field_width < 0) { // right padded 6519 for (uint i = 0; i < pad_chars; ++i) 6520 (rc++)->unicode() = fillChar.unicode(); 6521 } 6522 6523 if (++repl_cnt == d.occurrences) { 6524 memcpy(rc, c, (uc_end - c)*sizeof(QChar)); 6525 rc += uc_end - c; 6526 Q_ASSERT(rc - result_buff == result_len); 6527 c = uc_end; 6528 } 6529 } 6530 } 6531 Q_ASSERT(rc == result_buff + result_len); 6532 6533 return result; 6534 } 6535 6536 /*! 6537 Returns a copy of this string with the lowest numbered place marker 6538 replaced by string \a a, i.e., \c %1, \c %2, ..., \c %99. 6539 6540 \a fieldWidth specifies the minimum amount of space that argument \a 6541 a shall occupy. If \a a requires less space than \a fieldWidth, it 6542 is padded to \a fieldWidth with character \a fillChar. A positive 6543 \a fieldWidth produces right-aligned text. A negative \a fieldWidth 6544 produces left-aligned text. 6545 6546 This example shows how we might create a \c status string for 6547 reporting progress while processing a list of files: 6548 6549 \snippet doc/src/snippets/qstring/main.cpp 11 6550 6551 First, \c arg(i) replaces \c %1. Then \c arg(total) replaces \c 6552 %2. Finally, \c arg(fileName) replaces \c %3. 6553 6554 One advantage of using arg() over sprintf() is that the order of the 6555 numbered place markers can change, if the application's strings are 6556 translated into other languages, but each arg() will still replace 6557 the lowest numbered unreplaced place marker, no matter where it 6558 appears. Also, if place marker \c %i appears more than once in the 6559 string, the arg() replaces all of them. 6560 6561 If there is no unreplaced place marker remaining, a warning message 6562 is output and the result is undefined. Place marker numbers must be 6563 in the range 1 to 99. 6564 */ 6565 QString QString::arg(const QString &a, int fieldWidth, const QChar &fillChar) const 6566 { 6567 ArgEscapeData d = findArgEscapes(*this); 6568 6569 if (d.occurrences == 0) { 6570 qWarning("QString::arg: Argument missing: %s, %s", toLocal8Bit().data(), 6571 a.toLocal8Bit().data()); 6572 return *this; 6573 } 6574 return replaceArgEscapes(*this, d, fieldWidth, a, a, fillChar); 6575 } Seeing these stack based buffers or perhaps heap based buffer variables, bold statements in the comments about "being sure" lol... and memcopies, and also some sanity checking asserts is going to make some exploit writers/researchers going to water-teeth ;) :) The code does indeed look somewhat complex so a high likelyness of being a bug in there ;) Would be interesting to know if this is indeed the code that is being executed. So for now I guess I am going to stop my investigation until I or somebody else can actually execute the entire bitcoin+qt code and see/confirm if this is indeed the code being executed ;) One last possibility to consider is this piece of code in case it's a multi arg call: 6957 static int getEscape(const QChar *uc, int *pos, int len, int maxNumber = 999) 6958 { 6959 int i = *pos; 6960 ++i; 6961 if (i < len && uc == QLatin1Char('L')) 6962 ++i; 6963 if (i < len) { 6964 int escape = uc.unicode() - '0'; 6965 if (uint(escape) >= 10U) 6966 return -1; 6967 ++i; 6968 while (i < len) { 6969 int digit = uc.unicode() - '0'; 6970 if (uint(digit) >= 10U) 6971 break; 6972 escape = (escape * 10) + digit; 6973 ++i; 6974 } 6975 if (escape <= maxNumber) { 6976 *pos = i; 6977 return escape; 6978 } 6979 } 6980 return -1; 6981 } 6982 6983 QString QString::multiArg(int numArgs, const QString **args) const 6984 { 6985 QString result; 6986 QMap<int, int> numbersUsed; 6987 const QChar *uc = (const QChar *) d->data; 6988 const int len = d->size; 6989 const int end = len - 1; 6990 int lastNumber = -1; 6991 int i = 0; 6992 6993 // populate the numbersUsed map with the %n's that actually occur in the string 6994 while (i < end) { 6995 if (uc == QLatin1Char('%')) { 6996 int number = getEscape(uc, &i, len); 6997 if (number != -1) { 6998 numbersUsed.insert(number, -1); 6999 continue; 7000 } 7001 } 7002 ++i; 7003 } 7004 7005 // assign an argument number to each of the %n's 7006 QMap<int, int>::iterator j = numbersUsed.begin(); 7007 QMap<int, int>::iterator jend = numbersUsed.end(); 7008 int arg = 0; 7009 while (j != jend && arg < numArgs) { 7010 *j = arg++; 7011 lastNumber = j.key(); 7012 ++j; 7013 } 7014 7015 // sanity 7016 if (numArgs > arg) { 7017 qWarning("QString::arg: %d argument(s) missing in %s", numArgs - arg, toLocal8Bit().data()); 7018 numArgs = arg; 7019 } 7020 7021 i = 0; 7022 while (i < len) { 7023 if (uc == QLatin1Char('%') && i != end) { 7024 int number = getEscape(uc, &i, len, lastNumber); 7025 int arg = numbersUsed[number]; 7026 if (number != -1 && arg != -1) { 7027 result += *args[arg]; 7028 continue; 7029 } 7030 } 7031 result += uc[i++]; 7032 } 7033 return result; 7034 } |