Fix crash in FlutterJSONMessageCodec when decoding invalid Unicode#179775
Fix crash in FlutterJSONMessageCodec when decoding invalid Unicode#179775mahmuttaskiran wants to merge 5 commits into
Conversation
Added a safety check in decodeMethodCall to handle cases where JSON decoding returns nil. This prevents the app from crashing when invalid UTF-8 or malformed Unicode is passed into the codec.
There was a problem hiding this comment.
Code Review
This pull request addresses a crash that occurs in FlutterJSONMessageCodec when it attempts to decode invalid Unicode characters. The fix replaces an NSAssert with a standard if check to handle JSON decoding failures gracefully. Instead of crashing, the method now logs an error and returns nil. Consequently, a nil check has been added to decodeMethodCall to handle this new return case, and the corresponding unit test has been updated to verify the new behavior.
My review includes a suggestion to further improve the robustness of decodeMethodCall by not only checking for nil but also ensuring the decoded object is of the expected NSDictionary type. This will prevent potential crashes from valid but structurally incorrect JSON messages.
| NSDictionary* dictionary = [[FlutterJSONMessageCodec sharedInstance] decode:message]; | ||
| if (dictionary == nil) { | ||
| NSLog(@"Invalid JSON method call. Couldn't decode message: %@", message); | ||
| return nil; | ||
| } |
There was a problem hiding this comment.
While the added nil check is good, this is also a good opportunity to make this method more robust by checking that the decoded object is actually a dictionary. If decode: returns a valid JSON object that is not a dictionary (e.g., an array or a string), this code could cause a crash when dictionary[@"method"] is accessed.
Using isKindOfClass: handles both the nil case (as [nil isKindOfClass:] returns false) and the incorrect type case in a single check, making the code safer.
| NSDictionary* dictionary = [[FlutterJSONMessageCodec sharedInstance] decode:message]; | |
| if (dictionary == nil) { | |
| NSLog(@"Invalid JSON method call. Couldn't decode message: %@", message); | |
| return nil; | |
| } | |
| id object = [[FlutterJSONMessageCodec sharedInstance] decode:message]; | |
| if (![object isKindOfClass:[NSDictionary class]]) { | |
| NSLog(@"Invalid JSON method call. The decoded message must be a dictionary."); | |
| return nil; | |
| } | |
| NSDictionary* dictionary = (NSDictionary*)object; |
|
CI had a failure that stopped further tests from running. We need to investigate to determine the root cause. SHA at time of execution: 8c9e422. Possible causes:
A blank commit, or merging to head, will be required to resume running CI for this PR. Error Details: Stack trace: |
|
This changes existing behavior and existing tests verify this. Seems like throwing an exception is WAI. |
Android and iOS behave differently in this case. On Android, the message is encoded using Logging the error and continuing without issuing the system call (in this specific case While debugging the iOS platform code, I found that the crash is triggered by the following system call: If you look closely, the |
|
Hey there, I left some more details on this crash here: #179727 (comment) Instead of not crashing on invalid Unicode, it seems we should update the iOS embedder to accept Dart's strategy for escaping lone surrogate pairs. |
Thank you for the details on the issue. I’ve raised another PR that attempts to resolve it by introducing a sanitizer. I’ve validated that both Android and iOS now behave the same. |
| - (FlutterMethodCall*)decodeMethodCall:(NSData*)message { | ||
| NSDictionary* dictionary = [[FlutterJSONMessageCodec sharedInstance] decode:message]; | ||
| if (dictionary == nil) { | ||
| NSLog(@"Invalid JSON method call. Couldn't decode message: %@", message); |
There was a problem hiding this comment.
In FluterCodecs.h there's NS_ASSUME_NONNULL_BEGIN so I'm expecting the return type to be non-nullable, I'm a bit surprised that the compiler isn't complaining about this.
There was a problem hiding this comment.
The NSLog will be printing out a NSData, not sure what -[NSData description] does, but if it doesn't print out the raw data then it's basically useless, if it does then it may expose sensitive information like PII or passwords in standard output.
There was a problem hiding this comment.
Thank you for the review @LongCatIsLooong
In FluterCodecs.h there's
NS_ASSUME_NONNULL_BEGINso I'm expecting the return type to be non-nullable, I'm a bit surprised that the compiler isn't complaining about this.
"-Wnullable-to-nonnull-conversion" is missing in compiler configs for iOS and Mac in
common/confic.gni
After enabling it, compiler gives error for the following files:
../../flutter/shell/platform/darwin/common/framework/Source/FlutterNSBundleUtils.mm:40:36: error: implicit conversion from nullable pointer 'NSURL * _Nullable' to non-nullable pointer type 'NSURL * _Nonnull' [-Werror,-Wnullable-to-nonnull-conversion]
AND
../../flutter/shell/platform/darwin/common/framework/Source/FlutterStandardCodec.mm:49:65: error: implicit conversion from nullable pointer 'NSData *__strong _Nullable' to non-nullable pointer type 'NSData * _Nonnull' [-Werror,-Wnullable-to-nonnull-conversion]
I'm not sure if it's disabled intentionally. So, I'm leaving it as is
The NSLog will be printing out a
NSData, not sure what-[NSData description]does, but if it doesn't print out the raw data then it's basically useless, if it does then it may expose sensitive information like PII or passwords in standard output.
True. Added debug-only preprocessor guard there.
…thub.com:mahmuttaskiran/flutter into fix/flutterjsonmessagecodec-nil-dictionary-crash
|
@mahmuttaskiran do you think we should close this in favor of #179978? |
Fix crash in FlutterJSONMessageCodec when decoding invalid Unicode
Added a safety check in
decodeMethodCallto handle cases where JSON decoding returns nil. This prevents the app from crashing when invalid UTF-8 is passed into the codec.Fixing #179727
Pre-launch Checklist
///).