[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tool] when writing to openssl as a part of macOS/iOS code-signing, flush the stdin stream before closing it #150120

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
55 changes: 49 additions & 6 deletions packages/flutter_tools/lib/src/base/process.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,26 +244,69 @@ abstract class ProcessUtils {
/// ```
///
/// However it did not catch a [SocketException] on Linux.
///
/// As part of making sure errors are caught, this function will call [flush]
/// on [stdin] to ensure that [line] is written to the pipe before this
/// function returns. This means completion will be blocked if the kernel
/// buffer of the pipe is full.
static Future<void> writelnToStdinGuarded({
required IOSink stdin,
required String line,
required void Function(Object, StackTrace) onError,
}) async {
await _writeToStdinGuarded(
stdin: stdin,
content: line,
onError: onError,
isLine: true,
);
}

/// Please see [writelnToStdinGuarded].
///
/// This calls `stdin.write` instead of `stdin.writeln`.
static Future<void> writeToStdinGuarded({
christopherfujino marked this conversation as resolved.
Show resolved Hide resolved
required IOSink stdin,
required String content,
required void Function(Object, StackTrace) onError,
}) async {
await _writeToStdinGuarded(
stdin: stdin,
content: content,
onError: onError,
isLine: false,
);
}

static Future<void> _writeToStdinGuarded({
required IOSink stdin,
required String content,
required void Function(Object, StackTrace) onError,
required bool isLine,
}) async {
final Completer<void> completer = Completer<void>();

void writeFlushAndComplete() {
stdin.writeln(line);
stdin.flush().whenComplete(() {
if (!completer.isCompleted) {
completer.complete();
}
if (isLine) {
stdin.writeln(content);
} else {
stdin.write(content);
}
stdin.flush().then((_) {
completer.complete();
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not merging this yet because we probably should have a .onError call here.

}

runZonedGuarded(
writeFlushAndComplete,
(Object error, StackTrace stackTrace) {
onError(error, stackTrace);
try {
onError(error, stackTrace);
} on Exception catch (e) {
completer.completeError(e);
}

// We may have already completed with an error in the above catch block.
if (!completer.isCompleted) {
completer.complete();
}
Expand Down
10 changes: 9 additions & 1 deletion packages/flutter_tools/lib/src/ios/code_signing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,15 @@ Future<String?> _getCodeSigningIdentityDevelopmentTeam({

final Process opensslProcess = await processUtils.start(
const <String>['openssl', 'x509', '-subject']);
await (opensslProcess.stdin..write(signingCertificateStdout)).close();

await ProcessUtils.writeToStdinGuarded(
stdin: opensslProcess.stdin,
content: signingCertificateStdout,
onError: (Object? error, _) {
throw Exception('Unexpected error when writing to openssl: $error');
},
Comment on lines +238 to +240
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this ends up becoming a pattern (it very much probably will), we may want to consider creating like a writeToStdinUnsafe method that accepts something like a message or taskDescription. This new method would then throw a custom exception type from that.

);
await opensslProcess.stdin.close();

final String opensslOutput = await utf8.decodeStream(opensslProcess.stdout);
// Fire and forget discard of the stderr stream so we don't hold onto resources.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import 'package:flutter_tools/src/ios/code_signing.dart';

import '../../src/common.dart';
import '../../src/fake_process_manager.dart';
import '../../src/fakes.dart';

const String kCertificates = '''
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
Expand Down Expand Up @@ -581,6 +582,59 @@ void main() {
expect(developmentTeam, isNull);
expect(processManager, hasNoRemainingExpectations);
});

testWithoutContext('handles stdin pipe breaking on openssl process', () async {
final StreamSink<List<int>> stdinSink = ClosedStdinController();

final Completer<void> completer = Completer<void>();
const String certificates = '''
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
1 valid identities found''';
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['which', 'security'],
),
const FakeCommand(
command: <String>['which', 'openssl'],
),
const FakeCommand(
command: <String>['security', 'find-identity', '-p', 'codesigning', '-v'],
stdout: certificates,
),
const FakeCommand(
command: <String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
stdout: 'This is a fake certificate',
),
FakeCommand(
command: const <String>['openssl', 'x509', '-subject'],
stdin: IOSink(stdinSink),
stdout: 'subject= /CN=iPhone Developer: Profile 1 (1111AAAA11)/OU=3333CCCC33/O=My Team/C=US',
completer: completer,
),
]);

Future<Map<String, String>?> getCodeSigningIdentities() => getCodeSigningIdentityDevelopmentTeamBuildSetting(
buildSettings: <String, String>{
andrewkolos marked this conversation as resolved.
Show resolved Hide resolved
'bogus': 'bogus',
},
platform: macosPlatform,
processManager: processManager,
logger: logger,
config: testConfig,
terminal: testTerminal,
);

await expectLater(
() => getCodeSigningIdentities(),
throwsA(
const TypeMatcher<Exception>().having(
(Exception e) => e.toString(),
'message',
equals('Exception: Unexpected error when writing to openssl: SocketException: Bad pipe'),
),
),
);
});
});
}

Expand Down
9 changes: 9 additions & 0 deletions packages/flutter_tools/test/src/fakes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -737,3 +737,12 @@ class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher {
closed = true;
}
}

class ClosedStdinController extends Fake implements StreamSink<List<int>> {
@override
Future<Object?> addStream(Stream<List<int>> stream) async => throw const SocketException('Bad pipe');

@override Future<Object?> close() async {
andrewkolos marked this conversation as resolved.
Show resolved Hide resolved
return null;
}
}