Use CutterJson for Signatures and rewrite JsonModel (#2997)

This fixes a double-free in getSignatureInfo() when the string was
already freed by the intermediate CutterJson. But actually just using
this CutterJson makes more sense than the string anyway, so let's do
that.
The old JsonModel rebuilt the entire structure, defeating the main
purpose of a custom model in comparison to the existing QTreeWidget.
And since we are holding the json structures ourselves anyway, such a
custom model does not have many benefits anyway.
This commit is contained in:
Florian Märkl 2022-07-09 16:26:35 +02:00 committed by GitHub
parent 51c0a3d469
commit db0d4d85a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 67 additions and 329 deletions

View File

@ -72,7 +72,6 @@ set(SOURCES
widgets/CutterTreeWidget.cpp widgets/CutterTreeWidget.cpp
widgets/GraphWidget.cpp widgets/GraphWidget.cpp
widgets/OverviewWidget.cpp widgets/OverviewWidget.cpp
common/JsonTreeItem.cpp
common/JsonModel.cpp common/JsonModel.cpp
dialogs/VersionInfoDialog.cpp dialogs/VersionInfoDialog.cpp
widgets/FlirtWidget.cpp widgets/FlirtWidget.cpp
@ -222,7 +221,6 @@ set(HEADER_FILES
widgets/CutterTreeWidget.h widgets/CutterTreeWidget.h
widgets/GraphWidget.h widgets/GraphWidget.h
widgets/OverviewWidget.h widgets/OverviewWidget.h
common/JsonTreeItem.h
common/JsonModel.h common/JsonModel.h
dialogs/VersionInfoDialog.h dialogs/VersionInfoDialog.h
widgets/FlirtWidget.h widgets/FlirtWidget.h

View File

@ -1,123 +1,38 @@
#include "JsonModel.h" #include "JsonModel.h"
#include <QIODevice> QTreeWidgetItem *Cutter::jsonTreeWidgetItem(const QString &key, const CutterJson &json)
JsonModel::JsonModel(QObject *parent) : QAbstractItemModel(parent)
{ {
mRootItem = new JsonTreeItem; QString val;
mHeaders.append("key"); switch (json.type()) {
mHeaders.append("value"); case RZ_JSON_STRING:
} val = json.toString();
break;
JsonModel::~JsonModel() case RZ_JSON_BOOLEAN:
{ val = json.toBool() ? "true" : "false";
delete mRootItem; break;
} case RZ_JSON_DOUBLE:
val = QString::number(json.lowLevelValue()->num.dbl_value);
bool JsonModel::load(QIODevice *device) break;
{ case RZ_JSON_INTEGER:
return loadJson(device->readAll()); val = QString::number(json.toUt64());
} break;
case RZ_JSON_NULL:
bool JsonModel::loadJson(const QByteArray &json) val = "null";
{ break;
mDocument = QJsonDocument::fromJson(json); case RZ_JSON_OBJECT:
case RZ_JSON_ARRAY:
if (!mDocument.isNull()) { break;
beginResetModel(); }
delete mRootItem; auto r = new QTreeWidgetItem(QStringList({ key, val }));
if (mDocument.isArray()) { if (json.type() == RZ_JSON_ARRAY) {
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.array())); size_t i = 0;
} else { for (const auto &child : json) {
mRootItem = JsonTreeItem::load(QJsonValue(mDocument.object())); r->addChild(jsonTreeWidgetItem(QString::number(i++), child));
}
} else if (json.type() == RZ_JSON_OBJECT) {
for (const auto &child : json) {
r->addChild(jsonTreeWidgetItem(child.key(), child));
} }
endResetModel();
return true;
} }
return false; return r;
}
QVariant JsonModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
JsonTreeItem *item = static_cast<JsonTreeItem *>(index.internalPointer());
if (role == Qt::DisplayRole) {
if (index.column() == 0)
return QString("%1").arg(item->key());
if (index.column() == 1)
return QString("%1").arg(item->value());
}
return QVariant();
}
QVariant JsonModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal) {
return mHeaders.value(section);
} else
return QVariant();
}
QModelIndex JsonModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
JsonTreeItem *parentItem;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<JsonTreeItem *>(parent.internalPointer());
JsonTreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex JsonModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
JsonTreeItem *childItem = static_cast<JsonTreeItem *>(index.internalPointer());
JsonTreeItem *parentItem = childItem->parent();
if (parentItem == mRootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int JsonModel::rowCount(const QModelIndex &parent) const
{
JsonTreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = mRootItem;
else
parentItem = static_cast<JsonTreeItem *>(parent.internalPointer());
return parentItem->childCount();
}
int JsonModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 2;
} }

View File

