Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Fixes

- Fix JSON encoding of infinite numeric values in crash reports (#7802)

## 9.11.0

### Features
Expand Down
41 changes: 32 additions & 9 deletions Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,34 +317,57 @@ sentrycrashjson_addFloatingPointElement(
if (isnan(value)) {
return sentrycrashjson_addNullElement(context, name);
}
if (isinf(value)) {
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
return value > 0 ? addJSONData(context, "1e999", 5) : addJSONData(context, "-1e999", 6);
Comment thread
denrase marked this conversation as resolved.
}

// Conservative buffer for double formatting.
char buff[64];
int written = snprintf(buff, sizeof(buff), "%lg", value);
if (written < 0) {
return SentryCrashJSON_ERROR_INVALID_CHARACTER;
} else if (written >= (int)sizeof(buff)) {
return SentryCrashJSON_ERROR_DATA_TOO_LONG;
}
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
char buff[50];
snprintf(buff, sizeof(buff), "%lg", value);
return addJSONData(context, buff, (int)strlen(buff));
return addJSONData(context, buff, written);
}

int
sentrycrashjson_addIntegerElement(
SentryCrashJSONEncodeContext *const context, const char *const name, int64_t value)
{
// Exact max for int64_t in decimal: 19 digits plus sign and NUL.
char buff[21];
int written = snprintf(buff, sizeof(buff), "%" PRId64, value);
if (written < 0) {
return SentryCrashJSON_ERROR_INVALID_CHARACTER;
} else if (written >= (int)sizeof(buff)) {
return SentryCrashJSON_ERROR_DATA_TOO_LONG;
}
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
char buff[30];
snprintf(buff, sizeof(buff), "%" PRId64, value);
return addJSONData(context, buff, (int)strlen(buff));
return addJSONData(context, buff, written);
}

int
sentrycrashjson_addUIntegerElement(
SentryCrashJSONEncodeContext *const context, const char *const name, uint64_t value)
{
// Exact max for uint64_t in decimal: 20 digits plus NUL.
char buff[21];
int written = snprintf(buff, sizeof(buff), "%" PRIu64, value);
if (written < 0) {
return SentryCrashJSON_ERROR_INVALID_CHARACTER;
} else if (written >= (int)sizeof(buff)) {
return SentryCrashJSON_ERROR_DATA_TOO_LONG;
}
int result = sentrycrashjson_beginElement(context, name);
unlikely_if(result != SentryCrashJSON_OK) { return result; }
char buff[30];
snprintf(buff, sizeof(buff), "%" PRIu64, value);
return addJSONData(context, buff, (int)strlen(buff));
return addJSONData(context, buff, written);
}

int
Expand Down
151 changes: 143 additions & 8 deletions Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
//

#import <XCTest/XCTest.h>
#import <float.h>
#import <math.h>
#import <stdint.h>

#import "FileBasedTestCase.h"
#import "SentryCrashJSONCodec.h"
Expand Down Expand Up @@ -884,6 +886,84 @@ - (void)testSerializeDeserializeNANDouble
XCTAssertTrue([[result objectAtIndex:0] isKindOfClass:[NSNull class]]);
}

- (void)testSerializeDeserializePositiveInfinityFloat
{
NSError *error = (NSError *)self;
NSString *expected = @"[1e999]";
float infValue = INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithFloat:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
}

- (void)testSerializeDeserializeNegativeInfinityFloat
{
NSError *error = (NSError *)self;
NSString *expected = @"[-1e999]";
float infValue = -INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithFloat:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
XCTAssertTrue([[result objectAtIndex:0] doubleValue] < 0, @"Should be negative infinity");
}

- (void)testSerializeDeserializePositiveInfinityDouble
{
NSError *error = (NSError *)self;
NSString *expected = @"[1e999]";
double infValue = (double)INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithDouble:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
}

- (void)testSerializeDeserializeNegativeInfinityDouble
{
NSError *error = (NSError *)self;
NSString *expected = @"[-1e999]";
double infValue = -(double)INFINITY;
id original = [NSArray arrayWithObjects:[NSNumber numberWithDouble:infValue], nil];

NSString *jsonString = toString([SentryCrashJSONCodec encode:original
options:SentryCrashJSONEncodeOptionSorted
error:&error]);
XCTAssertNotNil(jsonString, @"");
XCTAssertNil(error, @"");
XCTAssertEqualObjects(jsonString, expected, @"");
id result = [SentryCrashJSONCodec decode:toData(jsonString) options:0 error:&error];
XCTAssertNotNil(result, @"");
XCTAssertNil(error, @"");
XCTAssertTrue(isinf([[result objectAtIndex:0] doubleValue]), @"Should decode back to infinity");
XCTAssertTrue([[result objectAtIndex:0] doubleValue] < 0, @"Should be negative infinity");
}

- (void)testSerializeDeserializeChar
{
NSError *error = (NSError *)self;
Expand Down Expand Up @@ -1562,10 +1642,9 @@ - (void)expectData:(NSData *)data encodesObject:(id)expectedObject
XCTAssertEqualObjects(object, expectedObject);
}

