Flutter中Objective-C端NSData转Byte*调用C++接口崩溃问题
Flutter结合Objective-C调用C++接口崩溃问题排查
问题场景
在Flutter项目中通过平台通道调用Objective-C原生逻辑,进而调用C加解密接口。按照平台通道规则,Flutter层传递的Uint8List类型在OC端对应FlutterStandardTypedData(NSData子类),实现流程为:接收Flutter传递的字节参数转为NSData,拷贝到Byte数组传入C接口处理,处理完成后将结果转回NSData返回给Flutter。代码运行时触发设备崩溃,需要定位错误。
原有实现代码
Flutter端参数发送逻辑
var data = <String, Uint8List>{ "key": byte, // 类型为Uint8List "value": byteBuffer, // 类型为Uint8List }; Uint8List? byteRes; byteRes = await platform.invokeMethod('SeedDecrypt', data);
Objective-C端(AppDelegate.m)参数接收与处理逻辑
NSData* key = call.arguments[@"key"]; NSData* value = call.arguments[@"value"]; NSUInteger keyLength = [key length]; NSUInteger valueLength = [value length]; Byte* byteKey = (Byte*)malloc(keyLength); Byte* byteValue = (Byte*)malloc(valueLength); memcpy(byteKey, [key bytes], keyLength); memcpy(byteValue, [value bytes], byteLength); DWORD roundKey[32]; // 调用C++ API // 函数原型 : void SeedKey(DWORD* roundKey, BYTE* byteKey); SeedKey(roundKey, byteKey); // 函数原型 : void Decrypt(BYTE* byteValue, DWORD* roundKey); Decrypt(byteValue, roundKey); NSData* res = [NSData dataWithBytes: byteValue length: sizeof(byteValue)]; result(res);
代码中存在的直接错误
- 内存拷贝变量名笔误:拷贝value对应内存时,传入的长度参数是未定义的
byteLength,实际定义的长度变量为valueLength。该问题会导致memcpy读取随机的错误长度值,直接触发内存越界访问崩溃。 - 返回数据长度计算错误:构造返回NSData时使用
sizeof(byteValue)计算长度,byteValue是Byte类型指针,在64位设备上指针固定长度为8字节,和实际解密后的数据长度完全不匹配,会导致返回数据截断,Flutter端解析时也可能触发越界。 - 堆内存未释放:通过
malloc申请的byteKey、byteValue两块堆内存,使用完成后没有调用free释放,会造成内存泄漏,多次调用后内存耗尽也会触发崩溃。 - 缺少参数类型校验:没有校验通道传递的参数类型是否为NSData,若参数传递异常,直接调用
length、bytes等方法会触发找不到方法的崩溃。
修正后的核心处理代码
// 先做参数类型校验,避免非法参数导致崩溃 if (![call.arguments[@"key"] isKindOfClass:[NSData class]] || ![call.arguments[@"value"] isKindOfClass:[NSData class]]) { result([FlutterError errorWithCode:@"INVALID_ARGUMENT" message:@"传入参数类型错误,要求为字节数组" details:nil]); return; } NSData* key = call.arguments[@"key"]; NSData* value = call.arguments[@"value"]; NSUInteger keyLength = [key length]; NSUInteger valueLength = [value length]; Byte* byteKey = (Byte*)malloc(keyLength); Byte* byteValue = (Byte*)malloc(valueLength); // 修正拷贝长度参数为正确定义的valueLength memcpy(byteKey, [key bytes], keyLength); memcpy(byteValue, [value bytes], valueLength); DWORD roundKey[32]; // 调用C++加解密接口 SeedKey(roundKey, byteKey); Decrypt(byteValue, roundKey); // 构造返回结果时使用实际的数据长度valueLength,不要用sizeof计算指针长度 NSData* res = [NSData dataWithBytes:byteValue length:valueLength]; // 释放手动申请的堆内存,避免内存泄漏 free(byteKey); free(byteValue); result(res);
额外注意:需要确认C层的
Decrypt接口是否为原地解密、输出长度是否和输入长度一致,如果解密后长度变化需要按照实际输出长度构造NSData,避免C层内部内存越界。
内容的提问来源于stack exchange,提问作者Jungwon
相关产品推荐
相关产品推荐

