Skip to content

Commit

Permalink
fix: Support uint64 in crash reports (#2631)
Browse files Browse the repository at this point in the history
Add support for uint64 when decoding crash reports.
  • Loading branch information
philipphofmann authored Jan 20, 2023
1 parent 302ee8b commit 15b8c61
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 35 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 13 additions & 10 deletions Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
Expand Down Expand Up @@ -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)
Expand All @@ -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)) {
Expand Down
120 changes: 95 additions & 25 deletions Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -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<NSNumber *> *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<NSNumber *> *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<NSNumber *> *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
Expand Down Expand Up @@ -1275,6 +1276,64 @@ - (void)testDeserializeArrayNumberOverflow
XCTAssertNil(error, @"");
}

- (void)testDeserializeArray_Int64Min
{
int64_t value = LLONG_MIN;
NSString *jsonString = [NSString stringWithFormat:@"[%lld]", value];

NSArray<NSNumber *> *result = [self decode:jsonString];

XCTAssertEqual([result[0] longLongValue], value);
}

- (void)testDeserializeArray_64IntMax
{
int64_t value = LLONG_MAX;
NSString *jsonString = [NSString stringWithFormat:@"[%lld]", value];

NSArray<NSNumber *> *result = [self decode:jsonString];

XCTAssertEqual([result[0] longLongValue], value);
}

- (void)testDeserializeArrayUIntMax_UsesDouble
{
uint64_t value = ULLONG_MAX;
NSString *jsonString = [NSString stringWithFormat:@"[%llu]", value];

NSArray<NSNumber *> *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<NSNumber *> *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<NSNumber *> *result = [SentryCrashJSONCodec decode:toData(jsonString)
options:0
error:&error];
XCTAssertNotNil(result);
XCTAssertNil(error);

XCTAssertEqual([result[0] doubleValue], value);
}

- (void)testDeserializeDictionaryInvalidKey
{
NSError *error = (NSError *)self;
Expand Down Expand Up @@ -1592,4 +1651,15 @@ - (void)testDontCloseLastContainer
[self expectEquivalentJSON:encodedData.bytes toJSON:expectedJson];
}

- (NSArray<NSNumber *> *)decode:(NSString *)jsonString
{
NSError *error = nil;
NSArray<NSNumber *> *result = [SentryCrashJSONCodec decode:toData(jsonString)
options:0
error:&error];
XCTAssertNotNil(result);
XCTAssertNil(error);
return result;
}

@end

0 comments on commit 15b8c61

Please sign in to comment.