diff --git a/CHANGELOG.md b/CHANGELOG.md index 76abc6fc9..78ef5c35e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - Structured logs: respect printf argument widths when extracting log parameters to avoid stack-data disclosure and corrupted attributes on 32-bit platforms. ([#1752](https://github.com/getsentry/sentry-native/pull/1752)) - Fix a potential out-of-bounds read when parsing non-NUL-terminated `sentry-trace` headers. ([#1749](https://github.com/getsentry/sentry-native/pull/1749)) - Fix division by zero when breadcrumbs are disabled. ([#1767](https://github.com/getsentry/sentry-native/pull/1767)) +- Handle memory allocation failures during JSON serialization to prevent truncated output. ([#1772](https://github.com/getsentry/sentry-native/pull/1772)) ## 0.14.2 diff --git a/src/sentry_json.c b/src/sentry_json.c index 2f691be4c..8d2d94748 100644 --- a/src/sentry_json.c +++ b/src/sentry_json.c @@ -25,9 +25,9 @@ typedef struct { void (*free)(sentry_jsonwriter_t *writer); - void (*write_str)(sentry_jsonwriter_t *writer, const char *str); - void (*write_buf)(sentry_jsonwriter_t *writer, const char *buf, size_t len); - void (*write_char)(sentry_jsonwriter_t *writer, char c); + bool (*write_str)(sentry_jsonwriter_t *writer, const char *str); + bool (*write_buf)(sentry_jsonwriter_t *writer, const char *buf, size_t len); + bool (*write_char)(sentry_jsonwriter_t *writer, char c); char *(*into_string)(sentry_jsonwriter_t *jw, size_t *len_out); } sentry_jsonwriter_ops_t; @@ -40,6 +40,7 @@ struct sentry_jsonwriter_s { uint32_t depth; bool last_was_key; bool owns_sb; + bool failed; sentry_jsonwriter_ops_t *ops; }; @@ -65,40 +66,42 @@ jsonwriter_free_file(sentry_jsonwriter_t *jw) sentry_free(jw); } -static void +static bool write_char_sb(sentry_jsonwriter_t *jw, char c) { - sentry__stringbuilder_append_char(jw->output.sb, c); + return sentry__stringbuilder_append_char(jw->output.sb, c) == 0; } -static void +static bool write_str_sb(sentry_jsonwriter_t *jw, const char *str) { - sentry__stringbuilder_append(jw->output.sb, str); + return sentry__stringbuilder_append(jw->output.sb, str) == 0; } -static void +static bool write_buf_sb(sentry_jsonwriter_t *jw, const char *buf, size_t len) { - sentry__stringbuilder_append_buf(jw->output.sb, buf, len); + return sentry__stringbuilder_append_buf(jw->output.sb, buf, len) == 0; } -static void +static bool write_char_file(sentry_jsonwriter_t *jw, char c) { - sentry__filewriter_write(jw->output.fw, &c, sizeof(char)); + return sentry__filewriter_write(jw->output.fw, &c, sizeof(char)) == 0; } -static void +static bool write_str_file(sentry_jsonwriter_t *jw, const char *str) { - sentry__filewriter_write(jw->output.fw, str, sizeof(char) * strlen(str)); + return sentry__filewriter_write( + jw->output.fw, str, sizeof(char) * strlen(str)) + == 0; } -static void +static bool write_buf_file(sentry_jsonwriter_t *jw, const char *buf, size_t len) { - sentry__filewriter_write(jw->output.fw, buf, len); + return sentry__filewriter_write(jw->output.fw, buf, len) == 0; } static char * @@ -107,9 +110,11 @@ into_string_sb(sentry_jsonwriter_t *jw, size_t *len_out) char *rv = NULL; sentry_stringbuilder_t *sb = jw->output.sb; if (len_out) { - *len_out = sb->len; + *len_out = jw->failed ? 0 : sb->len; + } + if (!jw->failed) { + rv = sentry__stringbuilder_into_string(sb); } - rv = sentry__stringbuilder_into_string(sb); sentry__jsonwriter_free(jw); return rv; } @@ -156,6 +161,7 @@ sentry__jsonwriter_new_sb(sentry_stringbuilder_t *sb) rv->depth = 0; rv->last_was_key = 0; rv->owns_sb = owns_sb; + rv->failed = false; rv->ops = &sb_ops; return rv; } @@ -182,6 +188,7 @@ sentry__jsonwriter_new_fw(sentry_filewriter_t *fw) rv->depth = 0; rv->last_was_key = 0; rv->owns_sb = owns_sb; + rv->failed = false; rv->ops = &file_ops; return rv; } @@ -198,6 +205,7 @@ sentry__jsonwriter_reset(sentry_jsonwriter_t *jw) jw->want_comma = 0; jw->depth = 0; jw->last_was_key = 0; + jw->failed = false; } char * @@ -228,13 +236,25 @@ set_comma(sentry_jsonwriter_t *jw, bool val) static void write_char(sentry_jsonwriter_t *jw, char c) { - jw->ops->write_char(jw, c); + if (!jw->ops->write_char(jw, c)) { + jw->failed = true; + } } static void write_str(sentry_jsonwriter_t *jw, const char *str) { - jw->ops->write_str(jw, str); + if (!jw->ops->write_str(jw, str)) { + jw->failed = true; + } +} + +static void +write_buf(sentry_jsonwriter_t *jw, const char *buf, size_t len) +{ + if (!jw->ops->write_buf(jw, buf, len)) { + jw->failed = true; + } } // The Lookup table and algorithm below are adapted from: @@ -270,14 +290,18 @@ write_json_str(sentry_jsonwriter_t *jw, const char *str) write_char(jw, '"'); const unsigned char *start = ptr; - for (; *ptr; ptr++) { + for (; *ptr && !jw->failed; ptr++) { if (!needs_escaping[*ptr]) { continue; } size_t len = (size_t)(ptr - start); if (len) { - jw->ops->write_buf(jw, (const char *)start, len); + write_buf(jw, (const char *)start, len); + } + + if (jw->failed) { + return; } switch (*ptr) { @@ -315,12 +339,16 @@ write_json_str(sentry_jsonwriter_t *jw, const char *str) } } + if (jw->failed) { + return; + } + start = ptr + 1; } size_t len = (size_t)(ptr - start); if (len) { - jw->ops->write_buf(jw, (const char *)start, len); + write_buf(jw, (const char *)start, len); } write_char(jw, '"'); @@ -329,7 +357,7 @@ write_json_str(sentry_jsonwriter_t *jw, const char *str) static bool can_write_item(sentry_jsonwriter_t *jw) { - if (at_max_depth(jw)) { + if (at_max_depth(jw) || jw->failed) { return false; } if (jw->last_was_key) { @@ -341,7 +369,7 @@ can_write_item(sentry_jsonwriter_t *jw) } else { set_comma(jw, true); } - return true; + return !jw->failed; } void