From 31ded3476a3af0c2435395fbf302b636d6f105af Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Tue, 14 Apr 2026 17:17:04 +0200 Subject: [PATCH 1/8] fix: Harden float JSON encoding and validate battery level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports numeric encoding guards from upstream KSCrash (kstenerud/KSCrash#526 (https://github.com/kstenerud/KSCrash/pull/526)): - Encode ±infinity as 1e999/-1e999 instead of the invalid JSON literal inf - Validate snprintf return before emitting data (format before beginElement so errors can't corrupt the JSON stream) Also validates battery level in SentrySystemEventBreadcrumbs — rejects non-finite and out of-range values before they reach the crash-scope JSON encoder. Relates to #4580 --- .../Recording/Tools/SentryCrashJSONCodec.c | 38 ++++++--- .../SentrySystemEventBreadcrumbs.swift | 8 +- .../SentrySystemEventBreadcrumbsTest.swift | 53 +++++++++++++ .../SentryCrash/SentryCrashJSONCodec_Tests.m | 78 +++++++++++++++++++ 4 files changed, 165 insertions(+), 12 deletions(-) diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c index 6d9bff4cf95..df5c70b7b63 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c @@ -317,34 +317,54 @@ 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); + } + char buff[50]; + 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) { + char buff[30]; + 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) { + char buff[30]; + 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 diff --git a/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift b/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift index 320743ef75e..195ec4f9efd 100644 --- a/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift +++ b/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift @@ -132,12 +132,14 @@ final class SentrySystemEventBreadcrumbs: NSObject { let currentLevel = currentDevice.batteryLevel var batteryData: [String: Any] = [:] - // W3C spec says level must be null if it is unknown - if currentState != .unknown && currentLevel != -1.0 { + // W3C spec says level must be null if it is unknown. + // Also guard against non-finite or out-of-range values so they never + // reach the crash-scope JSON encoder as invalid floats. + if currentState != .unknown && currentLevel.isFinite && (0...1).contains(currentLevel) { let w3cLevel = currentLevel * 100 batteryData["level"] = NSNumber(value: w3cLevel) } else { - SentrySDKLog.debug("batteryLevel is unknown.") + SentrySDKLog.debug("batteryLevel is unknown or has unexpected value: \(currentLevel)") } batteryData["plugged"] = isPlugged diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift index 0c0aa09a533..4405c99ccfa 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift @@ -144,6 +144,59 @@ class SentrySystemEventBreadcrumbsTest: XCTestCase { assertBatteryBreadcrumb(charging: false, level: 100) } + func testBatteryInfinityLevelOmitsLevel() { + let currentDevice = MyUIDevice(batteryLevel: .infinity, batteryState: .charging) + + sut = fixture.getSut(currentDevice: currentDevice) + + postBatteryLevelNotification(uiDevice: currentDevice) + + XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) + if let data = fixture.delegate.addCrumbInvocations.first?.data { + XCTAssertNil(data["level"], "Infinity battery level should be omitted") + XCTAssertEqual("BATTERY_STATE_CHANGE", data["action"] as? String) + } + } + + func testBatteryNegativeInfinityLevelOmitsLevel() { + let currentDevice = MyUIDevice(batteryLevel: -.infinity, batteryState: .charging) + + sut = fixture.getSut(currentDevice: currentDevice) + + postBatteryLevelNotification(uiDevice: currentDevice) + + XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) + if let data = fixture.delegate.addCrumbInvocations.first?.data { + XCTAssertNil(data["level"], "Negative infinity battery level should be omitted") + } + } + + func testBatteryNaNLevelOmitsLevel() { + let currentDevice = MyUIDevice(batteryLevel: .nan, batteryState: .charging) + + sut = fixture.getSut(currentDevice: currentDevice) + + postBatteryLevelNotification(uiDevice: currentDevice) + + XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) + if let data = fixture.delegate.addCrumbInvocations.first?.data { + XCTAssertNil(data["level"], "NaN battery level should be omitted") + } + } + + func testBatteryOutOfRangeLevelOmitsLevel() { + let currentDevice = MyUIDevice(batteryLevel: 1.5, batteryState: .charging) + + sut = fixture.getSut(currentDevice: currentDevice) + + postBatteryLevelNotification(uiDevice: currentDevice) + + XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) + if let data = fixture.delegate.addCrumbInvocations.first?.data { + XCTAssertNil(data["level"], "Out-of-range battery level should be omitted") + } + } + func testBatteryUIDeviceNilNotification() { let currentDevice = MyUIDevice() diff --git a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m index a1fadfe1d49..3f58bd70207 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m @@ -884,6 +884,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; From 466fb48efd7f15ba8a0f332c8e6345910f7aee8e Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Fri, 17 Apr 2026 10:36:07 +0200 Subject: [PATCH 2/8] revert bc changes --- .../SentrySystemEventBreadcrumbs.swift | 6 +-- .../SentrySystemEventBreadcrumbsTest.swift | 53 ------------------- 2 files changed, 2 insertions(+), 57 deletions(-) diff --git a/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift b/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift index 195ec4f9efd..d4f610f2bbf 100644 --- a/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift +++ b/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift @@ -132,10 +132,8 @@ final class SentrySystemEventBreadcrumbs: NSObject { let currentLevel = currentDevice.batteryLevel var batteryData: [String: Any] = [:] - // W3C spec says level must be null if it is unknown. - // Also guard against non-finite or out-of-range values so they never - // reach the crash-scope JSON encoder as invalid floats. - if currentState != .unknown && currentLevel.isFinite && (0...1).contains(currentLevel) { + // W3C spec says level must be null if it is unknown + if currentState != .unknown && currentLevel != -1.0 { let w3cLevel = currentLevel * 100 batteryData["level"] = NSNumber(value: w3cLevel) } else { diff --git a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift index 4405c99ccfa..0c0aa09a533 100644 --- a/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift +++ b/Tests/SentryTests/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbsTest.swift @@ -144,59 +144,6 @@ class SentrySystemEventBreadcrumbsTest: XCTestCase { assertBatteryBreadcrumb(charging: false, level: 100) } - func testBatteryInfinityLevelOmitsLevel() { - let currentDevice = MyUIDevice(batteryLevel: .infinity, batteryState: .charging) - - sut = fixture.getSut(currentDevice: currentDevice) - - postBatteryLevelNotification(uiDevice: currentDevice) - - XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) - if let data = fixture.delegate.addCrumbInvocations.first?.data { - XCTAssertNil(data["level"], "Infinity battery level should be omitted") - XCTAssertEqual("BATTERY_STATE_CHANGE", data["action"] as? String) - } - } - - func testBatteryNegativeInfinityLevelOmitsLevel() { - let currentDevice = MyUIDevice(batteryLevel: -.infinity, batteryState: .charging) - - sut = fixture.getSut(currentDevice: currentDevice) - - postBatteryLevelNotification(uiDevice: currentDevice) - - XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) - if let data = fixture.delegate.addCrumbInvocations.first?.data { - XCTAssertNil(data["level"], "Negative infinity battery level should be omitted") - } - } - - func testBatteryNaNLevelOmitsLevel() { - let currentDevice = MyUIDevice(batteryLevel: .nan, batteryState: .charging) - - sut = fixture.getSut(currentDevice: currentDevice) - - postBatteryLevelNotification(uiDevice: currentDevice) - - XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) - if let data = fixture.delegate.addCrumbInvocations.first?.data { - XCTAssertNil(data["level"], "NaN battery level should be omitted") - } - } - - func testBatteryOutOfRangeLevelOmitsLevel() { - let currentDevice = MyUIDevice(batteryLevel: 1.5, batteryState: .charging) - - sut = fixture.getSut(currentDevice: currentDevice) - - postBatteryLevelNotification(uiDevice: currentDevice) - - XCTAssertEqual(1, fixture.delegate.addCrumbInvocations.count) - if let data = fixture.delegate.addCrumbInvocations.first?.data { - XCTAssertNil(data["level"], "Out-of-range battery level should be omitted") - } - } - func testBatteryUIDeviceNilNotification() { let currentDevice = MyUIDevice() From 49200bd5254f615e22bf14eaf05440c6dc730063 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Fri, 17 Apr 2026 10:37:47 +0200 Subject: [PATCH 3/8] revert log --- .../Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift b/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift index d4f610f2bbf..320743ef75e 100644 --- a/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift +++ b/Sources/Swift/Integrations/Breadcrumbs/SentrySystemEventBreadcrumbs.swift @@ -137,7 +137,7 @@ final class SentrySystemEventBreadcrumbs: NSObject { let w3cLevel = currentLevel * 100 batteryData["level"] = NSNumber(value: w3cLevel) } else { - SentrySDKLog.debug("batteryLevel is unknown or has unexpected value: \(currentLevel)") + SentrySDKLog.debug("batteryLevel is unknown.") } batteryData["plugged"] = isPlugged From 0e8adfda1e197ffcd749d174347bf21cefdcaec1 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Fri, 17 Apr 2026 10:41:32 +0200 Subject: [PATCH 4/8] add cl entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e891a66c516..5e971ba4529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Detect development builds via provisioning profile and debugger attachment (#7702) - Keep replayType as `buffer` for Session Replay triggered by an error (#7804) - Fix race condition in scope observer notifications causing EXC_BAD_ACCESS during cold launch (#7807) +- Harden SentryCrash JSON floating-point encoding (#7802) ## 9.10.0 From 585845da49a346aad1797b9351b2081193ffd9b4 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 27 Apr 2026 09:45:20 +0200 Subject: [PATCH 5/8] update cl entry --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ede09b139..83ec37ad1f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Fix JSON encoding of infinite numeric values in crash reports (#7802) + ## 9.11.0 ### Features @@ -12,7 +18,6 @@ - Detect development builds via provisioning profile and debugger attachment (#7702) - Keep replayType as `buffer` for Session Replay triggered by an error (#7804) - Fix race condition in scope observer notifications causing EXC_BAD_ACCESS during cold launch (#7807) -- Harden SentryCrash JSON floating-point encoding (#7802) - Unsubscribe to system event during background to avoid reporting breadcrumbs with wrong timestamps on return to foreground (#7702) - Fix SwiftUI's images and text redaction in iOS 26 (#7781) - Add foreground state to app context in app hang events (#7801) From 284deffd7769a3fbbbc6c6c1c7c1c254054d6c84 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 27 Apr 2026 11:49:19 +0200 Subject: [PATCH 6/8] =?UTF-8?q?don=E2=80=99t=20appen=20nul=20terminator=20?= =?UTF-8?q?in=20test=20for=20expectations,update=20buffers=20from=20upstre?= =?UTF-8?q?am=20and=20add=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Recording/Tools/SentryCrashJSONCodec.c | 9 ++- .../SentryCrash/SentryCrashJSONCodec_Tests.m | 73 +++++++++++++++++-- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c index df5c70b7b63..83dbda06e16 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashJSONCodec.c @@ -323,7 +323,8 @@ sentrycrashjson_addFloatingPointElement( return value > 0 ? addJSONData(context, "1e999", 5) : addJSONData(context, "-1e999", 6); } - char buff[50]; + // Conservative buffer for double formatting. + char buff[64]; int written = snprintf(buff, sizeof(buff), "%lg", value); if (written < 0) { return SentryCrashJSON_ERROR_INVALID_CHARACTER; @@ -339,7 +340,8 @@ int sentrycrashjson_addIntegerElement( SentryCrashJSONEncodeContext *const context, const char *const name, int64_t value) { - char buff[30]; + // 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; @@ -355,7 +357,8 @@ int sentrycrashjson_addUIntegerElement( SentryCrashJSONEncodeContext *const context, const char *const name, uint64_t value) { - char buff[30]; + // 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; diff --git a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m index 3f58bd70207..c6ac503b579 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashJSONCodec_Tests.m @@ -26,7 +26,9 @@ // #import +#import #import +#import #import "FileBasedTestCase.h" #import "SentryCrashJSONCodec.h" @@ -1640,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]; @@ -1652,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); } @@ -1762,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 *)decode:(NSString *)jsonString @@ -1789,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 From ef3e6359124e2eb6d10adf5587d62bd3aa2844c1 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 27 Apr 2026 15:13:32 +0200 Subject: [PATCH 7/8] update cl --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ec37ad1f6..e3ad51e5972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixes -- Fix JSON encoding of infinite numeric values in crash reports (#7802) +- Fix JSON encoding of infinite numeric values in crash reportss (#7802) ## 9.11.0 From 2d872284afffca0e77cc304faa24f4fa506018ce Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 27 Apr 2026 15:13:39 +0200 Subject: [PATCH 8/8] fix cl --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3ad51e5972..83ec37ad1f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixes -- Fix JSON encoding of infinite numeric values in crash reportss (#7802) +- Fix JSON encoding of infinite numeric values in crash reports (#7802) ## 9.11.0