Repository: flutter
Issue: [flutter_tools] Writing to the `stdin` sink of any process risks a crash that can have no useful stack trace
Labels: c: crash,tool,P2,team-tool,triaged-tool

**(Issue description hijacked by @andrewkolos on 7/21/24, who is leading work on this issue. See the edit history for original contents)**

## Issue
Communicating with any `Process` (via writing to `stdin`) spawned the Flutter CLI tool risks an exception being thrown. This exception will be thrown asynchronously (and thus cannot be caught using `catch` as the `write`, `writeln` and `add` APIs are synchronous). Additionally, the stack trace of the callsite may or may not be preserved in these exceptions (this is a race). This is a documented issue in the Dart SDK, see

* https://github.com/dart-lang/sdk/issues/48501

This has had a pronounced impact in the Flutter CLI tool. As of 7/21/24, this has resulted in crashes affecting 0.0618% of Flutter 3.22.2 clients with telemetry enabled. This crash alone is over the target crash rate of the tool.

## Solution

The issue is unlikely to be truly fixed within the Dart SDK (and without a breaking change) due to the choice of `IOSink` as an abstraction from a process' stdin buffer. Because of this and the urgency of this issue, I propose we work around this in the Flutter CLI tool.

Since the filing of this issue, static utility methods have been added to the tool that make any errors properly catchable (or at least preserve their stack trace when they bubble up).  They also prevent exceptions caused by calling `stdin.close` while there is still a pending `write` (recall that `write` is a synchronous method but is asynchronous in its implementation). For an example of one of these utility methods, see [`ProcessUtils.writelnToStdinGuarded`](https://github.com/flutter/flutter/blob/c63733310f2751959df940005e9decc68b39da6e/packages/flutter_tools/lib/src/base/process.dart#L252). These methods should be used instead of writing to a stdin buffer directly using the `Process` API. Here are the known PRs that have/are addressing this issue, or are at least related:

* https://github.com/flutter/flutter/pull/139784
*  https://github.com/flutter/flutter/pull/140996
* https://github.com/flutter/flutter/pull/142356
* https://github.com/flutter/flutter/pull/150120
* https://github.com/flutter/flutter/pull/151146
* https://github.com/flutter/flutter/pull/151183
* https://github.com/flutter/flutter/pull/152187

## Prevention

Ideally, there would be a way to prevent future regressions. However, we cannot use a simple static lint for `IOSink`'s `write`, `writeln` and `add` methods as other `IOSink` implementations have different semantics. Perhaps a more sophisticated lint that does flow analysis (checking where an `IOSink` came from) could reduce false positives, but I do not know how to write such a thing, and I imagine it would not be easy to write such a thing without expert knowledge in writing Dart-analyzing code. In the event where we could not prevent false positives, we can introduce a wrapper utility function that effectively disables the lint (similar to what [`unawaited`](https://api.flutter.dev/flutter/dart-async/unawaited.html) from `dart:async` does for the [`unawaited_futures` lint](https://dart.dev/tools/linter-rules/unawaited_futures)).

As a much more heavy-weight and robust option, the Flutter CLI tool would write its own `Process` abstraction and forbid importing the one from `dart:io`. Such an abstraction would provide a different API for writing to a process that prevents this issue or at least provides more discoverable and accessing means for handling it.

Regardless, implementing a preventative measure is not a requirement for resolving this issue.