@ -2,37 +2,13 @@
#ifndef JSONMODEL_H #ifndef JSONMODEL_H
#define JSONMODEL_H #define JSONMODEL_H
#include <QAbstractItemModel> #include <QTreeWidgetItem>
#include <QJsonDocument> #include "CutterJson.h"
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon>
#include "JsonTreeItem.h" namespace Cutter {
class JsonTreeItem; QTreeWidgetItem *jsonTreeWidgetItem(const QString &key, const CutterJson &json);
class JsonModel : public QAbstractItemModel
{
public:
explicit JsonModel(QObject *parent = nullptr);
bool load(QIODevice *device);
bool loadJson(const QByteArray &json);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation, int role) const Q_DECL_OVERRIDE;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
~JsonModel();
private:
JsonTreeItem *mRootItem;
QJsonDocument mDocument;
QStringList mHeaders;
}; };
#endif // JSONMODEL_H #endif // JSONMODEL_H

View File

@ -1,104 +0,0 @@
#include "JsonTreeItem.h"
JsonTreeItem::JsonTreeItem(JsonTreeItem *parent)
{
mParent = parent;
}
JsonTreeItem::~JsonTreeItem()
{
qDeleteAll(mChilds);
}
void JsonTreeItem::appendChild(JsonTreeItem *item)
{
mChilds.append(item);
}
JsonTreeItem *JsonTreeItem::child(int row)
{
return mChilds.value(row);
}
JsonTreeItem *JsonTreeItem::parent()
{
return mParent;
}
int JsonTreeItem::childCount() const
{
return mChilds.count();
}
int JsonTreeItem::row() const
{
if (mParent)
return mParent->mChilds.indexOf(const_cast<JsonTreeItem *>(this));
return 0;
}
void JsonTreeItem::setKey(const QString &key)
{
mKey = key;
}
void JsonTreeItem::setValue(const QString &value)
{
mValue = value;
}
void JsonTreeItem::setType(const QJsonValue::Type &type)
{
mType = type;
}
QString JsonTreeItem::key() const
{
return mKey;
}
QString JsonTreeItem::value() const
{
return mValue;
}
QJsonValue::Type JsonTreeItem::type() const
{
return mType;
}
JsonTreeItem *JsonTreeItem::load(const QJsonValue &value, JsonTreeItem *parent)
{
JsonTreeItem *rootItem = new JsonTreeItem(parent);
rootItem->setKey("root");
if (value.isObject()) {
// Get all QJsonValue childs
for (const QString &key : value.toObject().keys()) {
QJsonValue v = value.toObject().value(key);
JsonTreeItem *child = load(v, rootItem);
child->setKey(key);
child->setType(v.type());
rootItem->appendChild(child);
}
} else if (value.isArray()) {
// Get all QJsonValue childs
int index = 0;
for (const QJsonValue &v : value.toArray()) {
JsonTreeItem *child = load(v, rootItem);
child->setKey(QString::number(index));
child->setType(v.type());
rootItem->appendChild(child);
++index;
}
} else {
rootItem->setValue(value.toVariant().toString());
rootItem->setType(value.type());
}
return rootItem;
}

View File

@ -1,39 +0,0 @@
#ifndef JSONTREEITEM_H
#define JSONTREEITEM_H
#include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonObject>
#include <QIcon>
#include "JsonModel.h"
class JsonTreeItem
{
public:
JsonTreeItem(JsonTreeItem *parent = nullptr);
~JsonTreeItem();
void appendChild(JsonTreeItem *item);
JsonTreeItem *child(int row);
JsonTreeItem *parent();
int childCount() const;
int row() const;
void setKey(const QString &key);
void setValue(const QString &value);
void setType(const QJsonValue::Type &type);
QString key() const;
QString value() const;
QJsonValue::Type type() const;
static JsonTreeItem *load(const QJsonValue &value, JsonTreeItem *parent = nullptr);
private:
QString mKey;
QString mValue;
QJsonValue::Type mType;
QList<JsonTreeItem *> mChilds;
JsonTreeItem *mParent;
};
#endif // JSONTREEITEM_H

View File

@ -1440,7 +1440,7 @@ bool CutterCore::registerDecompiler(Decompiler *decompiler)
return true; return true;
} }
QString CutterCore::getSignatureInfo() CutterJson CutterCore::getSignatureInfo()
{ {
CORE_LOCK(); CORE_LOCK();
RzBinFile *cur = rz_bin_cur(core->bin); RzBinFile *cur = rz_bin_cur(core->bin);
@ -1452,11 +1452,7 @@ QString CutterCore::getSignatureInfo()
if (!signature) { if (!signature) {
return {}; return {};
} }
auto sig = parseJson(signature, nullptr); return parseJson(signature, nullptr);
if (sig.size() == 0) {
return {};
}
return fromOwnedCharPtr(signature);
} }
bool CutterCore::existsFileInfo() bool CutterCore::existsFileInfo()

View File

@ -569,7 +569,7 @@ public:
bool registerDecompiler(Decompiler *decompiler); bool registerDecompiler(Decompiler *decompiler);
RVA getOffsetJump(RVA addr); RVA getOffsetJump(RVA addr);
QString getSignatureInfo(); CutterJson getSignatureInfo();
bool existsFileInfo(); bool existsFileInfo();
void setGraphEmpty(bool empty); void setGraphEmpty(bool empty);
bool isGraphEmpty(); bool isGraphEmpty();

