Bitcoin Forum
November 11, 2024, 02:00:18 PM *
News: Latest Bitcoin Core release: 28.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Two possible bugs, and perhaps a bitcoin breakdown, and perhaps even str attack.  (Read 1654 times)
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 06:42:58 PM
 #1

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 ?! Wink (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 ?
SgtSpike
Legendary
*
Offline Offline

Activity: 1400
Merit: 1005



View Profile
November 22, 2011, 06:45:52 PM
 #2

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
0x00ffff * 2**(8*(0x1d - 3)) = 0x00000000FFFF0000000000000000000000000000000000000000000000000000

So [b]the difficulty at 0x1b0404cb[/b] is therefore:

0x00000000FFFF0000000000000000000000000000000000000000000000000000 /
0x00000000000404CB000000000000000000000000000000000000000000000000
= 16307.420938523983

It doesn't say max difficulty is 16307.  That's just an example.
casascius
Mike Caldwell
VIP
Legendary
*
Offline Offline

Activity: 1386
Merit: 1140


The Casascius 1oz 10BTC Silver Round (w/ Gold B)


View Profile WWW
November 22, 2011, 06:47:26 PM
 #3

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.

Companies claiming they got hacked and lost your coins sounds like fraud so perfect it could be called fashionable.  I never believe them.  If I ever experience the misfortune of a real intrusion, I declare I have been honest about the way I have managed the keys in Casascius Coins.  I maintain no ability to recover or reproduce the keys, not even under limitless duress or total intrusion.  Remember that trusting strangers with your coins without any recourse is, as a matter of principle, not a best practice.  Don't keep coins online. Use paper or hardware wallets instead.
DeathAndTaxes
Donator
Legendary
*
Offline Offline

Activity: 1218
Merit: 1079


Gerald Davis


View Profile
November 22, 2011, 06:52:32 PM
 #4

When you have 10 posts ... read more and post less.  Cheesy

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
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 06:54:51 PM
 #5

Ok, thanks guys in pointing out my misread Wink Smiley

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 Wink), 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 ! Wink
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 07:11:28 PM
 #6

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.
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 07:17:22 PM
 #7

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 Wink Smiley
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 07:42:25 PM
Last edit: November 22, 2011, 07:59:36 PM by Skybuck
 #8

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 Wink

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 Wink

It seems rather large to search manually.

So for now my search stops here Wink

(Also I am not sure which version of QT bitcoin 0.5 was compiled with, but for now I will assume the latest version Wink Smiley)

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.

Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 08:02:36 PM
 #9

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 Wink
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 08:15:43 PM
Last edit: November 22, 2011, 08:28:40 PM by Skybuck
 #10

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 Wink

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 ? Wink
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 08:31:24 PM
 #11

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.
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 08:35:34 PM
 #12

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 Wink

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 ! Wink Smiley
SgtSpike
Legendary
*
Offline Offline

Activity: 1400
Merit: 1005



View Profile
November 22, 2011, 08:40:30 PM
 #13

Well at least this guy doesn't mind diving in headfirst!
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 08:44:47 PM
Last edit: November 22, 2011, 09:02:33 PM by Skybuck
 #14

QString can be found here:

http://qt.gitorious.org/qt/qt/trees/4.8/src/corelib/tools

Now it's going to get interesting ! Wink Smiley

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... Wink

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 ! Wink Smiley
Skybuck (OP)
Full Member
***
Offline Offline

Activity: 386
Merit: 111


View Profile
November 22, 2011, 09:20:32 PM
Last edit: November 22, 2011, 09:58:07 PM by Skybuck
 #15

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 Wink)

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 ?! Wink Smiley =D

(Also the link/web-based approach takes a minute or so to render it in html/ie9 so some patience is needed Wink)

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 Wink Smiley

The code does indeed look somewhat complex so a high likelyness of being a bug in there Wink

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 Wink

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 }

Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!