Limit maximum L2CAP frame size and move read buffer to instance variable.

PiperOrigin-RevId: 946782423
This commit is contained in:
Edwin Wu
2026-07-12 21:46:20 -07:00
committed by Copybara-Service
parent e9186fb176
commit 2f3e7beb78
3 changed files with 46 additions and 3 deletions
@@ -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) {
@@ -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));
@@ -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];