From 973769d74433de3c56c4ffdf4f343cb35d98e4f7 Mon Sep 17 00:00:00 2001
From: Aki Tuomi <aki.tuomi@open-xchange.com>
Date: Tue, 2 Apr 2019 13:09:48 +0300
Subject: [PATCH] lib: json - Escape invalid UTF-8 as unicode bytes

This prevents dovecot from crashing if invalid UTF-8 input
is given.
---
 src/lib/json-parser.c      | 12 ++++++++----
 src/lib/test-json-parser.c |  8 ++++----
 2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/src/lib/json-parser.c b/src/lib/json-parser.c
index 677091d649..e7846a329f 100644
--- a/src/lib/json-parser.c
+++ b/src/lib/json-parser.c
@@ -803,9 +803,13 @@ void json_append_escaped_data(string_t *dest, const unsigned char *src, size_t s
 
 	for (i = 0; i < size;) {
 		bytes = uni_utf8_get_char_n(src+i, size-i, &chr);
-		/* refuse to add invalid data */
-		i_assert(bytes > 0 && uni_is_valid_ucs4(chr));
-		json_append_escaped_ucs4(dest, chr);
-		i += bytes;
+		if (bytes > 0 && uni_is_valid_ucs4(chr)) {
+			json_append_escaped_ucs4(dest, chr);
+			i += bytes;
+		} else {
+			str_append_data(dest, UNICODE_REPLACEMENT_CHAR_UTF8,
+					      UTF8_REPLACEMENT_CHAR_LEN);
+			i++;
+		}
 	}
 }
diff --git a/src/lib/test-json-parser.c b/src/lib/test-json-parser.c
index bae6fb202b..9ce1e489ba 100644
--- a/src/lib/test-json-parser.c
+++ b/src/lib/test-json-parser.c
@@ -267,20 +267,20 @@ static void test_json_append_escaped(void)
 	string_t *str = t_str_new(32);
 
 	test_begin("json_append_escaped()");
-	json_append_escaped(str, "\b\f\r\n\t\"\\\001\002-\xC3\xA4\xf0\x90\x90\xb7");
-	test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0001\\u0002-\\u00e4\\ud801\\udc37") == 0);
+	json_append_escaped(str, "\b\f\r\n\t\"\\\001\002-\xC3\xA4\xf0\x90\x90\xb7\xff");
+	test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0001\\u0002-\\u00e4\\ud801\\udc37" UNICODE_REPLACEMENT_CHAR_UTF8) == 0);
 	test_end();
 }
 
 static void test_json_append_escaped_data(void)
 {
 	static const unsigned char test_input[] =
-		"\b\f\r\n\t\"\\\000\001\002-\xC3\xA4\xf0\x90\x90\xb7";
+		"\b\f\r\n\t\"\\\000\001\002-\xC3\xA4\xf0\x90\x90\xb7\xff";
 	string_t *str = t_str_new(32);
 
 	test_begin("json_append_escaped()");
 	json_append_escaped_data(str, test_input, sizeof(test_input)-1);
-	test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0000\\u0001\\u0002-\\u00e4\\ud801\\udc37") == 0);
+	test_assert(strcmp(str_c(str), "\\b\\f\\r\\n\\t\\\"\\\\\\u0000\\u0001\\u0002-\\u00e4\\ud801\\udc37" UNICODE_REPLACEMENT_CHAR_UTF8) == 0);
 	test_end();
 }
