From 4aa44efca7a503cbede8369742afc02afed924fd Mon Sep 17 00:00:00 2001 From: will wade Date: Fri, 19 Jun 2026 18:49:39 +0100 Subject: [PATCH] Fix editOutput crash: reset cursorPos when editBuffer is cleared MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three CAPI functions cleared editBuffer without resetting cursorPos: - dasher_reset_output_text() - dasher_reset() - dasher_set_alphabet_id() After any of these, cursorPos could be non-zero (e.g. 500) while editBuffer.size() was 0. The next editOutput() call then did: editBuffer.insert(cursorPos, text) // cursorPos=500, size=0 → std::out_of_range exception → SIGABRT This caused 3 of 4 TestFlight crashes on DasherApp build 32 (iPhone 14,2 / iOS 26.5) — all with the identical stack: std::__throw_out_of_range → std::string::insert → dasher_ctx::Interface::editOutput → CSymbolNode::Do() → CDasherModel::OutputTo Fix in two layers: 1. Root cause: reset cursorPos=0 in all three buffer-clear functions so the position and buffer never diverge. 2. Defensive: clamp cursorPos to editBuffer.size() at the top of editOutput() so even an unknown future caller can't trigger the same exception. Signed-off-by: will wade --- src/CAPI.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CAPI.cpp b/src/CAPI.cpp index fd591976..8b24c5d1 100644 --- a/src/CAPI.cpp +++ b/src/CAPI.cpp @@ -410,6 +410,9 @@ struct dasher_ctx { return static_cast(m_owner->cursorPos); } void editOutput(const std::string& strText, Dasher::CDasherNode* pCause) override { + if (m_owner->cursorPos > m_owner->editBuffer.size()) { + m_owner->cursorPos = m_owner->editBuffer.size(); + } m_owner->editBuffer.insert(m_owner->cursorPos, strText); m_owner->cursorPos += strText.size(); if (m_owner->outputCb && !strText.empty()) m_owner->outputCb(0, strText.c_str(), m_owner->outputCbUserData); @@ -643,11 +646,13 @@ DASHER_API const char* dasher_get_output_text(dasher_ctx* ctx) { DASHER_API void dasher_reset_output_text(dasher_ctx* ctx) { if (!ctx) return; ctx->editBuffer.clear(); + ctx->cursorPos = 0; } DASHER_API void dasher_reset(dasher_ctx* ctx) { if (!ctx || !ctx->intf) return; ctx->editBuffer.clear(); + ctx->cursorPos = 0; ctx->intf->SetOffset(0, true); } @@ -660,6 +665,7 @@ DASHER_API const char* dasher_get_alphabet_id(dasher_ctx* ctx) { DASHER_API void dasher_set_alphabet_id(dasher_ctx* ctx, const char* alphabet_id) { if (!ctx || !ctx->intf || !alphabet_id) return; ctx->editBuffer.clear(); + ctx->cursorPos = 0; if (!ctx->realized) { ctx->pendingAlphabet = alphabet_id; return;