Skip to content

[macOS] SIGABRT in TextInputModel::UpdateComposingText: unguarded u16string::replace throws std::out_of_range when a third-party Chinese IME sends setMarkedText during input-source switch #190704

Closed

Description

@Scorpiolol

Steps to reproduce

Not reliably reproducible on demand — this is from a production (TestFlight) crash report on macOS. Observed conditions:

  1. A macOS Flutter app with a focused text input, using a third-party Chinese IME (Tencent WeType, com.tencent.inputmethod.wetype).
  2. Marked (composing) text is active or was recently active.
  3. The user switches the input source (the crashing stack goes through -[IMKClient_Modern switchedInputMode:completionHandler:]), which makes the IME deliver setMarkedText:selectedRange:replacementRange: via XPC.
  4. If the engine's TextInputModel composing range is out of sync with the actual text buffer at that moment (framework TextInput.setEditingState racing with the IME's TSM event chain), the engine performs an out-of-bounds std::u16string::replace and the process aborts.

Expected results

Text input state is clamped/validated; a desynced composing range must never abort the whole process.

Actual results

SIGABRT on the main thread. std::out_of_range is thrown inside flutter::TextInputModel::UpdateComposingText and never caught, so libc++ calls abort().

Root cause

engine/src/flutter/shell/platform/common/text_input_model.cc (line 80 on engine revision 42d3d75a56, unchanged on current master as published on api.flutter.dev):

void TextInputModel::UpdateComposingText(const std::u16string& text,
                                         const TextRange& selection) {
  // Preserve selection if we get a no-op update to the composing region.
  if (text.length() == 0 && composing_range_.collapsed()) {
    return;
  }
  const TextRange& rangeToDelete =
      composing_range_.collapsed() ? selection_ : composing_range_;
  text_.replace(rangeToDelete.start(), rangeToDelete.length(), text);  // <-- no bounds check
  ...
}

std::basic_string::replace(pos, len, str) throws std::out_of_range when pos > size(). Neither rangeToDelete.start() nor its length is validated against text_.length(). Related state-desync reports over the years: #64741, #100272, #92060. A neighboring crash in the same plugin (insertText / NSAttributedString) was recently fixed in #176329, but this UpdateComposingText path is still unguarded.

Suggested fix — clamp before replacing (or bail out and resync):

const size_t start = std::min(rangeToDelete.start(), text_.length());
const size_t length = std::min(rangeToDelete.length(), text_.length() - start);
text_.replace(start, length, text);

Crash log (symbolicated key frames)

Exception Type:    EXC_CRASH (SIGABRT)
Triggered by Thread: 0 (input method 1535 com.tencent.inputmethod.wetype), com.apple.main-thread

0   libsystem_kernel.dylib   __pthread_kill + 8
1   libsystem_pthread.dylib  pthread_kill + 296
2   libsystem_c.dylib        abort + 148
3   FlutterMacOS             std::__fl::__libcpp_verbose_abort(char const*, ...) + 48 (verbose_abort.cpp:62)
4   FlutterMacOS             std::__fl::__throw_out_of_range[abi:nn210000](char const*) + 28 (stdexcept:253)
5   FlutterMacOS             std::__fl::basic_string<char16_t, ...>::__throw_out_of_range[abi:nn210000]() + 20 (string:2398)
6   FlutterMacOS             std::__fl::basic_string<char16_t, ...>::replace(unsigned long, unsigned long, char16_t const*, unsigned long) + 504 (string:3263)
8   FlutterMacOS             flutter::TextInputModel::UpdateComposingText(std::__fl::basic_string<char16_t, ...> const&, flutter::TextRange const&) + 148 (text_input_model.cc:80)
9   FlutterMacOS             -[FlutterTextInputPlugin setMarkedText:selectedRange:replacementRange:] + 672 (FlutterTextInputPlugin.mm:896)
10  AppKit                   +[NSInputAnalytics(TrackedActionsManager) allowActionTrackingAnalyticsWithName:forAction:] + 160
11  AppKit                   -[NSTextInputContext(NSInputContext_WithCompletion) setMarkedText:selectedRange:replacementRange:completionHandler:] + 268
...
45  HIToolbox                -[IMKInputSession_Modern setMarkedText:selectionRange:replacementRange:completionHandler:] + 540
...
70  HIToolbox                -[IMKClient_Modern switchedInputMode:completionHandler:] + 416
...
102 FlutterMacOS             -[FlutterTextInputPlugin handleKeyEvent:] + 224 (FlutterTextInputPlugin.mm:637)

FlutterMacOS binary UUID 4C4C44E6-5555-3144-A1FE-1E90541BCD45 — byte-identical to the darwin-x64-release arm64 artifact shipped with Flutter 3.41.9 (engine 42d3d75a56efe1a2e9902f52dc8006099c45d937).

Environment: macOS 26.5.2 (25F84), Apple M5 (arm64), input method Tencent WeType.

Code sample

No minimal reproduction available (race condition with a third-party IME process). The receiving widget was a plain TextField / SuperEditor text input; nothing app-specific is on the crashing stack between AppKit and the engine.

Logs / Flutter Doctor output

flutter doctor -v (trimmed)
[!] Flutter (Channel stable, 3.41.9, on macOS 26.5.2 25F84 darwin-arm64, locale zh-Hans-CN)
    • Flutter version 3.41.9 on channel stable
    • Framework revision 00b0c91f06 (2026-04-29)
    • Engine revision 42d3d75a56
    • Dart version 3.11.5

Activity

  1. added
    a: text inputEntering text in a text field or keyboard related problems
    c: crashStack traces logged to the console
    platform-macosBuilding on or for macOS specifically
    team-macosOwned by the macOS platform team
    on Aug 7, 2026
  2. mbcorona commented on Aug 7, 2026

    @mbcorona
    Member

    Thanks for the symbolicated crash and the precise diagnosis.
    Routing to team-macos with fyi-text-input.

  3. added
    P2Important issues not at the top of the work list
    on Aug 13, 2026
  4. cbracken commented on Aug 13, 2026

    @cbracken
    Member

    This seems likely to be IME-specific; we've definitely seen issues with inconsistent ordering/etc. expectations between IMEs on other platforms (I'm thinking about Sogou IME on Windows as a classic example).

    Regardless it seems like it should be trivial for us to guard against a crash; getting the correct behaviour may be trickier.

  5. justinmc commented on Aug 13, 2026

    @justinmc
    Contributor

    The raciness sounds like it would be fixed if/when we move to a synchronous text input model.

    But yeah makes sense to me to guard against it for now either way.

  6. self-assigned this
    on Aug 13, 2026
  7. added
    triaged-iosTriaged by iOS platform team
    triaged-macosTriaged by the macOS platform team
    and removed
    triaged-iosTriaged by iOS platform team
    on Aug 13, 2026
  8. cbracken commented on Aug 27, 2026

    @cbracken
    Member

    @Scorpiolol I've landed a fix (or at least crash prevention) on master branch. Can you try that out with the third-party IME in question and see if the issue is resolved.

  9. github-actions commented on Sep 10, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  10. locked as resolved and limited conversation to collaborators on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work lista: desktopRunning on desktopa: text inputEntering text in a text field or keyboard related problemsc: crashStack traces logged to the consolefound in release: 3.41Found to occur in 3.41platform-macosBuilding on or for macOS specificallyteam-macosOwned by the macOS platform teamtriaged-macosTriaged by the macOS platform team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions