forked from qt-creator/qt-creator
Debugger: Fix parsing of octal-encoded gdb escapes
Fixes: QTCREATORBUG-24462 Change-Id: I89153a04eeef6a2e20fefef45e0efa3712ec0997 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -89,105 +89,112 @@ void GdbMi::parseResultOrValue(const QChar *&from, const QChar *to)
|
||||
}
|
||||
}
|
||||
|
||||
// Reads one \ooo entity.
|
||||
static bool parseOctalEscapedHelper(const QChar *&from, const QChar *to, QByteArray &buffer)
|
||||
{
|
||||
if (to - from < 4)
|
||||
return false;
|
||||
if (*from != '\\')
|
||||
return false;
|
||||
|
||||
const char c1 = from[1].unicode();
|
||||
const char c2 = from[2].unicode();
|
||||
const char c3 = from[3].unicode();
|
||||
if (!isdigit(c1) || !isdigit(c2) || !isdigit(c3))
|
||||
return false;
|
||||
|
||||
buffer += char((c1 - '0') * 64 + (c2 - '0') * 8 + (c3 - '0'));
|
||||
from += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parseHexEscapedHelper(const QChar *&from, const QChar *to, QByteArray &buffer)
|
||||
{
|
||||
if (to - from < 4)
|
||||
return false;
|
||||
if (from[0]!= '\\')
|
||||
return false;
|
||||
if (from[1] != 'x')
|
||||
return false;
|
||||
|
||||
const char c1 = from[2].unicode();
|
||||
const char c2 = from[3].unicode();
|
||||
if (!isxdigit(c1) || !isxdigit(c2))
|
||||
return false;
|
||||
|
||||
buffer += char(16 * fromhex(c1) + fromhex(c2));
|
||||
from += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parseSimpleEscape(const QChar *&from, const QChar *to, QString &result)
|
||||
{
|
||||
if (from == to) {
|
||||
qDebug() << "MI Parse Error, unterminated backslash escape";
|
||||
return;
|
||||
}
|
||||
|
||||
QChar c = *from++;
|
||||
switch (c.unicode()) {
|
||||
case 'a': result += '\a'; break;
|
||||
case 'b': result += '\b'; break;
|
||||
case 'f': result += '\f'; break;
|
||||
case 'n': result += '\n'; break;
|
||||
case 'r': result += '\r'; break;
|
||||
case 't': result += '\t'; break;
|
||||
case 'v': result += '\v'; break;
|
||||
case '"': result += '"'; break;
|
||||
case '\'': result += '\''; break;
|
||||
case '\\': result += '\\'; break;
|
||||
default:
|
||||
qDebug() << "MI Parse Error, unrecognized backslash escape";
|
||||
}
|
||||
}
|
||||
|
||||
// Reads subsequent \123 or \x12 entities and converts to Utf8,
|
||||
// *or* one escaped char, *or* one unescaped char.
|
||||
static void parseCharOrEscape(const QChar *&from, const QChar *to, QString &result)
|
||||
{
|
||||
QByteArray buffer;
|
||||
while (parseOctalEscapedHelper(from, to, buffer))
|
||||
;
|
||||
while (parseHexEscapedHelper(from, to, buffer))
|
||||
;
|
||||
|
||||
if (!buffer.isEmpty())
|
||||
result.append(QString::fromUtf8(buffer));
|
||||
else if (*from == '\\')
|
||||
parseSimpleEscape(++from, to, result);
|
||||
else
|
||||
result += *from++;
|
||||
}
|
||||
|
||||
QString GdbMi::parseCString(const QChar *&from, const QChar *to)
|
||||
{
|
||||
QString result;
|
||||
if (to == from)
|
||||
return QString();
|
||||
|
||||
//qDebug() << "parseCString: " << QString(from, to - from);
|
||||
if (*from != '"') {
|
||||
qDebug() << "MI Parse Error, double quote expected";
|
||||
++from; // So we don't hang
|
||||
return QString();
|
||||
}
|
||||
const QChar *ptr = from;
|
||||
++ptr;
|
||||
while (ptr < to) {
|
||||
if (*ptr == '"') {
|
||||
++ptr;
|
||||
result = QString(from + 1, ptr - from - 2);
|
||||
break;
|
||||
}
|
||||
if (*ptr == '\\') {
|
||||
++ptr;
|
||||
if (ptr == to) {
|
||||
qDebug() << "MI Parse Error, unterminated backslash escape";
|
||||
from = ptr; // So we don't hang
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
from = ptr;
|
||||
|
||||
int idx = result.indexOf('\\');
|
||||
if (idx >= 0) {
|
||||
QChar *dst = result.data() + idx;
|
||||
const QChar *src = dst + 1, *end = result.data() + result.length();
|
||||
do {
|
||||
QChar c = *src++;
|
||||
switch (c.unicode()) {
|
||||
case 'a': *dst++ = '\a'; break;
|
||||
case 'b': *dst++ = '\b'; break;
|
||||
case 'f': *dst++ = '\f'; break;
|
||||
case 'n': *dst++ = '\n'; break;
|
||||
case 'r': *dst++ = '\r'; break;
|
||||
case 't': *dst++ = '\t'; break;
|
||||
case 'v': *dst++ = '\v'; break;
|
||||
case '"': *dst++ = '"'; break;
|
||||
case '\\': *dst++ = '\\'; break;
|
||||
case 'x': {
|
||||
c = *src++;
|
||||
int chars = 0;
|
||||
uchar prod = 0;
|
||||
while (true) {
|
||||
uchar val = fromhex(c.unicode());
|
||||
if (val == UCHAR_MAX)
|
||||
break;
|
||||
prod = prod * 16 + val;
|
||||
if (++chars == 3 || src == end)
|
||||
break;
|
||||
c = *src++;
|
||||
}
|
||||
if (!chars) {
|
||||
qDebug() << "MI Parse Error, unrecognized hex escape";
|
||||
return QString();
|
||||
}
|
||||
*dst++ = prod;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
int chars = 0;
|
||||
uchar prod = 0;
|
||||
forever {
|
||||
if (c < '0' || c > '7') {
|
||||
--src;
|
||||
break;
|
||||
}
|
||||
prod = prod * 8 + c.unicode() - '0';
|
||||
if (++chars == 3 || src == end)
|
||||
break;
|
||||
c = *src++;
|
||||
}
|
||||
if (!chars) {
|
||||
qDebug() << "MI Parse Error, unrecognized backslash escape";
|
||||
return QString();
|
||||
}
|
||||
*dst++ = prod;
|
||||
}
|
||||
}
|
||||
while (src != end) {
|
||||
QChar c = *src++;
|
||||
if (c == '\\')
|
||||
break;
|
||||
*dst++ = c;
|
||||
}
|
||||
} while (src != end);
|
||||
*dst = 0;
|
||||
result.truncate(dst - result.data());
|
||||
}
|
||||
|
||||
++from; // Skip initial quote.
|
||||
QString result;
|
||||
result.reserve(to - from);
|
||||
while (from < to) {
|
||||
if (*from == '"') {
|
||||
++from;
|
||||
return result;
|
||||
}
|
||||
parseCharOrEscape(from, to, result);
|
||||
}
|
||||
|
||||
qDebug() << "MI Parse Error, unfinished string";
|
||||
return QString();
|
||||
}
|
||||
|
||||
void GdbMi::parseValue(const QChar *&from, const QChar *to)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,15 @@ add_qtc_test(tst_debugger_gdb
|
||||
"${DEBUGGERDIR}/debuggerprotocol.cpp" "${DEBUGGERDIR}/debuggerprotocol.h"
|
||||
)
|
||||
|
||||
add_qtc_test(tst_debugger_protocol
|
||||
DEPENDS Qt5::Network Utils
|
||||
INCLUDES
|
||||
"${DEBUGGERDIR}"
|
||||
SOURCES
|
||||
"${DEBUGGERDIR}/debuggerprotocol.cpp" "${DEBUGGERDIR}/debuggerprotocol.h"
|
||||
tst_protocol.cpp
|
||||
)
|
||||
|
||||
add_qtc_test(tst_debugger_offsets
|
||||
DEPENDS Qt5::CorePrivate
|
||||
INCLUDES "${DEBUGGERDIR}"
|
||||
|
||||
@@ -12,3 +12,4 @@ SUBDIRS += simplifytypes.pro
|
||||
SUBDIRS += dumpers.pro
|
||||
SUBDIRS += disassembler.pro
|
||||
SUBDIRS += offsets.pro
|
||||
SUBDIRS += protocol.pro
|
||||
|
||||
@@ -7,6 +7,7 @@ Project {
|
||||
"disassembler.qbs",
|
||||
"dumpers.qbs",
|
||||
"gdb.qbs",
|
||||
"protocol.qbs",
|
||||
"offsets.qbs",
|
||||
"simplifytypes.qbs",
|
||||
]
|
||||
|
||||
46
tests/auto/debugger/protocol.pro
Normal file
46
tests/auto/debugger/protocol.pro
Normal file
@@ -0,0 +1,46 @@
|
||||
QT = core network
|
||||
|
||||
msvc: QTC_LIB_DEPENDS += utils
|
||||
include(../qttest.pri)
|
||||
|
||||
DEBUGGERDIR = $$IDE_SOURCE_TREE/src/plugins/debugger
|
||||
UTILSDIR = $$IDE_SOURCE_TREE/src/libs/utils
|
||||
|
||||
INCLUDEPATH += $$DEBUGGERDIR
|
||||
|
||||
SOURCES += \
|
||||
tst_protocol.cpp \
|
||||
$$DEBUGGERDIR/debuggerprotocol.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$DEBUGGERDIR/debuggerprotocol.h
|
||||
|
||||
!msvc {
|
||||
SOURCES += \
|
||||
$$UTILSDIR/environment.cpp \
|
||||
$$UTILSDIR/fileutils.cpp \
|
||||
$$UTILSDIR/hostosinfo.cpp \
|
||||
$$UTILSDIR/namevaluedictionary.cpp \
|
||||
$$UTILSDIR/namevalueitem.cpp \
|
||||
$$UTILSDIR/qtcassert.cpp \
|
||||
$$UTILSDIR/qtcprocess.cpp \
|
||||
$$UTILSDIR/processhandle.cpp \
|
||||
$$UTILSDIR/savefile.cpp \
|
||||
|
||||
HEADERS += \
|
||||
$$UTILSDIR/environment.h \
|
||||
$$UTILSDIR/fileutils.h \
|
||||
$$UTILSDIR/hostosinfo.h \
|
||||
$$UTILSDIR/namevaluedictionary.h \
|
||||
$$UTILSDIR/namevalueitem.h \
|
||||
$$UTILSDIR/qtcassert.h \
|
||||
$$UTILSDIR/qtcprocess.h \
|
||||
$$UTILSDIR/processhandle.h \
|
||||
$$UTILSDIR/savefile.h \
|
||||
|
||||
macos: {
|
||||
HEADERS += $$UTILSDIR/fileutils_mac.h
|
||||
OBJECTIVE_SOURCES += $$UTILSDIR/fileutils_mac.mm
|
||||
LIBS += -framework Foundation
|
||||
}
|
||||
}
|
||||
17
tests/auto/debugger/protocol.qbs
Normal file
17
tests/auto/debugger/protocol.qbs
Normal file
@@ -0,0 +1,17 @@
|
||||
import qbs
|
||||
|
||||
QtcAutotest {
|
||||
name: "debugger protocol autotest"
|
||||
Depends { name: "Utils" }
|
||||
Depends { name: "Qt.network" } // For QHostAddress
|
||||
Group {
|
||||
name: "Sources from Debugger plugin"
|
||||
prefix: project.debuggerDir
|
||||
files: "debuggerprotocol.cpp"
|
||||
}
|
||||
Group {
|
||||
name: "Test sources"
|
||||
files: "tst_protocol.cpp"
|
||||
}
|
||||
cpp.includePaths: base.concat([project.debuggerDir])
|
||||
}
|
||||
92
tests/auto/debugger/tst_protocol.cpp
Normal file
92
tests/auto/debugger/tst_protocol.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2020 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <debuggerprotocol.h>
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
//TESTED_COMPONENT=src/plugins/debugger
|
||||
|
||||
class tst_protocol : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_protocol() {}
|
||||
|
||||
private slots:
|
||||
void parseCString();
|
||||
void parseCString_data();
|
||||
};
|
||||
|
||||
void tst_protocol::parseCString()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, expected);
|
||||
|
||||
const QChar *from = input.begin();
|
||||
const QChar *to = input.end();
|
||||
QString parsed = Debugger::Internal::GdbMi::parseCString(from, to);
|
||||
|
||||
QCOMPARE(parsed, expected);
|
||||
}
|
||||
|
||||
void tst_protocol::parseCString_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("expected");
|
||||
|
||||
QTest::newRow("empty")
|
||||
<< ""
|
||||
<< "";
|
||||
|
||||
QTest::newRow("unquoted")
|
||||
<< "irgendwas"
|
||||
<< "";
|
||||
|
||||
QTest::newRow("plain")
|
||||
<< R"("plain")"
|
||||
<< "plain";
|
||||
|
||||
// This is expected to throw several warnings
|
||||
// "MI Parse Error, unrecognized backslash escape"
|
||||
QChar escapes[] = {'\a', '\b', '\f', '\n', '\r', '\t', '\v', '"', '\'', '\\'};
|
||||
QTest::newRow("escaped")
|
||||
<< R"("\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\y\z\"\'\\")"
|
||||
<< QString(escapes, sizeof(escapes)/sizeof(escapes[0]));
|
||||
|
||||
QTest::newRow("octal")
|
||||
<< R"("abc\303\244\303\251def\303\261")"
|
||||
<< R"(abcäédefñ)";
|
||||
|
||||
QTest::newRow("hex")
|
||||
<< R"("abc\xc3\xa4\xc3\xa9def\xc3\xb1")"
|
||||
<< R"(abcäédefñ)";
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_protocol);
|
||||
|
||||
#include "tst_protocol.moc"
|
||||
|
||||
Reference in New Issue
Block a user