From 15b8c6111854bacf68624e765d99adcf93bfc987 Mon Sep 17 00:00:00 2001 From: Philipp Hofmann Date: Fri, 20 Jan 2023 09:20:28 +0100 Subject: [PATCH] fix: Support uint64 in crash reports (#2631) Add support for uint64 when decoding crash reports. --- CHANGELOG.md | 4 + .../Recording/Tools/SentryCrashJSONCodec.c | 23 ++-- .../SentryCrash/SentryCrashJSONCodec_Tests.m | 120 ++++++++++++++---- 3 files changed, 112 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75be02c313a..95257d7178c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - AttachScreenshots is GA (#2623) - Gather profiling timeseries metrics for CPU usage and memory footprint (#2493) +### Fixes + +- Support uint64 in crash reports (#2631) + ## 8.0.0 ### Features diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c index d2f231cefd5..741c5df5ea7 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1190,17 +1191,17 @@ decodeElement(const char *const name, SentryCrashJSONDecodeContext *context) case '8': case '9': { // Try integer conversion. - int64_t accum = 0; + uint64_t accum = 0; + bool isOverflow = false; const char *const start = context->bufferPtr; for (; context->bufferPtr < context->bufferEnd && isdigit(*context->bufferPtr); context->bufferPtr++) { - accum = accum * 10 + (*context->bufferPtr - '0'); - unlikely_if(accum < 0) - { - // Overflow - break; - } + unlikely_if((isOverflow = accum > (ULLONG_MAX / 10))) { break; } + accum *= 10; + int nextDigit = (*context->bufferPtr - '0'); + unlikely_if((isOverflow = accum > (ULLONG_MAX - nextDigit))) { break; } + accum += nextDigit; } unlikely_if(context->bufferPtr >= context->bufferEnd) @@ -1209,9 +1210,11 @@ decodeElement(const char *const name, SentryCrashJSONDecodeContext *context) return SentryCrashJSON_ERROR_INCOMPLETE; } - if (!isFPChar(*context->bufferPtr) && accum >= 0) { - accum *= sign; - return context->callbacks->onIntegerElement(name, accum, context->userData); + if (!isFPChar(*context->bufferPtr) && !isOverflow) { + if ((sign == -1 && accum <= ((uint64_t)LLONG_MIN)) || accum <= ((uint64_t)LLONG_MAX)) { + int64_t signedAccum = accum * sign; + return context->callbacks->onIntegerElement(name, signedAccum, context->userData); + } } while (context->bufferPtr < context->bufferEnd && isFPChar(*context->bufferPtr)) { diff --git a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m index 838f4001d9e..ffa148a0afe 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m @@ -126,55 +126,56 @@ - (void)testSerializeDeserializeArrayInteger { NSError *error = (NSError *)self; NSString *expected = @"[1]"; - id original = [NSArray arrayWithObjects:[NSNumber numberWithInt:1], nil]; + int value = 1; + NSArray *original = [NSArray arrayWithObjects:[NSNumber numberWithInt:value], nil]; NSString *jsonString = toString([SentryCrashJSONCodec encode:original options:SentryCrashJSONEncodeOptionSorted error:&error]); - XCTAssertNotNil(jsonString, @""); - XCTAssertNil(error, @""); - XCTAssertEqualObjects(jsonString, expected, @""); + XCTAssertNotNil(jsonString); + XCTAssertNil(error); + XCTAssertEqualObjects(jsonString, expected); id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error]; - XCTAssertNotNil(result, @""); - XCTAssertNil(error, @""); - XCTAssertEqualObjects(result, original, @""); + XCTAssertNotNil(result); + XCTAssertNil(error); + XCTAssertEqualObjects(result, original); } - (void)testSerializeDeserializeArrayFloat { NSError *error = (NSError *)self; + float value = -0.2f; NSString *expected = @"[-0.2]"; - id original = [NSArray arrayWithObjects:[NSNumber numberWithFloat:-0.2f], nil]; + NSArray *original = + [NSArray arrayWithObjects:[NSNumber numberWithFloat:value], nil]; NSString *jsonString = toString([SentryCrashJSONCodec encode:original options:SentryCrashJSONEncodeOptionSorted error:&error]); - XCTAssertNotNil(jsonString, @""); - XCTAssertNil(error, @""); - XCTAssertEqualObjects(jsonString, expected, @""); + XCTAssertNotNil(jsonString); + XCTAssertNil(error); + XCTAssertEqualObjects(jsonString, expected); id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error]; - XCTAssertNotNil(result, @""); - XCTAssertNil(error, @""); - XCTAssertEqual([[result objectAtIndex:0] floatValue], -0.2f, @""); - // This always fails on NSNumber filled with float. - // XCTAssertEqualObjects(result, original, @""); + XCTAssertNotNil(result); + XCTAssertNil(error); + XCTAssertEqual([[result objectAtIndex:0] floatValue], value); } - (void)testSerializeDeserializeArrayFloat2 { NSError *error = (NSError *)self; NSString *expected = @"[-2e-15]"; - id original = [NSArray arrayWithObjects:[NSNumber numberWithFloat:-2e-15f], nil]; + float value = -2e-15f; + NSArray *original = + [NSArray arrayWithObjects:[NSNumber numberWithFloat:value], nil]; NSString *jsonString = toString([SentryCrashJSONCodec encode:original options:SentryCrashJSONEncodeOptionSorted error:&error]); - XCTAssertNotNil(jsonString, @""); - XCTAssertNil(error, @""); - XCTAssertEqualObjects(jsonString, expected, @""); + XCTAssertNotNil(jsonString); + XCTAssertNil(error); + XCTAssertEqualObjects(jsonString, expected); id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error]; - XCTAssertNotNil(result, @""); - XCTAssertNil(error, @""); - XCTAssertEqual([[result objectAtIndex:0] floatValue], -2e-15f, @""); - // This always fails on NSNumber filled with float. - // XCTAssertEqualObjects(result, original, @""); + XCTAssertNotNil(result); + XCTAssertNil(error); + XCTAssertEqual([[result objectAtIndex:0] floatValue], value); } - (void)testSerializeDeserializeArrayString @@ -1275,6 +1276,64 @@ - (void)testDeserializeArrayNumberOverflow XCTAssertNil(error, @""); } +- (void)testDeserializeArray_Int64Min +{ + int64_t value = LLONG_MIN; + NSString *jsonString = [NSString stringWithFormat:@"[%lld]", value]; + + NSArray *result = [self decode:jsonString]; + + XCTAssertEqual([result[0] longLongValue], value); +} + +- (void)testDeserializeArray_64IntMax +{ + int64_t value = LLONG_MAX; + NSString *jsonString = [NSString stringWithFormat:@"[%lld]", value]; + + NSArray *result = [self decode:jsonString]; + + XCTAssertEqual([result[0] longLongValue], value); +} + +- (void)testDeserializeArrayUIntMax_UsesDouble +{ + uint64_t value = ULLONG_MAX; + NSString *jsonString = [NSString stringWithFormat:@"[%llu]", value]; + + NSArray *result = [self decode:jsonString]; + + XCTAssertNotEqual([result[0] unsignedLongLongValue], value); + XCTAssertEqual([result[0] doubleValue], [@(value) doubleValue]); +} + +- (void)testDeserializeArray_NegativeLLONG_MIN_plusOne_UsesDouble +{ + uint64_t value = (uint64_t)LLONG_MIN + 1; + NSString *jsonString = [NSString stringWithFormat:@"[-%llu]", value]; + + NSArray *result = [self decode:jsonString]; + + XCTAssertNotEqual([result[0] unsignedLongLongValue], value); + XCTAssertEqual([result[0] doubleValue], -[@(value) doubleValue]); +} + +- (void)testDeserializeArray_UIntOverflow_UsesDouble +{ + NSError *error = (NSError *)self; + uint64_t ullongmax = ULLONG_MAX; + double value = (double)ULLONG_MAX + 1; + NSLog(@"%f, %llu", value, ullongmax); + NSString *jsonString = [NSString stringWithFormat:@"[%f]", value]; + NSArray *result = [SentryCrashJSONCodec decode:toData(jsonString) + options:0 + error:&error]; + XCTAssertNotNil(result); + XCTAssertNil(error); + + XCTAssertEqual([result[0] doubleValue], value); +} + - (void)testDeserializeDictionaryInvalidKey { NSError *error = (NSError *)self; @@ -1592,4 +1651,15 @@ - (void)testDontCloseLastContainer [self expectEquivalentJSON:encodedData.bytes toJSON:expectedJson]; } +- (NSArray *)decode:(NSString *)jsonString +{ + NSError *error = nil; + NSArray *result = [SentryCrashJSONCodec decode:toData(jsonString) + options:0 + error:&error]; + XCTAssertNotNil(result); + XCTAssertNil(error); + return result; +} + @end