- (id)decodeJSON:(const char *)jsonBytes
- (id)decodeJSONData:(NSData *)jsonData
{
NSError *error = nil;
NSData *jsonData = [NSData dataWithBytes:jsonBytes length:strlen(jsonBytes)];
id object = [SentryCrashJSONCodec decode:jsonData
options:SentryCrashJSONDecodeOptionKeepPartialObject
error:&error];
Expand All @@ -1574,9 +1653,15 @@ - (id)decodeJSON:(const char *)jsonBytes
return object;
}

- (void)expectEquivalentJSON:(const char *)jsonCompareBytes toJSON:(const char *)jsonExpectedBytes
- (id)decodeJSON:(const char *)jsonBytes
{
NSData *jsonData = [NSData dataWithBytes:jsonBytes length:strlen(jsonBytes)];
return [self decodeJSONData:jsonData];
}

- (void)expectEquivalentJSONData:(NSData *)jsonCompareData toJSON:(const char *)jsonExpectedBytes
{
id objectCompare = [self decodeJSON:jsonCompareBytes];
id objectCompare = [self decodeJSONData:jsonCompareData];
id objectExpect = [self decodeJSON:jsonExpectedBytes];
XCTAssertEqualObjects(objectCompare, objectExpect);
}
Expand Down Expand Up @@ -1684,9 +1769,8 @@ - (void)testDontCloseLastContainer
sentrycrashjson_addStringElement(&context, "testing", "this", SentryCrashJSON_SIZE_AUTOMATIC);
sentrycrashjson_endContainer(&context);
sentrycrashjson_endEncode(&context);
[encodedData appendBytes:"\0" length:1];

[self expectEquivalentJSON:encodedData.bytes toJSON:expectedJson];
[self expectEquivalentJSONData:encodedData toJSON:expectedJson];
}

- (NSArray<NSNumber *> *)decode:(NSString *)jsonString
Expand All @@ -1711,9 +1795,60 @@ - (void)testFastUIntEncode
sentrycrashjson_addUIntegerElement(&context, "uint", 1234567890);
sentrycrashjson_endContainer(&context);
sentrycrashjson_endEncode(&context);
[encodedData appendBytes:"\0" length:1];

[self expectEquivalentJSON:encodedData.bytes toJSON:expectedJson];
[self expectEquivalentJSONData:encodedData toJSON:expectedJson];
}

- (void)testFastDoubleEncode_DoubleLimits
{
const char *expectedJson = "{\"min\":-1.79769e+308,\"max\":1.79769e+308}";

NSMutableData *encodedData = [NSMutableData data];
SentryCrashJSONEncodeContext context = { 0 };
sentrycrashjson_beginEncode(&context, false, addJSONData, (__bridge void *)(encodedData));
sentrycrashjson_beginObject(&context, NULL);
XCTAssertEqual(
sentrycrashjson_addFloatingPointElement(&context, "min", -DBL_MAX), SentryCrashJSON_OK);
XCTAssertEqual(
sentrycrashjson_addFloatingPointElement(&context, "max", DBL_MAX), SentryCrashJSON_OK);
sentrycrashjson_endContainer(&context);
sentrycrashjson_endEncode(&context);

[self expectEquivalentJSONData:encodedData toJSON:expectedJson];
}

- (void)testFastIntEncode_Int64Limits
{
const char *expectedJson = "{\"min\":-9223372036854775808,\"max\":9223372036854775807}";

NSMutableData *encodedData = [NSMutableData data];
SentryCrashJSONEncodeContext context = { 0 };
sentrycrashjson_beginEncode(&context, false, addJSONData, (__bridge void *)(encodedData));
sentrycrashjson_beginObject(&context, NULL);
XCTAssertEqual(
sentrycrashjson_addIntegerElement(&context, "min", INT64_MIN), SentryCrashJSON_OK);
XCTAssertEqual(
sentrycrashjson_addIntegerElement(&context, "max", INT64_MAX), SentryCrashJSON_OK);
sentrycrashjson_endContainer(&context);
sentrycrashjson_endEncode(&context);

[self expectEquivalentJSONData:encodedData toJSON:expectedJson];
}

- (void)testFastUIntEncode_UInt64Max
{
const char *expectedJson = "{\"uint\":18446744073709551615}";

NSMutableData *encodedData = [NSMutableData data];
SentryCrashJSONEncodeContext context = { 0 };
sentrycrashjson_beginEncode(&context, false, addJSONData, (__bridge void *)(encodedData));
sentrycrashjson_beginObject(&context, NULL);
XCTAssertEqual(
sentrycrashjson_addUIntegerElement(&context, "uint", UINT64_MAX), SentryCrashJSON_OK);
sentrycrashjson_endContainer(&context);
sentrycrashjson_endEncode(&context);

[self expectEquivalentJSONData:encodedData toJSON:expectedJson];
}

@end
Loading