mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
Merge remote-tracking branch 'nearby/main'
# Conflicts: # connections/implementation/bwu_manager_test.cc # sharing/BUILD # sharing/certificates/fake_nearby_share_certificate_manager.cc # sharing/certificates/fake_nearby_share_certificate_manager.h # sharing/internal/base/utf_string_conversions.h
This commit is contained in:
@@ -386,6 +386,10 @@ static const int kMaxAdvertisementLengthOnIOS = 23;
|
||||
[_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound];
|
||||
return;
|
||||
}
|
||||
if (request.offset > value.length) {
|
||||
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
|
||||
return;
|
||||
}
|
||||
request.value =
|
||||
[value subdataWithRange:NSMakeRange(request.offset, value.length - request.offset)];
|
||||
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
|
||||
|
||||
@@ -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,7 +140,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return;
|
||||
}
|
||||
|
||||
if (![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
|
||||
// IntroductionFrame.service_id_hash. We MUST bounds-check before
|
||||
// -subdataWithRange:, otherwise a short follow-up packet throws
|
||||
// NSRangeException on CoreBluetooth's dispatch queue -> objc_terminate.
|
||||
if (data.length < prefixLength ||
|
||||
![[data subdataWithRange:NSMakeRange(0, prefixLength)] isEqual:_serviceIDHash]) {
|
||||
GNCLoggerInfo(@"[NEARBY] Input stream: Received wrong data packet and discarded");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,11 @@ NSData *_Nullable GNCMParseBLEFramesIntroductionPacket(NSData *_Nullable data) {
|
||||
::location::nearby::mediums::SocketVersion::V2 &&
|
||||
socket_control_frame.introduction().has_service_id_hash()) {
|
||||
std::string service_id_hash = socket_control_frame.introduction().service_id_hash();
|
||||
// service_id_hash is attacker-supplied; clamp to the protocol-defined
|
||||
// 3-byte length so it cannot be used to inflate prefixLength downstream.
|
||||
if (service_id_hash.size() != GNCMBleAdvertisementLengthServiceIDHash) {
|
||||
return nil;
|
||||
}
|
||||
return [NSData dataWithBytes:service_id_hash.data() length:service_id_hash.length()];
|
||||
}
|
||||
}
|
||||
|
||||
+28
-20
@@ -111,15 +111,17 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f;
|
||||
|
||||
- (void)addPeripheralServiceManager:(GNSPeripheralServiceManager *)peripheralServiceManager
|
||||
bleServiceAddedCompletion:(GNSErrorHandler)completion {
|
||||
[_peripheralServiceManagers setObject:peripheralServiceManager
|
||||
forKey:peripheralServiceManager.serviceUUID];
|
||||
[peripheralServiceManager addedToPeripheralManager:self bleServiceAddedCompletion:completion];
|
||||
if (_started) {
|
||||
[self addBleServiceForServiceManager:peripheralServiceManager];
|
||||
}
|
||||
// Update all advertised services to make sure that the right services are advertised in case
|
||||
// all BLE services were already added.
|
||||
[self updateAdvertisedServices];
|
||||
dispatch_async(_queue, ^{
|
||||
[self->_peripheralServiceManagers setObject:peripheralServiceManager
|
||||
forKey:peripheralServiceManager.serviceUUID];
|
||||
[peripheralServiceManager addedToPeripheralManager:self bleServiceAddedCompletion:completion];
|
||||
if (self->_started) {
|
||||
[self addBleServiceForServiceManager:peripheralServiceManager];
|
||||
}
|
||||
// Update all advertised services to make sure that the right services are advertised in case
|
||||
// all BLE services were already added.
|
||||
[self updateAdvertisedServices];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
@@ -211,18 +213,24 @@ static NSTimeInterval gKBTCrashLoopMaxTimeBetweenResetting = 15.f;
|
||||
|
||||
- (void)removePeripheralServiceManagerForServiceUUID:(CBUUID *)serviceUUID
|
||||
bleServiceRemovedCompletion:(GNSErrorHandler)completion {
|
||||
GNSPeripheralServiceManager *peripheralServiceManager =
|
||||
[_peripheralServiceManagers objectForKey:serviceUUID];
|
||||
if (peripheralServiceManager == nil) {
|
||||
completion(nil);
|
||||
return;
|
||||
}
|
||||
[_cbPeripheralManager removeService:peripheralServiceManager.cbService];
|
||||
[_peripheralServiceManagers removeObjectForKey:serviceUUID];
|
||||
[peripheralServiceManager didRemoveCBService];
|
||||
dispatch_async(_queue, ^{
|
||||
GNSPeripheralServiceManager *peripheralServiceManager =
|
||||
[self->_peripheralServiceManagers objectForKey:serviceUUID];
|
||||
if (peripheralServiceManager == nil) {
|
||||
if (completion) {
|
||||
completion(nil);
|
||||
}
|
||||
return;
|
||||
}
|
||||
[self->_cbPeripheralManager removeService:peripheralServiceManager.cbService];
|
||||
[self->_peripheralServiceManagers removeObjectForKey:serviceUUID];
|
||||
[peripheralServiceManager didRemoveCBService];
|
||||
|
||||
[self updateAdvertisedServices];
|
||||
completion(nil);
|
||||
[self updateAdvertisedServices];
|
||||
if (completion) {
|
||||
completion(nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)removeAllBleServices {
|
||||
|
||||
+474
-372
File diff suppressed because it is too large
Load Diff
@@ -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];
|
||||
|
||||
@@ -47,6 +47,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/** Expectation fulfilled when peripheral responds to a request with an error. */
|
||||
@property(nonatomic, readonly) XCTestExpectation *respondToRequestErrorExpectation;
|
||||
|
||||
/** The last response result. */
|
||||
@property(nonatomic, assign) CBATTError lastResponseResult;
|
||||
|
||||
/** Expectation fulfilled when peripheral unpublishes an L2CAP channel. */
|
||||
@property(nonatomic, readonly) XCTestExpectation *unpublishExpectation;
|
||||
|
||||
@@ -117,6 +120,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
|
||||
characteristic:(CBUUID *)characteristic;
|
||||
|
||||
/**
|
||||
* Simulates a read request event with an offset.
|
||||
*
|
||||
* Creates a fake read request with the given offset for the given service and characteristic UUIDs
|
||||
* and calls the @c gnc_peripheralManager:didReceiveReadRequest: delegate method.
|
||||
*
|
||||
* @param service The service UUID of the characteristic to read from.
|
||||
* @param characteristic The characteristic UUID to read from.
|
||||
* @param offset The offset to read from.
|
||||
*/
|
||||
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
|
||||
characteristic:(CBUUID *)characteristic
|
||||
offset:(NSUInteger)offset;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
// Keep a strong reference to the service.
|
||||
@property(readwrite, nonatomic) CBService *service;
|
||||
|
||||
// Change property to readwrite for tests.
|
||||
@property(readwrite, nonatomic) NSUInteger offset;
|
||||
|
||||
- (instancetype)initWithService:(CBUUID *)service characteristic:(CBUUID *)characteristic;
|
||||
|
||||
@end
|
||||
@@ -115,6 +118,7 @@ static const uint16_t kPSM = 192;
|
||||
}
|
||||
|
||||
- (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result {
|
||||
self.lastResponseResult = result;
|
||||
if (result == CBATTErrorSuccess) {
|
||||
[_respondToRequestSuccessExpectation fulfill];
|
||||
return;
|
||||
@@ -176,6 +180,15 @@ static const uint16_t kPSM = 192;
|
||||
[_peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request];
|
||||
}
|
||||
|
||||
- (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service
|
||||
characteristic:(CBUUID *)characteristic
|
||||
offset:(NSUInteger)offset {
|
||||
CBATTRequest *request = [[CBATTRequest alloc] initWithService:service
|
||||
characteristic:characteristic];
|
||||
request.offset = offset;
|
||||
[_peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request];
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id<CBPeripheralManagerDelegate>)delegate {
|
||||
self.peripheralDelegate = (id<GNCPeripheralManagerDelegate>)delegate;
|
||||
}
|
||||
|
||||
@@ -241,4 +241,33 @@ static const NSTimeInterval kTimeout = 1.0;
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
- (void)testReceiveShortDataPacketAfterIntro {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:nil
|
||||
expectedIntroPacket:YES
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
NSData *introPacket = GNCMGenerateBLEFramesIntroductionPacket(GNCMServiceIDHash(kServiceID));
|
||||
|
||||
// Receive the intro packet first to set `_serviceIDHash`.
|
||||
[_fakeSocket simulateSocketDidReceiveData:introPacket];
|
||||
|
||||
// Receive a data packet that is shorter than the service ID hash length.
|
||||
// This should not crash; it should just be discarded.
|
||||
NSData *shortPacket = [@"ab" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Payload handler not called"];
|
||||
expectation.inverted = YES;
|
||||
|
||||
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
|
||||
handlers.payloadHandler = ^(NSData *data) {
|
||||
[expectation fulfill];
|
||||
};
|
||||
_connection.connectionHandlers = handlers;
|
||||
|
||||
[_fakeSocket simulateSocketDidReceiveData:shortPacket];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -45,6 +45,18 @@ static const NSTimeInterval kWaitForConnectionTimeout = 6.0; // Allow for the 5
|
||||
XCTAssertEqualObjects(parsedHash, serviceIDHash);
|
||||
}
|
||||
|
||||
- (void)testParseBLEFramesIntroductionPacketFailure_InvalidHashLength {
|
||||
// Too long hash (4 bytes, protocol expects 3 bytes)
|
||||
NSData *longHash = [@"1234" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *longPacket = GNCMGenerateBLEFramesIntroductionPacket(longHash);
|
||||
XCTAssertNil(GNCMParseBLEFramesIntroductionPacket(longPacket));
|
||||
|
||||
// Too short hash (2 bytes, protocol expects 3 bytes)
|
||||
NSData *shortHash = [@"12" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *shortPacket = GNCMGenerateBLEFramesIntroductionPacket(shortHash);
|
||||
XCTAssertNil(GNCMParseBLEFramesIntroductionPacket(shortPacket));
|
||||
}
|
||||
|
||||
- (void)testParseBLEFramesIntroductionPacketFailure_NilData {
|
||||
NSData *parsedHash = GNCMParseBLEFramesIntroductionPacket(nil);
|
||||
XCTAssertNil(parsedHash);
|
||||
|
||||
Reference in New Issue
Block a user