Heap widget wrapup (#2716)

This commit is contained in:
Pulak Malhotra 2021-07-16 19:18:10 +05:30 committed by GitHub
parent e29585c672
commit 60343fa8b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 370 additions and 43 deletions

2
rizin

@ -1 +1 @@
Subproject commit 381f22d7cc81bf2eb6663a690a715ff4f8f09373
Subproject commit 254c5119e3563d05d02665cabbcba003c921cee8

View File

@ -146,6 +146,7 @@ set(SOURCES
widgets/GlibcHeapWidget.cpp
dialogs/GlibcHeapBinsDialog.cpp
widgets/HeapBinsGraphView.cpp
dialogs/ArenaInfoDialog.cpp
)
set(HEADER_FILES
core/Cutter.h
@ -304,6 +305,7 @@ set(HEADER_FILES
widgets/GlibcHeapWidget.h
dialogs/GlibcHeapBinsDialog.h
widgets/HeapBinsGraphView.h
dialogs/ArenaInfoDialog.h
)
set(UI_FILES
dialogs/AboutDialog.ui
@ -374,6 +376,7 @@ set(UI_FILES
widgets/HeapDockWidget.ui
widgets/GlibcHeapWidget.ui
dialogs/GlibcHeapBinsDialog.ui
dialogs/ArenaInfoDialog.ui
)
set(QRC_FILES
resources.qrc

View File

@ -1592,6 +1592,12 @@ QVector<Chunk> CutterCore::getHeapChunks(RVA arena_addr)
return chunks_vector;
}
int CutterCore::getArchBits()
{
CORE_LOCK();
return core->dbg->bits;
}
QVector<Arena> CutterCore::getArenas()
{
CORE_LOCK();
@ -1606,6 +1612,12 @@ QVector<Arena> CutterCore::getArenas()
Arena arena;
arena.offset = data->addr;
arena.type = QString(data->type);
arena.last_remainder = data->arena->last_remainder;
arena.top = data->arena->top;
arena.next = data->arena->next;
arena.next_free = data->arena->next_free;
arena.system_mem = data->arena->system_mem;
arena.max_system_mem = data->arena->max_system_mem;
arena_vector.append(arena);
}
@ -1653,10 +1665,30 @@ QVector<RzHeapBin *> CutterCore::getHeapBins(ut64 arena_addr)
}
bins_vector.append(bin);
}
// get tcache bins
RzList *tcache_bins = rz_heap_tcache_content(core, arena_addr);
RzListIter *iter;
RzHeapBin *bin;
CutterRListForeach(tcache_bins, iter, RzHeapBin, bin)
{
if (!bin) {
continue;
}
if (!rz_list_length(bin->chunks)) {
rz_heap_bin_free_64(bin);
continue;
}
bins_vector.append(bin);
}
return bins_vector;
}
bool CutterCore::writeHeapChunk(RzHeapChunkSimple *chunk_simple)
{
CORE_LOCK();
return rz_heap_write_chunk(core, chunk_simple);
}
QJsonDocument CutterCore::getChildProcesses(int pid)
{
// Return the currently debugged process and it's children

View File

@ -419,7 +419,20 @@ public:
* @return RzHeapChunkSimple struct pointer for the heap chunk
*/
RzHeapChunkSimple *getHeapChunk(ut64 addr);
/**
* @brief Get heap bins of an arena with given base address
* (including large, small, fast, unsorted, tcache)
* @param arena_addr Base address of the arena
* @return QVector of non empty RzHeapBin pointers
*/
QVector<RzHeapBin *> getHeapBins(ut64 arena_addr);
/**
* @brief Write the given chunk header to memory
* @param chunkSimple RzHeapChunkSimple pointer of the chunk to be written
* @return true if the write succeeded else false
*/
bool writeHeapChunk(RzHeapChunkSimple *chunkSimple);
int getArchBits();
void startDebug();
void startEmulation();
/**

View File

@ -375,6 +375,12 @@ struct Arena
{
RVA offset;
QString type;
ut64 top;
ut64 last_remainder;
ut64 next;
ut64 next_free;
ut64 system_mem;
ut64 max_system_mem;
};
Q_DECLARE_METATYPE(FunctionDescription)

View File

@ -0,0 +1,25 @@
#include "ArenaInfoDialog.h"
#include "ui_ArenaInfoDialog.h"
ArenaInfoDialog::ArenaInfoDialog(Arena &arena, QWidget *parent)
: arena(arena), QDialog(parent), ui(new Ui::ArenaInfoDialog)
{
ui->setupUi(this);
setWindowTitle("Arena @ " + RAddressString(arena.offset));
updateContents();
}
void ArenaInfoDialog::updateContents()
{
ui->lineEditTop->setText(RAddressString(arena.top));
ui->lineEditLastRem->setText(RAddressString(arena.last_remainder));
ui->lineEditNext->setText(RAddressString(arena.next));
ui->lineEditNextfree->setText(RAddressString(arena.next_free));
ui->lineEditSysMem->setText(RAddressString(arena.system_mem));
ui->lineEditMaxMem->setText(RAddressString(arena.max_system_mem));
}
ArenaInfoDialog::~ArenaInfoDialog()
{
delete ui;
}

View File

@ -0,0 +1,25 @@
#ifndef ARENAINFODIALOG_H
#define ARENAINFODIALOG_H
#include <QDialog>
#include <CutterDescriptions.h>
namespace Ui {
class ArenaInfoDialog;
}
class ArenaInfoDialog : public QDialog
{
Q_OBJECT
public:
explicit ArenaInfoDialog(Arena &arena, QWidget *parent = nullptr);
~ArenaInfoDialog();
void updateContents();
private:
Ui::ArenaInfoDialog *ui;
Arena arena;
};
#endif // ARENAINFODIALOG_H

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ArenaInfoDialog</class>
<widget class="QDialog" name="ArenaInfoDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>202</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Top</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Next</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Next free</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>System Memory</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Max Memory</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEditTop">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditNext">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditNextfree">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="lineEditSysMem">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineEditMaxMem">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Last Remainder</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="lineEditLastRem">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -4,10 +4,10 @@
<widget class="QDialog" name="GlibcHeapBinsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>883</width>
<height>544</height>
<x>0</x>
<y>0</y>
<width>883</width>
<height>544</height>
</rect>
</property>
<property name="windowTitle">
@ -27,7 +27,11 @@
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="chainInfoEdit"/>
<widget class="QPlainTextEdit" name="chainInfoEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout3">

View File

@ -8,11 +8,6 @@ GlibcHeapInfoDialog::GlibcHeapInfoDialog(RVA offset, QString status, QWidget *pa
{
ui->setupUi(this);
// disable all the radio buttons for flag field so they are not user editable
this->ui->rbIM->setEnabled(false);
this->ui->rbNMA->setEnabled(false);
this->ui->rbPI->setEnabled(false);
// set window title
QString windowTitle = tr("Chunk @ ") + RAddressString(offset);
if (!this->status.isEmpty()) {
@ -20,6 +15,8 @@ GlibcHeapInfoDialog::GlibcHeapInfoDialog(RVA offset, QString status, QWidget *pa
}
this->setWindowTitle(windowTitle);
connect(ui->saveButton, &QPushButton::clicked, this, &GlibcHeapInfoDialog::saveChunkInfo);
updateFields();
}
@ -46,13 +43,69 @@ void GlibcHeapInfoDialog::updateFields()
this->ui->prevSizeEdit->setText(RHexString(chunk->prev_size));
if (chunk->is_mmapped) {
this->ui->rbIM->setChecked(true);
} else {
this->ui->rbIM->setChecked(false);
}
if (chunk->prev_inuse) {
this->ui->rbPI->setChecked(true);
} else {
this->ui->rbPI->setChecked(false);
}
if (chunk->non_main_arena) {
this->ui->rbNMA->setChecked(true);
} else {
this->ui->rbNMA->setChecked(false);
}
free(chunk);
}
void GlibcHeapInfoDialog::saveChunkInfo()
{
QMessageBox msgBox;
msgBox.setText("Do you want to overwrite chunk metadata?");
msgBox.setInformativeText(
"Any field which cannot be converted to a valid integer will be saved as zero");
msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);
int ret = msgBox.exec();
switch (ret) {
case QMessageBox::Save:
// Save was clicked
RzHeapChunkSimple chunkSimple;
chunkSimple.size = Core()->math(ui->sizeEdit->text());
chunkSimple.fd = Core()->math(ui->fdEdit->text());
chunkSimple.bk = Core()->math(ui->bkEdit->text());
chunkSimple.fd_nextsize = Core()->math(ui->fdnsEdit->text());
chunkSimple.bk_nextsize = Core()->math(ui->bknsEdit->text());
chunkSimple.addr = offset;
if (ui->rbIM->isChecked()) {
chunkSimple.is_mmapped = true;
} else {
chunkSimple.is_mmapped = false;
}
if (ui->rbNMA->isChecked()) {
chunkSimple.non_main_arena = true;
} else {
chunkSimple.non_main_arena = false;
}
if (ui->rbPI->isChecked()) {
chunkSimple.prev_inuse = true;
} else {
chunkSimple.prev_inuse = false;
}
if (Core()->writeHeapChunk(&chunkSimple)) {
updateFields();
QMessageBox::information(this, tr("Chunk saved"),
tr("Chunk header successfully overwritten"));
} else {
QMessageBox::information(this, tr("Chunk not saved"),
tr("Chunk header not successfully overwritten"));
}
break;
case QMessageBox::Cancel:
// Cancel was clicked
break;
}
}

View File

@ -16,6 +16,9 @@ public:
explicit GlibcHeapInfoDialog(RVA offset, QString status, QWidget *parent = nullptr);
~GlibcHeapInfoDialog();
private slots:
void saveChunkInfo();
private:
Ui::GlibcHeapInfoDialog *ui;
void updateFields();

View File

@ -46,7 +46,7 @@
<string>Size of the heap chunk including metadata</string>
</property>
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -63,7 +63,7 @@
<string>Link to next free chunk in bin's linked list</string>
</property>
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -80,7 +80,7 @@
<string>Link to previous free chunk in bin's linked list</string>
</property>
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -90,7 +90,7 @@
<string>Link to next larger free chunk (only for large chunks)</string>
</property>
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -114,7 +114,7 @@
<string>Link to next smaller free chunk (for large chunks)</string>
</property>
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -124,7 +124,7 @@
<string>Size of previous chunk (if free)</string>
</property>
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
@ -189,6 +189,13 @@
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="saveButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@ -1,4 +1,5 @@
#include <dialogs/GlibcHeapBinsDialog.h>
#include <dialogs/ArenaInfoDialog.h>
#include "GlibcHeapWidget.h"
#include "ui_GlibcHeapWidget.h"
#include "core/MainWindow.h"
@ -38,6 +39,7 @@ GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent)
connect(chunkInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewChunkInfo);
connect(binInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewBinInfo);
connect(ui->binsButton, &QPushButton::clicked, this, &GlibcHeapWidget::viewBinInfo);
connect(ui->arenaButton, &QPushButton::clicked, this, &GlibcHeapWidget::viewArenaInfo);
addressableItemContextMenu.addAction(chunkInfoAction);
addressableItemContextMenu.addAction(binInfoAction);
@ -214,3 +216,18 @@ void GlibcHeapWidget::viewBinInfo()
GlibcHeapBinsDialog heapBinsDialog(modelHeap->arena_addr, main, this);
heapBinsDialog.exec();
}
void GlibcHeapWidget::viewArenaInfo()
{
// find the active arena
Arena currentArena;
for (auto &arena : arenas) {
if (arena.offset == modelHeap->arena_addr) {
currentArena = arena;
break;
}
}
ArenaInfoDialog arenaInfoDialog(currentArena, this);
arenaInfoDialog.exec();
}

View File

@ -44,6 +44,7 @@ private slots:
void onCurrentChanged(const QModelIndex &current, const QModelIndex &previous);
void viewChunkInfo();
void viewBinInfo();
void viewArenaInfo();
private:
void updateArenas();

View File

@ -19,25 +19,38 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="arenaSelector"/>
</item>
<item>
<widget class="QPushButton" name="binsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>View bins info for an arena</string>
</property>
<property name="text">
<string>Bins</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="arenaSelector"/>
</item>
<item>
<widget class="QPushButton" name="arenaButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Arena</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="binsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>View bins info for an arena</string>
</property>
<property name="text">
<string>Bins</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@ -11,6 +11,8 @@ HeapBinsGraphView::HeapBinsGraphView(QWidget *parent, RzHeapBin *bin, MainWindow
connect(chunkInfoAction, &QAction::triggered, this, &HeapBinsGraphView::viewChunkInfo);
bits = Core()->getArchBits();
enableAddresses(true);
}
@ -30,7 +32,8 @@ void HeapBinsGraphView::loadCurrentGraph()
QVector<GraphHeapChunk> chunks;
// if the bin is a fastbin or not
bool fast = QString(heapBin->type) == QString("Fast");
bool singleLinkedBin = QString(heapBin->type) == QString("Fast")
|| QString(heapBin->type) == QString("Tcache");
// store info about the chunks in a vector for easy access
CutterRListForeach(heapBin->chunks, iter, RzHeapChunkListItem, item)
@ -45,7 +48,7 @@ void HeapBinsGraphView::loadCurrentGraph()
+ RHexString(chunkInfo->size) + "\nFd: " + RAddressString(chunkInfo->fd);
// fastbins lack bk pointer
if (!fast) {
if (!singleLinkedBin) {
content += "\nBk: " + RAddressString(chunkInfo->bk);
}
graphHeapChunk.fd = chunkInfo->fd;
@ -55,8 +58,8 @@ void HeapBinsGraphView::loadCurrentGraph()
free(chunkInfo);
}
// fast bins have single linked list and other bins have double linked list
if (fast) {
// fast and tcache bins have single linked list and other bins have double linked list
if (singleLinkedBin) {
display_single_linked_list(chunks);
} else {
display_double_linked_list(chunks);
@ -68,19 +71,31 @@ void HeapBinsGraphView::loadCurrentGraph()
void HeapBinsGraphView::display_single_linked_list(QVector<GraphHeapChunk> chunks)
{
bool tcache = QString(heapBin->type) == QString("Tcache");
int ptrSize = bits;
// add the graph block for the bin
GraphLayout::GraphBlock gbBin;
gbBin.entry = 1;
gbBin.edges.emplace_back(heapBin->fd);
QString content = tr(heapBin->type) + tr("bin ") + QString::number(heapBin->bin_num);
content += "\nFd: " + RAddressString(heapBin->fd);
if (tcache) {
content += "\nEntry: " + RAddressString(heapBin->fd);
} else {
content += "\nFd: " + RAddressString(heapBin->fd);
}
addBlock(gbBin, content);
// add the graph blocks for the chunks
for (int i = 0; i < chunks.size(); i++) {
GraphLayout::GraphBlock gbChunk;
gbChunk.entry = chunks[i].addr;
gbChunk.edges.emplace_back(chunks[i].fd);
if (tcache && chunks[i].fd) {
// base_address = address - 2 * PTR_SIZE
gbChunk.edges.emplace_back(chunks[i].fd - 2 * ptrSize);
} else {
gbChunk.edges.emplace_back(chunks[i].fd);
}
if (i == chunks.size() - 1 && heapBin->message) {
chunks[i].content += "\n" + QString(heapBin->message);

View File

@ -33,6 +33,7 @@ private:
void display_double_linked_list(QVector<GraphHeapChunk>);
QAction *chunkInfoAction;
RVA selectedBlock;
int bits;
};
#endif // CUTTER_HEAPBINSGRAPHVIEW_H