From b242366293a22cf27ff8952e93819871494060ba Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 17 Mar 2021 23:12:04 -0700 Subject: [PATCH] Decode empty message to nil in standard codec This updates Flutter.*Codec `decode:` implementations to treat empty messages as equivalent to nil or NSNull, which avoids a crash when we subsequently otherwise try to read the type/value from the message. This handles the case where the sender were to send `null` over the channel. e.g., final channel = BasicMessageChannel('somechannel', StandardMessageCodec()); channel.send(null); It also updates the macOS embedder to send nil to the decoder when a zero-length message is received in order to be consistent with the iOS embedding. Previously, the macOS embedder always encoded platform messages as NSData regardless of length: https://github.com/flutter/engine/blob/ba93431e6885f94614d91c291ae0336bac6b70b0/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm#L498-L500 The iOS embedder did not have this issue since it special-cased zero-length messages as nil: https://github.com/flutter/engine/blob/ba93431e6885f94614d91c291ae0336bac6b70b0/shell/platform/darwin/ios/framework/Source/platform_message_router.mm#L23-L26 Bug: https://github.com/flutter/flutter/issues/78003 --- .../darwin/common/framework/Source/FlutterCodecs.mm | 2 +- .../common/framework/Source/FlutterStandardCodec.mm | 2 +- .../common/framework/Source/flutter_codecs_unittest.mm | 5 +++++ .../framework/Source/flutter_standard_codec_unittest.mm | 6 ++++++ .../darwin/macos/framework/Source/FlutterEngine.mm | 9 ++++++--- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm index a73ede961e1ac..27bbbce5c9435 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm @@ -78,7 +78,7 @@ - (NSData*)encode:(id)message { } - (id)decode:(NSData*)message { - if (message == nil) + if ([message length] == 0) return nil; BOOL isSimpleValue = NO; id decoded = nil; diff --git a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm index 984dfff8435b4..9019f5bff953a 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm @@ -45,7 +45,7 @@ - (NSData*)encode:(id)message { } - (id)decode:(NSData*)message { - if (message == nil) + if ([message length] == 0) return nil; FlutterStandardReader* reader = [_readerWriter readerWithData:message]; id value = [reader readValue]; diff --git a/shell/platform/darwin/common/framework/Source/flutter_codecs_unittest.mm b/shell/platform/darwin/common/framework/Source/flutter_codecs_unittest.mm index ee515934fcf83..226afbe82a3c0 100644 --- a/shell/platform/darwin/common/framework/Source/flutter_codecs_unittest.mm +++ b/shell/platform/darwin/common/framework/Source/flutter_codecs_unittest.mm @@ -42,6 +42,11 @@ ASSERT_TRUE([value isEqualTo:decoded]); } +TEST(FlutterJSONCodec, CanDecodeZeroLength) { + FlutterJSONMessageCodec* codec = [FlutterJSONMessageCodec sharedInstance]; + ASSERT_TRUE([codec decode:[NSData data]] == nil); +} + TEST(FlutterJSONCodec, CanEncodeAndDecodeNil) { FlutterJSONMessageCodec* codec = [FlutterJSONMessageCodec sharedInstance]; ASSERT_TRUE([codec encode:nil] == nil); diff --git a/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm b/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm index 91ffd60935bc9..34ab6f1c0040b 100644 --- a/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm +++ b/shell/platform/darwin/common/framework/Source/flutter_standard_codec_unittest.mm @@ -30,6 +30,12 @@ void checkEncodeDecode(id value) { ASSERT_TRUE([value isEqual:decoded]); } +TEST(FlutterStandardCodec, CanDecodeZeroLength) { + FlutterStandardMessageCodec* codec = [FlutterStandardMessageCodec sharedInstance]; + id decoded = [codec decode:[NSData data]]; + ASSERT_TRUE(decoded == nil); +} + TEST(FlutterStandardCodec, CanEncodeAndDecodeNil) { checkEncodeDecode(nil, nil); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 517f6e30a3d36..a95a0343fd154 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -495,9 +495,12 @@ - (void)sendUserLocales { } - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message { - NSData* messageData = [NSData dataWithBytesNoCopy:(void*)message->message - length:message->message_size - freeWhenDone:NO]; + NSData* messageData = nil; + if (message->message_size > 0) { + messageData = [NSData dataWithBytesNoCopy:(void*)message->message + length:message->message_size + freeWhenDone:NO]; + } NSString* channel = @(message->channel); __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;