diff --git a/Signal/src/util/StringUtil.m b/Signal/src/util/StringUtil.m index 5012c2c7a..5159869fc 100644 --- a/Signal/src/util/StringUtil.m +++ b/Signal/src/util/StringUtil.m @@ -134,7 +134,14 @@ -(NSNumber*) tryParseAsDecimalNumber { NSNumberFormatter* formatter = [NSNumberFormatter new]; [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; - return [formatter numberFromString:self]; + + // NSNumberFormatter.numberFromString is good at noticing bad inputs, but loses precision for large values + // NSDecimalNumber.decimalNumberWithString has perfect precision, but lets bad inputs through sometimes (e.g. "88ffhih" -> 88) + // We use both to get both accuracy and detection of bad inputs + if ([formatter numberFromString:self] == nil) { + return nil; + } + return [NSDecimalNumber decimalNumberWithString:self]; } -(NSNumber*) tryParseAsUnsignedInteger { NSNumber* value = [self tryParseAsDecimalNumber]; diff --git a/Signal/test/util/UtilTest.m b/Signal/test/util/UtilTest.m index 230ba0aca..d2d21cef8 100644 --- a/Signal/test/util/UtilTest.m +++ b/Signal/test/util/UtilTest.m @@ -383,18 +383,25 @@ test([@"88ffhih" tryParseAsUnsignedInteger] == nil); test([@"0xA" tryParseAsUnsignedInteger] == nil); test([@"A" tryParseAsUnsignedInteger] == nil); - test([@"4294967297" tryParseAsUnsignedInteger] == nil); - test([@"123456789123456789123456789123456789" tryParseAsUnsignedInteger] == nil); test([@"-1" tryParseAsUnsignedInteger] == nil); test([@"-" tryParseAsUnsignedInteger] == nil); test([[@"0" tryParseAsUnsignedInteger] isEqual:@0]); + test([[@"00" tryParseAsUnsignedInteger] isEqual:@0]); test([[@"1" tryParseAsUnsignedInteger] isEqual:@1]); + test([[@"01" tryParseAsUnsignedInteger] isEqual:@1]); test([[@"25" tryParseAsUnsignedInteger] isEqual:@25]); test([[(@NSUIntegerMax).description tryParseAsUnsignedInteger] isEqual:@NSUIntegerMax]); - if (NSUIntegerMax == 4294967295) { + if (NSUIntegerMax == 4294967295UL) { test([@"4294967296" tryParseAsUnsignedInteger] == nil); } + if (NSUIntegerMax == 18446744073709551615ULL) { + test([@"18446744073709551616" tryParseAsUnsignedInteger] == nil); + } + + NSString* max = (@NSUIntegerMax).description; + NSString* farTooLarge = [max stringByAppendingString:max]; + test([farTooLarge tryParseAsUnsignedInteger] == nil); } -(void) testRemoveAllCharactersIn { testThrows([@"" removeAllCharactersIn:nil]);