From 60343fa8b56db3718132b6cb45c015b174020ee8 Mon Sep 17 00:00:00 2001 From: Pulak Malhotra <56169176+PulakIIIT@users.noreply.github.com> Date: Fri, 16 Jul 2021 19:18:10 +0530 Subject: [PATCH] Heap widget wrapup (#2716) --- rizin | 2 +- src/CMakeLists.txt | 3 + src/core/Cutter.cpp | 34 ++++++++- src/core/Cutter.h | 13 ++++ src/core/CutterDescriptions.h | 6 ++ src/dialogs/ArenaInfoDialog.cpp | 25 +++++++ src/dialogs/ArenaInfoDialog.h | 25 +++++++ src/dialogs/ArenaInfoDialog.ui | 109 ++++++++++++++++++++++++++++ src/dialogs/GlibcHeapBinsDialog.ui | 14 ++-- src/dialogs/GlibcHeapInfoDialog.cpp | 63 ++++++++++++++-- src/dialogs/GlibcHeapInfoDialog.h | 3 + src/dialogs/GlibcHeapInfoDialog.ui | 19 +++-- src/widgets/GlibcHeapWidget.cpp | 17 +++++ src/widgets/GlibcHeapWidget.h | 1 + src/widgets/GlibcHeapWidget.ui | 51 ++++++++----- src/widgets/HeapBinsGraphView.cpp | 27 +++++-- src/widgets/HeapBinsGraphView.h | 1 + 17 files changed, 370 insertions(+), 43 deletions(-) create mode 100644 src/dialogs/ArenaInfoDialog.cpp create mode 100644 src/dialogs/ArenaInfoDialog.h create mode 100644 src/dialogs/ArenaInfoDialog.ui diff --git a/rizin b/rizin index 381f22d7..254c5119 160000 --- a/rizin +++ b/rizin @@ -1 +1 @@ -Subproject commit 381f22d7cc81bf2eb6663a690a715ff4f8f09373 +Subproject commit 254c5119e3563d05d02665cabbcba003c921cee8 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 147caa56..1d798902 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index b5856535..12d50fce 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -1592,6 +1592,12 @@ QVector CutterCore::getHeapChunks(RVA arena_addr) return chunks_vector; } +int CutterCore::getArchBits() +{ + CORE_LOCK(); + return core->dbg->bits; +} + QVector CutterCore::getArenas() { CORE_LOCK(); @@ -1606,6 +1612,12 @@ QVector 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 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 diff --git a/src/core/Cutter.h b/src/core/Cutter.h index d728d18c..c9245b74 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -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 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(); /** diff --git a/src/core/CutterDescriptions.h b/src/core/CutterDescriptions.h index 2466d2d9..12170f92 100644 --- a/src/core/CutterDescriptions.h +++ b/src/core/CutterDescriptions.h @@ -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) diff --git a/src/dialogs/ArenaInfoDialog.cpp b/src/dialogs/ArenaInfoDialog.cpp new file mode 100644 index 00000000..53634a75 --- /dev/null +++ b/src/dialogs/ArenaInfoDialog.cpp @@ -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; +} diff --git a/src/dialogs/ArenaInfoDialog.h b/src/dialogs/ArenaInfoDialog.h new file mode 100644 index 00000000..73f45345 --- /dev/null +++ b/src/dialogs/ArenaInfoDialog.h @@ -0,0 +1,25 @@ +#ifndef ARENAINFODIALOG_H +#define ARENAINFODIALOG_H + +#include +#include + +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 diff --git a/src/dialogs/ArenaInfoDialog.ui b/src/dialogs/ArenaInfoDialog.ui new file mode 100644 index 00000000..31e1cd70 --- /dev/null +++ b/src/dialogs/ArenaInfoDialog.ui @@ -0,0 +1,109 @@ + + + ArenaInfoDialog + + + + 0 + 0 + 400 + 202 + + + + Dialog + + + + + + + + Top + + + + + + + Next + + + + + + + Next free + + + + + + + System Memory + + + + + + + Max Memory + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + Last Remainder + + + + + + + true + + + + + + + + + + diff --git a/src/dialogs/GlibcHeapBinsDialog.ui b/src/dialogs/GlibcHeapBinsDialog.ui index 36c7a689..1e45ee9a 100644 --- a/src/dialogs/GlibcHeapBinsDialog.ui +++ b/src/dialogs/GlibcHeapBinsDialog.ui @@ -4,10 +4,10 @@ - 0 - 0 - 883 - 544 + 0 + 0 + 883 + 544 @@ -27,7 +27,11 @@ - + + + true + + diff --git a/src/dialogs/GlibcHeapInfoDialog.cpp b/src/dialogs/GlibcHeapInfoDialog.cpp index 9e9f8c80..c22d6122 100644 --- a/src/dialogs/GlibcHeapInfoDialog.cpp +++ b/src/dialogs/GlibcHeapInfoDialog.cpp @@ -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; + } +} \ No newline at end of file diff --git a/src/dialogs/GlibcHeapInfoDialog.h b/src/dialogs/GlibcHeapInfoDialog.h index 88cbb4c4..684628e0 100644 --- a/src/dialogs/GlibcHeapInfoDialog.h +++ b/src/dialogs/GlibcHeapInfoDialog.h @@ -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(); diff --git a/src/dialogs/GlibcHeapInfoDialog.ui b/src/dialogs/GlibcHeapInfoDialog.ui index 48f19c90..a41d4c73 100644 --- a/src/dialogs/GlibcHeapInfoDialog.ui +++ b/src/dialogs/GlibcHeapInfoDialog.ui @@ -46,7 +46,7 @@ Size of the heap chunk including metadata - true + false @@ -63,7 +63,7 @@ Link to next free chunk in bin's linked list - true + false @@ -80,7 +80,7 @@ Link to previous free chunk in bin's linked list - true + false @@ -90,7 +90,7 @@ Link to next larger free chunk (only for large chunks) - true + false @@ -114,7 +114,7 @@ Link to next smaller free chunk (for large chunks) - true + false @@ -124,7 +124,7 @@ Size of previous chunk (if free) - true + false @@ -189,6 +189,13 @@ + + + + Save + + + diff --git a/src/widgets/GlibcHeapWidget.cpp b/src/widgets/GlibcHeapWidget.cpp index 83378ad2..990ee210 100644 --- a/src/widgets/GlibcHeapWidget.cpp +++ b/src/widgets/GlibcHeapWidget.cpp @@ -1,4 +1,5 @@ #include +#include #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(); +} \ No newline at end of file diff --git a/src/widgets/GlibcHeapWidget.h b/src/widgets/GlibcHeapWidget.h index da6f110f..23a4d7bd 100644 --- a/src/widgets/GlibcHeapWidget.h +++ b/src/widgets/GlibcHeapWidget.h @@ -44,6 +44,7 @@ private slots: void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); void viewChunkInfo(); void viewBinInfo(); + void viewArenaInfo(); private: void updateArenas(); diff --git a/src/widgets/GlibcHeapWidget.ui b/src/widgets/GlibcHeapWidget.ui index ae2bd9f8..ce757d07 100644 --- a/src/widgets/GlibcHeapWidget.ui +++ b/src/widgets/GlibcHeapWidget.ui @@ -19,25 +19,38 @@ - - - - - - - - 0 - 0 - - - - View bins info for an arena - - - Bins - - - + + + + + + + + 0 + 0 + + + + Arena + + + + + + + + 0 + 0 + + + + View bins info for an arena + + + Bins + + + diff --git a/src/widgets/HeapBinsGraphView.cpp b/src/widgets/HeapBinsGraphView.cpp index 6d987468..97ae534d 100644 --- a/src/widgets/HeapBinsGraphView.cpp +++ b/src/widgets/HeapBinsGraphView.cpp @@ -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 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 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); diff --git a/src/widgets/HeapBinsGraphView.h b/src/widgets/HeapBinsGraphView.h index 99b8c128..1f3e6f6e 100644 --- a/src/widgets/HeapBinsGraphView.h +++ b/src/widgets/HeapBinsGraphView.h @@ -33,6 +33,7 @@ private: void display_double_linked_list(QVector); QAction *chunkInfoAction; RVA selectedBlock; + int bits; }; #endif // CUTTER_HEAPBINSGRAPHVIEW_H