diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPConnection.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPConnection.m index cbf9f013..15056414 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPConnection.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPConnection.m @@ -24,6 +24,7 @@ #import "internal/platform/implementation/apple/Mediums/BLE/GNCMConnection.h" enum { kL2CAPPacketLength = 4 }; +static const NSUInteger kGNCBLEL2CAPMaxFrameLength = 5 * 1024 * 1024; // 5 MB static const CGFloat kRequestDataConnectionDelayInSeconds = 0.0; static const UInt8 kRequestDataConnectionTimeoutInSeconds = 5; @@ -266,6 +267,20 @@ static NSData *PrefixLengthData(NSData *data) { } _expectedDataLength = CFSwapInt32BigToHost( *(int *)([[data subdataWithRange:NSMakeRange(0, kL2CAPPacketLength)] bytes])); + if (_expectedDataLength == 0 || _expectedDataLength > kGNCBLEL2CAPMaxFrameLength) { + GNCLoggerError(@"[NEARBY] Rejecting L2CAP frame: declared length %lu out of range " + @"(max %lu); closing.", + (unsigned long)_expectedDataLength, + (unsigned long)kGNCBLEL2CAPMaxFrameLength); + _expectedDataLength = 0; + [_stream close]; + if (_connectionHandlers.disconnectedHandler) { + dispatch_async(_callbackQueue, ^{ + _connectionHandlers.disconnectedHandler(); + }); + } + return nil; + } } NSUInteger realDataLength = data.length - kL2CAPPacketLength; if (realDataLength < _expectedDataLength) { diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.m index a9ab14ee..47ae7429 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.m @@ -74,6 +74,9 @@ enum { READ_BUFFER_SIZE = 409600 }; /// Whether the stream is closed. BOOL _closed; + + /// Buffer for reading data from the input stream. + uint8_t _readBuffer[READ_BUFFER_SIZE]; } #pragma mark Public @@ -319,12 +322,11 @@ enum { READ_BUFFER_SIZE = 409600 }; /// Receives data from device and invokes |_receivedDataBlock|. - (void)receiveStreamData { dispatch_assert_queue_debug(_streamQueue); - uint8_t readBuffer[READ_BUFFER_SIZE]; - NSInteger bytesRead = [self.inputStream read:readBuffer maxLength:READ_BUFFER_SIZE]; + NSInteger bytesRead = [self.inputStream read:_readBuffer maxLength:READ_BUFFER_SIZE]; if (bytesRead > 0) { NSMutableData *data = [NSMutableData data]; - [data appendBytes:readBuffer length:(NSUInteger)bytesRead]; + [data appendBytes:_readBuffer length:(NSUInteger)bytesRead]; if (_verboseLoggingEnabled) { GNCLoggerDebug(@"[NEARBY] Stream data from device of length %@", @(data.length)); diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPConnectionTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPConnectionTest.m index 37e5793d..d2384bbd 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPConnectionTest.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPConnectionTest.m @@ -140,6 +140,32 @@ static const NSTimeInterval kTestTimeout = 1.0; XCTAssertNil(realData); } +- (void)testExtractRealDataFromData_oversizedData { + _connection = [self createConnectionWithIncoming:YES]; + + XCTestExpectation *disconnectionExpectation = + [self expectationWithDescription:@"disconnection handler"]; + _connection.connectionHandlers = [GNCMConnectionHandlers + payloadHandler:^(NSData *data) { + XCTFail(@"Unexpected payload"); + } + disconnectedHandler:^{ + [disconnectionExpectation fulfill]; + }]; + + // 6 MB frame length + uint32_t oversizedLength = 6 * 1024 * 1024; + uint32_t lengthBigEndian = CFSwapInt32HostToBig(oversizedLength); + NSMutableData *prefixData = [NSMutableData dataWithCapacity:sizeof(uint32_t)]; + [prefixData appendBytes:&lengthBigEndian length:sizeof(uint32_t)]; + + NSData *realData = [_connection extractRealDataFromData:prefixData]; + XCTAssertNil(realData); + XCTAssertEqual(_connection.expectedDataLength, 0); + + [self waitForExpectationsWithTimeout:kTestTimeout handler:nil]; +} + - (void)testExtractRealDataFromData_moreThanExpectedData { _connection = [self createConnectionWithIncoming:YES]; NSData *testData = [self createDataWithLength:10];