View File

@ -70,7 +70,6 @@ public:
iterator end() const { return iterator(nullptr, nullptr); } iterator end() const { return iterator(nullptr, nullptr); }
bool toBool() const { return value && value->type == RZ_JSON_BOOLEAN && value->num.u_value; } bool toBool() const { return value && value->type == RZ_JSON_BOOLEAN && value->num.u_value; }
QString toJson() const { return rz_json_as_string(value); }
st64 toSt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.s_value : 0; } st64 toSt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.s_value : 0; }
ut64 toUt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.u_value : 0; } ut64 toUt64() const { return value && value->type == RZ_JSON_INTEGER ? value->num.u_value : 0; }

View File

@ -4,8 +4,13 @@
CutterTreeView::CutterTreeView(QWidget *parent) : QTreeView(parent), ui(new Ui::CutterTreeView()) CutterTreeView::CutterTreeView(QWidget *parent) : QTreeView(parent), ui(new Ui::CutterTreeView())
{ {
ui->setupUi(this); ui->setupUi(this);
this->setSelectionMode(QAbstractItemView::ExtendedSelection); applyCutterStyle(this);
this->setUniformRowHeights(true);
} }
CutterTreeView::~CutterTreeView() {} CutterTreeView::~CutterTreeView() {}
void CutterTreeView::applyCutterStyle(QTreeView *view)
{
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
view->setUniformRowHeights(true);
}

View File

@ -19,6 +19,8 @@ public:
explicit CutterTreeView(QWidget *parent = nullptr); explicit CutterTreeView(QWidget *parent = nullptr);
~CutterTreeView(); ~CutterTreeView();
static void applyCutterStyle(QTreeView *view);
private: private:
std::unique_ptr<Ui::CutterTreeView> ui; std::unique_ptr<Ui::CutterTreeView> ui;
}; };

View File

@ -2,7 +2,6 @@
#include "ui_Dashboard.h" #include "ui_Dashboard.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include "common/JsonModel.h" #include "common/JsonModel.h"
#include "common/JsonTreeItem.h"
#include "common/TempConfig.h" #include "common/TempConfig.h"
#include "dialogs/VersionInfoDialog.h" #include "dialogs/VersionInfoDialog.h"
@ -162,7 +161,7 @@ void Dashboard::updateContents()
ui->verticalLayout_2->addSpacerItem(spacer); ui->verticalLayout_2->addSpacerItem(spacer);
// Check if signature info and version info available // Check if signature info and version info available
if (Core()->getSignatureInfo().isEmpty()) { if (!Core()->getSignatureInfo().size()) {
ui->certificateButton->setEnabled(false); ui->certificateButton->setEnabled(false);
} }
ui->versioninfoButton->setEnabled(Core()->existsFileInfo()); ui->versioninfoButton->setEnabled(Core()->existsFileInfo());
@ -170,33 +169,24 @@ void Dashboard::updateContents()
void Dashboard::on_certificateButton_clicked() void Dashboard::on_certificateButton_clicked()
{ {
static QDialog *viewDialog = nullptr; QDialog dialog(this);
static CutterTreeView *view = nullptr; auto view = new QTreeWidget(&dialog);
static JsonModel *model = nullptr; view->setHeaderLabels({ tr("Key"), tr("Value") });
static QString qstrCertificates; view->addTopLevelItem(Cutter::jsonTreeWidgetItem(QString("<%1>").arg(tr("root")),
if (!viewDialog) { Core()->getSignatureInfo()));
viewDialog = new QDialog(this); CutterTreeView::applyCutterStyle(view);
view = new CutterTreeView(viewDialog); view->expandAll();
model = new JsonModel(); view->resize(900, 600);
qstrCertificates = Core()->getSignatureInfo(); QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
} sizePolicy.setHorizontalStretch(0);
if (!viewDialog->isVisible()) { sizePolicy.setVerticalStretch(0);
std::string strCertificates = qstrCertificates.toUtf8().constData(); sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());
model->loadJson(QByteArray::fromStdString(strCertificates)); dialog.setSizePolicy(sizePolicy);
view->setModel(model); dialog.setMinimumSize(QSize(900, 600));
view->expandAll(); dialog.setMaximumSize(QSize(900, 600));
view->resize(900, 600); dialog.setSizeGripEnabled(false);
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); dialog.setWindowTitle("Certificates");
sizePolicy.setHorizontalStretch(0); dialog.exec();
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(view->sizePolicy().hasHeightForWidth());
viewDialog->setSizePolicy(sizePolicy);
viewDialog->setMinimumSize(QSize(900, 600));
viewDialog->setMaximumSize(QSize(900, 600));
viewDialog->setSizeGripEnabled(false);
viewDialog->setWindowTitle("Certificates");
viewDialog->show();
}
} }
void Dashboard::on_versioninfoButton_clicked() void Dashboard::on_versioninfoButton_clicked()