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,104 +89,111 @@ 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 GdbMi::parseCString(const QChar *&from, const QChar *to)
|
||||||
{
|
{
|
||||||
QString result;
|
if (to == from)
|
||||||
|
return QString();
|
||||||
|
|
||||||
//qDebug() << "parseCString: " << QString(from, to - from);
|
//qDebug() << "parseCString: " << QString(from, to - from);
|
||||||
if (*from != '"') {
|
if (*from != '"') {
|
||||||
qDebug() << "MI Parse Error, double quote expected";
|
qDebug() << "MI Parse Error, double quote expected";
|
||||||
++from; // So we don't hang
|
++from; // So we don't hang
|
||||||
return QString();
|
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('\\');
|
++from; // Skip initial quote.
|
||||||
if (idx >= 0) {
|
QString result;
|
||||||
QChar *dst = result.data() + idx;
|
result.reserve(to - from);
|
||||||
const QChar *src = dst + 1, *end = result.data() + result.length();
|
while (from < to) {
|
||||||
do {
|
if (*from == '"') {
|
||||||
QChar c = *src++;
|
++from;
|
||||||
switch (c.unicode()) {
|
return result;
|
||||||
case 'a': *dst++ = '\a'; break;
|
}
|
||||||
case 'b': *dst++ = '\b'; break;
|
parseCharOrEscape(from, to, result);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
qDebug() << "MI Parse Error, unfinished string";
|
||||||
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbMi::parseValue(const QChar *&from, const QChar *to)
|
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"
|
"${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
|
add_qtc_test(tst_debugger_offsets
|
||||||
DEPENDS Qt5::CorePrivate
|
DEPENDS Qt5::CorePrivate
|
||||||
INCLUDES "${DEBUGGERDIR}"
|
INCLUDES "${DEBUGGERDIR}"
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ SUBDIRS += simplifytypes.pro
|
|||||||
SUBDIRS += dumpers.pro
|
SUBDIRS += dumpers.pro
|
||||||
SUBDIRS += disassembler.pro
|
SUBDIRS += disassembler.pro
|
||||||
SUBDIRS += offsets.pro
|
SUBDIRS += offsets.pro
|
||||||
|
SUBDIRS += protocol.pro
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ Project {
|
|||||||
"disassembler.qbs",
|
"disassembler.qbs",
|
||||||
"dumpers.qbs",
|
"dumpers.qbs",
|
||||||
"gdb.qbs",
|
"gdb.qbs",
|
||||||
|
"protocol.qbs",
|
||||||
"offsets.qbs",
|
"offsets.qbs",
|
||||||
"simplifytypes.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