Fix use-after-free/double-free crash when leaving an empty lyrics syllable#34132
Fix use-after-free/double-free crash when leaving an empty lyrics syllable#34132denisfalqueto wants to merge 1 commit into
Conversation
…ed lyrics syllable TextBase::endEdit() rolls back the "add element" command when a newly created text (e.g. a Lyrics syllable created while navigating with Space/Shift+Space) is left empty. Since 67b5fa1 ("Don't leak items owned by UndoableCommand deleted in `unwind`"), that rollback's unwind() now calls AddElement::cleanup(false), which synchronously deletes the element being edited (`this`). endEdit() kept using `this`/`ted` right after the rollback (setting ted->deleteText = true, calling isLyrics(), etc.), and that deleteText flag scheduled a second, redundant deletion via TextEditData::~TextEditData() -> deleteLater() -> deletePostponed(). The result was a use-after-free followed by a double free, crashing in mu::engraving::deletePostponed (cmd.cpp) - reproducible by creating an empty lyrics syllable and pressing Shift+Space to navigate away from it without typing anything. Detect up front when the rollback is about to delete `this` (i.e. the "add element" command for this element is part of the transaction being rolled back) and return immediately afterwards instead of touching the now-dangling object. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis change modifies 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsLinked repositories: Public OSS repositories can only analyze public repositories installed in this organization. No linked repositories were analyzed; skipped Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
See also #34013 |
|
Thanks for this! However it is covered by #34013 |
Fixes #34131
Problem
MuseScore Studio crashes when, while editing lyrics, the user navigates to
a note/rest that has no lyrics yet (which auto-creates an empty
Lyricssyllable) and then leaves that syllable without typing anything into
it — e.g. by pressing
Shift+Spaceto go back to the previous syllable.Steps to reproduce
Ctrl+L).Space(or-) to move to the nextnote. This creates a new, empty
Lyricselement on that note andplaces the cursor in it.
Shift+Spaceto navigate back tothe previous syllable.
Backtrace (Debug build)
Root cause
In
TextBase::endEdit()(src/engraving/editing/textedit.cpp), when thetext being edited is empty and was newly added in the current undo
transaction, the code rolls back the "add element" command:
score()->endCmd(true)rolls back viaUndoableTransaction::unwind()(
src/engraving/editing/transaction/undostack.cpp), which now callscommand->cleanup(false)on every undone command. For theAddElementcommand that created this
Lyrics,AddElement::cleanup()(
src/engraving/editing/addremoveelement.cpp) does:This synchronously deletes the very
Lyricsobject thatTextBase::endEdit()is still executing on (this). Execution thenreturns to
endEdit(), which keeps using the now-danglingthis/ted(
ted->deleteText = true;,isLyrics(),toLyrics(this), ...) — ause-after-free.
Because
ted->deleteTextgets set,TextEditData's destructor laterschedules a second deletion of the same, already-freed object via
deleteLater():The corrupted pointer(s) end up in
MasterScore's postponed-deletionlist, and the next
MasterScore::update()crashes trying todeletethem a second time (
deletePostponed,cmd.cpp:289) — the observedcrash.
Bisected culprit
I bisected this to 67b5fa1 ("Don't leak
items owned by UndoableCommand deleted in
unwind"), which added thesynchronous
command->cleanup(false)call tounwind(). Before thatcommit,
unwind()only deleted the undo command, not the element itreferred to, so the element stayed alive for the separate
deleteText/deleteLater()cleanup path introduced in1f0c8cc ("Don't leak
newly-added-but-removed-because-empty text objects"), itself built on
top of 9c178a6 which introduced the
score()->endCmd(true)rollback for this case.So this is a case of two independent, previously-correct leak fixes that
now conflict:
67b5fa12a4madeunwind()delete elementssynchronously, without accounting for the fact that
TextBase::endEdit()'snewlyAddedbranch already relies on aseparate, deferred deletion mechanism for the same object.
Fix
Detect ahead of time whether the upcoming rollback will delete
this(i.e. whether the "add element" command for
thisis part of thetransaction being rolled back — the same condition already checked a
few lines above, when merging transactions), and if so, return
immediately after
endCmd(true)without touchingthis/tedagain.Note on issue #31986
While investigating, I compared this crash against the older, still-open
issue #31986 ("Crash if lyrics text is empty or contains line breaks"),
since GitHub suggested it as a possible duplicate. They are not the
same bug:
Crash if lyrics text is empty or contains line breaks #31986 predates the culprit commit above (
67b5fa12a4, several monthslater than Crash if lyrics text is empty or contains line breaks #31986's last activity), so it cannot share this root
cause.
Reproducing Crash if lyrics text is empty or contains line breaks #31986's steps (paste multi-line text into a lyrics,
save/reload, then repeatedly press
-/Space on another lyrics) stillcrashes on
maineven with this fix applied, but with a differentbacktrace:
That crash is a dangling
Lyrics*left insideChordRest::lyrics()(dereferenced later during layout in
chordRestShape,src/engraving/rendering/score/chordlayout.cpp:3367-3371), which is adistinct use-after-free unrelated to the undo/rollback path fixed
here. I'll leave a comment on Crash if lyrics text is empty or contains line breaks #31986 with these findings; it will need
a separate fix.
Testing
engraving+MuseScoreStudio(Debug) and confirmed undergdbthat the original repro steps no longer crash with this fix.the exact backtrace above, isolating the regression to
67b5fa12a4.