Recursion depth equalled history length, so destroying or clearing a buffer with a few hundred thousand undo steps overflowed the stack. UndoSystem::clear() also freed only the first root branch, leaking the root's siblings; it now frees the whole forest. Audit finding M4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
23 lines
827 B
C
23 lines
827 B
C
#pragma once
|
|
#include "UndoNode.h"
|
|
|
|
|
|
struct UndoTree {
|
|
UndoNode *root = nullptr; // first edit ever
|
|
UndoNode *current = nullptr; // current state of buffer
|
|
UndoNode *saved = nullptr; // points to node matching last save (for dirty flag)
|
|
UndoNode *pending = nullptr; // in-progress batch (detached)
|
|
|
|
// Frees the entire node graph (root's subtree/branches plus any detached
|
|
// pending node). `current`/`saved` are aliases into that same graph, not
|
|
// separately owned. Without this, closing/reloading/resetting a buffer
|
|
// (which replaces or destroys its UndoTree) leaks every UndoNode ever
|
|
// created for it.
|
|
~UndoTree();
|
|
};
|
|
|
|
|
|
// Frees `first`, its whole child subtree, and every node on its `next`
|
|
// (sibling) chain. Iterative, so history depth cannot overflow the stack.
|
|
void FreeUndoForest(UndoNode *first);
|