testLoadFailure.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #include "testBase.h"
  2. static void testLoadFailureNullInput(void)
  3. {
  4. // 仅验证 API 级别的空指针输入保护(不与 RFC8259 语法样本重复)。
  5. TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(NULL), "Parse(NULL) 应返回 NULL");
  6. const char *end = (const char *)0x1;
  7. TEST_ASSERT_NULL_MESSAGE(RyanJsonParseOptions(NULL, 0, RyanJsonFalse, &end), "ParseOptions(NULL) 应返回 NULL");
  8. TEST_ASSERT_EQUAL_PTR_MESSAGE((const char *)0x1, end, "ParseOptions(NULL) 不应改写 end 指针");
  9. }
  10. static void testLoadFailureWhitespaceOnlySlice(void)
  11. {
  12. const char *text = " \t\r\n ";
  13. TEST_ASSERT_NULL_MESSAGE(RyanJsonParseOptions(text, (uint32_t)strlen(text), RyanJsonFalse, NULL),
  14. "ParseOptions(仅空白, 非 strict) 应失败");
  15. TEST_ASSERT_NULL_MESSAGE(RyanJsonParseOptions(text, (uint32_t)strlen(text), RyanJsonTrue, NULL),
  16. "ParseOptions(仅空白, strict) 应失败");
  17. }
  18. static void testLoadFailureHugeNumberOverflow(void)
  19. {
  20. // 超长 Int:应在数值累乘过程中触发 isfinite 防御并失败
  21. const uint32_t intLen = 1024;
  22. char *hugeInt = (char *)malloc((size_t)intLen + 1U);
  23. TEST_ASSERT_NOT_NULL(hugeInt);
  24. hugeInt[0] = '1';
  25. memset(hugeInt + 1, '9', intLen - 1U);
  26. hugeInt[intLen] = '\0';
  27. TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(hugeInt), "Parse(超长 Int 溢出) 应返回 NULL");
  28. free(hugeInt);
  29. // 超长小数:同样应触发 isfinite 防御并失败
  30. const uint32_t fracLen = 1024;
  31. char *hugeFrac = (char *)malloc((size_t)fracLen + 3U);
  32. TEST_ASSERT_NOT_NULL(hugeFrac);
  33. hugeFrac[0] = '0';
  34. hugeFrac[1] = '.';
  35. memset(hugeFrac + 2, '9', fracLen);
  36. hugeFrac[fracLen + 2U] = '\0';
  37. TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(hugeFrac), "Parse(超长小数溢出) 应返回 NULL");
  38. free(hugeFrac);
  39. }
  40. static void testLoadFailureExponentAccumulatorOverflow(void)
  41. {
  42. TEST_ASSERT_NULL_MESSAGE(RyanJsonParse("1e2147483647"), "指数过大应触发 isfinite 防御");
  43. TEST_ASSERT_NULL_MESSAGE(RyanJsonParse("1e2147483648"), "指数累积溢出应解析失败");
  44. TEST_ASSERT_NULL_MESSAGE(RyanJsonParse("1e-2147483648"), "负指数累积溢出应解析失败");
  45. }
  46. static void testLoadParseOptionsFailure(void)
  47. {
  48. // 禁止尾部垃圾:requireNullTerminator = true
  49. const char *text = " {\"a\":1} trailing";
  50. RyanJson_t json = RyanJsonParseOptions(text, (uint32_t)strlen(text), RyanJsonTrue, NULL);
  51. TEST_ASSERT_NULL_MESSAGE(json, "ParseOptions(require null terminator) 应失败");
  52. }
  53. static void testLoadParseOptionsSequentialValidThenInvalidIsolation(void)
  54. {
  55. // 复杂链路:
  56. // ParseOptions(文档1成功, 非强制) -> ParseOptions(文档2失败) -> 校验文档1语义未被污染。
  57. const char *stream = "{\"ok\":1}{\"bad\":}";
  58. const uint32_t len = (uint32_t)strlen(stream);
  59. const char *end = NULL;
  60. RyanJson_t first = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end);
  61. TEST_ASSERT_NOT_NULL_MESSAGE(first, "顺序流文档1 解析应成功");
  62. TEST_ASSERT_NOT_NULL(end);
  63. TEST_ASSERT_EQUAL_CHAR('{', *end);
  64. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(first, "ok")));
  65. uint32_t remain = (uint32_t)(len - (uint32_t)(end - stream));
  66. RyanJson_t second = RyanJsonParseOptions(end, remain, RyanJsonFalse, NULL);
  67. TEST_ASSERT_NULL_MESSAGE(second, "顺序流文档2(非法)解析应失败");
  68. RyanJson_t baseline = RyanJsonParse("{\"ok\":1}");
  69. TEST_ASSERT_NOT_NULL(baseline);
  70. TEST_ASSERT_TRUE_MESSAGE(RyanJsonCompare(first, baseline), "文档2 失败后,文档1 语义不应改变");
  71. RyanJson_t strictWhole = RyanJsonParseOptions(stream, len, RyanJsonTrue, NULL);
  72. TEST_ASSERT_NULL_MESSAGE(strictWhole, "整串包含非法尾文档时 strict 解析应失败");
  73. RyanJsonDelete(baseline);
  74. RyanJsonDelete(first);
  75. }
  76. static void testLoadParseOptionsBinaryTailFailureIsolation(void)
  77. {
  78. // 复杂链路:
  79. // ParseOptions(二进制切片, 首文档成功) -> ParseOptions(尾片段失败) -> 校验首文档结果仍稳定。
  80. const uint8_t buffer[] = {'{', '"', 'a', '"', ':', '1', '}', '#', '!', '\0'};
  81. const uint32_t bufLen = 9U; // 不包含末尾 '\0'
  82. const char *end = NULL;
  83. RyanJson_t head = RyanJsonParseOptions((const char *)buffer, bufLen, RyanJsonFalse, &end);
  84. TEST_ASSERT_NOT_NULL_MESSAGE(head, "二进制切片首文档解析应成功");
  85. TEST_ASSERT_NOT_NULL(end);
  86. TEST_ASSERT_EQUAL_CHAR('#', *end);
  87. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "a")));
  88. uint32_t remain = (uint32_t)(bufLen - (uint32_t)(end - (const char *)buffer));
  89. RyanJson_t tail = RyanJsonParseOptions(end, remain, RyanJsonFalse, NULL);
  90. TEST_ASSERT_NULL_MESSAGE(tail, "二进制切片尾片段解析应失败");
  91. // 校验首文档保持稳定。
  92. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "a")));
  93. RyanJson_t strictWhole = RyanJsonParseOptions((const char *)buffer, bufLen, RyanJsonTrue, NULL);
  94. TEST_ASSERT_NULL_MESSAGE(strictWhole, "strict 模式应拒绝二进制切片中的尾部垃圾");
  95. RyanJsonDelete(head);
  96. }
  97. static void testLoadParseOptionsSequentialInvalidThenTailResync(void)
  98. {
  99. // 复杂链路:
  100. // ParseOptions(文档1成功) -> ParseOptions(文档2失败) -> 手动重同步到文档3并解析成功。
  101. const char *stream = "{\"head\":1}{\"bad\":}{\"tail\":3}";
  102. const uint32_t len = (uint32_t)strlen(stream);
  103. const char *end = NULL;
  104. RyanJson_t head = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end);
  105. TEST_ASSERT_NOT_NULL_MESSAGE(head, "文档1 解析应成功");
  106. TEST_ASSERT_NOT_NULL(end);
  107. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
  108. uint32_t remain = (uint32_t)(len - (uint32_t)(end - stream));
  109. RyanJson_t bad = RyanJsonParseOptions(end, remain, RyanJsonFalse, NULL);
  110. TEST_ASSERT_NULL_MESSAGE(bad, "文档2(非法) 解析应失败");
  111. const char *tailStart = strstr(end, "{\"tail\":3}");
  112. TEST_ASSERT_NOT_NULL_MESSAGE(tailStart, "应可在原始缓冲中定位到文档3 起点");
  113. RyanJson_t tail = RyanJsonParseOptions(tailStart, (uint32_t)strlen(tailStart), RyanJsonTrue, NULL);
  114. TEST_ASSERT_NOT_NULL_MESSAGE(tail, "文档3 解析应成功");
  115. TEST_ASSERT_EQUAL_INT(3, RyanJsonGetIntValue(RyanJsonGetObjectToKey(tail, "tail")));
  116. // 再次确认文档1 未受失败路径影响。
  117. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
  118. RyanJsonDelete(tail);
  119. RyanJsonDelete(head);
  120. }
  121. static void testLoadParseOptionsFailureKeepsEndPtrStable(void)
  122. {
  123. // 复杂链路:
  124. // ParseOptions(文档1成功) -> ParseOptions(文档2失败, 传入 parseEndPtr) -> 复用旧偏移重同步文档3。
  125. const char *stream = "{\"head\":1}{\"bad\":}{\"tail\":2}";
  126. const uint32_t len = (uint32_t)strlen(stream);
  127. const char *end1 = NULL;
  128. RyanJson_t head = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end1);
  129. TEST_ASSERT_NOT_NULL_MESSAGE(head, "文档1 解析应成功");
  130. TEST_ASSERT_NOT_NULL(end1);
  131. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
  132. // 将 parseEndPtr 预置为“文档1结束位置”,用于验证失败调用不会修改它。
  133. const char *failEnd = end1;
  134. uint32_t remain = (uint32_t)(len - (uint32_t)(end1 - stream));
  135. RyanJson_t bad = RyanJsonParseOptions(end1, remain, RyanJsonFalse, &failEnd);
  136. TEST_ASSERT_NULL_MESSAGE(bad, "文档2(非法) 解析应失败");
  137. TEST_ASSERT_EQUAL_PTR_MESSAGE(end1, failEnd, "失败解析不应改写调用方的 parseEndPtr");
  138. // 从保留下来的偏移位置继续重同步到文档3。
  139. const char *tailStart = strstr(failEnd, "{\"tail\":2}");
  140. TEST_ASSERT_NOT_NULL_MESSAGE(tailStart, "应可基于 failEnd 重同步定位文档3");
  141. RyanJson_t tail = RyanJsonParseOptions(tailStart, (uint32_t)strlen(tailStart), RyanJsonTrue, NULL);
  142. TEST_ASSERT_NOT_NULL_MESSAGE(tail, "文档3 解析应成功");
  143. TEST_ASSERT_EQUAL_INT(2, RyanJsonGetIntValue(RyanJsonGetObjectToKey(tail, "tail")));
  144. // 再次确认文档1 未被失败路径污染。
  145. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(head, "head")));
  146. RyanJsonDelete(tail);
  147. RyanJsonDelete(head);
  148. }
  149. static void testLoadFailureOomParse(void)
  150. {
  151. const char *longKeyJson =
  152. "{\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\":1}";
  153. UNITY_TEST_OOM_BEGIN(0);
  154. RyanJson_t json = RyanJsonParse("{\"a\":1}");
  155. UNITY_TEST_OOM_END();
  156. if (json) { RyanJsonDelete(json); }
  157. TEST_ASSERT_NULL_MESSAGE(json, "Parse OOM(根节点分配失败) 应返回 NULL");
  158. UNITY_TEST_OOM_BEGIN(1); // root 成功,key buffer 失败
  159. json = RyanJsonParse(longKeyJson);
  160. UNITY_TEST_OOM_END();
  161. if (json) { RyanJsonDelete(json); }
  162. TEST_ASSERT_NULL_MESSAGE(json, "Parse OOM 应返回 NULL");
  163. }
  164. static void testLoadFailureAllocatedKeyCleanupOnValueError(void)
  165. {
  166. const char *bad = "{\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\":}";
  167. TEST_ASSERT_NULL_MESSAGE(RyanJsonParse(bad), "长 key 后 value 非法时应解析失败");
  168. }
  169. static void testLoadParseOptionsSequentialOomRecovery(void)
  170. {
  171. // 复杂链路:
  172. // ParseOptions(文档1成功) -> 人工 OOM 让文档2 失败 -> 恢复 hooks 后文档2 再解析成功。
  173. const char *stream = "{\"a\":1}{\"b\":2}";
  174. const uint32_t len = (uint32_t)strlen(stream);
  175. const char *end = NULL;
  176. RyanJson_t first = RyanJsonParseOptions(stream, len, RyanJsonFalse, &end);
  177. TEST_ASSERT_NOT_NULL_MESSAGE(first, "文档1 解析应成功");
  178. TEST_ASSERT_NOT_NULL(end);
  179. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(first, "a")));
  180. uint32_t remain = (uint32_t)(len - (uint32_t)(end - stream));
  181. UNITY_TEST_OOM_BEGIN(0);
  182. RyanJson_t secondFail = RyanJsonParseOptions(end, remain, RyanJsonTrue, NULL);
  183. UNITY_TEST_OOM_END();
  184. TEST_ASSERT_NULL_MESSAGE(secondFail, "OOM 注入下文档2 解析应失败");
  185. RyanJson_t second = RyanJsonParseOptions(end, remain, RyanJsonTrue, NULL);
  186. TEST_ASSERT_NOT_NULL_MESSAGE(second, "恢复 hooks 后文档2 应可解析成功");
  187. TEST_ASSERT_EQUAL_INT(2, RyanJsonGetIntValue(RyanJsonGetObjectToKey(second, "b")));
  188. // 再确认文档1 不受 OOM 链路污染。
  189. TEST_ASSERT_EQUAL_INT(1, RyanJsonGetIntValue(RyanJsonGetObjectToKey(first, "a")));
  190. RyanJsonDelete(second);
  191. RyanJsonDelete(first);
  192. }
  193. static void testLoadFailureInvalidUtf8AndEmbeddedNull(void)
  194. {
  195. // 复杂输入场景:
  196. // - 非法 UTF-8 字节序列(截断、错误续字节、过长编码);
  197. // - String 内容中内嵌 NUL(\0)字节。
  198. // 目标:
  199. // - 记录当前实现对“非法 UTF-8 原始字节”的行为:按普通字节透传,不在 Parse 阶段拦截;
  200. // - 验证内嵌 NUL(控制字符)仍会严格失败,防止混入不可见截断字节。
  201. const uint8_t invalidUtf8Truncated2[] = {'{', '"', 's', '"', ':', '"', 0xC2, '"', '}', '\0'};
  202. const uint8_t invalidUtf8BadContinuation[] = {'{', '"', 's', '"', ':', '"', 0xE2, 0x28, 0xA1, '"', '}', '\0'};
  203. const uint8_t invalidUtf8Overlong[] = {'{', '"', 's', '"', ':', '"', 0xC0, 0x80, '"', '}', '\0'};
  204. const uint8_t invalidUtf8Truncated4[] = {'{', '"', 's', '"', ':', '"', 0xF0, 0x9F, 0x92, '"', '}', '\0'};
  205. {
  206. RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8Truncated2, (uint32_t)(sizeof(invalidUtf8Truncated2) - 1U),
  207. RyanJsonTrue, NULL);
  208. TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,截断 2 字节 UTF-8 原始字节应被透传解析");
  209. char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
  210. TEST_ASSERT_NOT_NULL(s);
  211. TEST_ASSERT_EQUAL_UINT8((uint8_t)0xC2, (uint8_t)s[0]);
  212. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[1]);
  213. RyanJsonDelete(doc);
  214. }
  215. {
  216. RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8BadContinuation,
  217. (uint32_t)(sizeof(invalidUtf8BadContinuation) - 1U), RyanJsonTrue, NULL);
  218. TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,错误续字节 UTF-8 原始字节应被透传解析");
  219. char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
  220. TEST_ASSERT_NOT_NULL(s);
  221. TEST_ASSERT_EQUAL_UINT8((uint8_t)0xE2, (uint8_t)s[0]);
  222. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x28, (uint8_t)s[1]);
  223. TEST_ASSERT_EQUAL_UINT8((uint8_t)0xA1, (uint8_t)s[2]);
  224. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[3]);
  225. RyanJsonDelete(doc);
  226. }
  227. {
  228. RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8Overlong, (uint32_t)(sizeof(invalidUtf8Overlong) - 1U),
  229. RyanJsonTrue, NULL);
  230. TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,过长编码 UTF-8 原始字节应被透传解析");
  231. char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
  232. TEST_ASSERT_NOT_NULL(s);
  233. TEST_ASSERT_EQUAL_UINT8((uint8_t)0xC0, (uint8_t)s[0]);
  234. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x80, (uint8_t)s[1]);
  235. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[2]);
  236. RyanJsonDelete(doc);
  237. }
  238. {
  239. RyanJson_t doc = RyanJsonParseOptions((const char *)invalidUtf8Truncated4, (uint32_t)(sizeof(invalidUtf8Truncated4) - 1U),
  240. RyanJsonTrue, NULL);
  241. TEST_ASSERT_NOT_NULL_MESSAGE(doc, "当前实现下,截断 4 字节 UTF-8 原始字节应被透传解析");
  242. char *s = RyanJsonGetStringValue(RyanJsonGetObjectToKey(doc, "s"));
  243. TEST_ASSERT_NOT_NULL(s);
  244. TEST_ASSERT_EQUAL_UINT8((uint8_t)0xF0, (uint8_t)s[0]);
  245. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x9F, (uint8_t)s[1]);
  246. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x92, (uint8_t)s[2]);
  247. TEST_ASSERT_EQUAL_UINT8((uint8_t)0x00, (uint8_t)s[3]);
  248. RyanJsonDelete(doc);
  249. }
  250. const uint8_t embeddedNulInString[] = {'{', '"', 's', '"', ':', '"', 'a', 'b', '\0', 'c', 'd', '"', '}', '\0'};
  251. TEST_ASSERT_NULL_MESSAGE(
  252. RyanJsonParseOptions((const char *)embeddedNulInString, (uint32_t)(sizeof(embeddedNulInString) - 1U), RyanJsonTrue, NULL),
  253. "String 内嵌 NUL 且 strict 终止时应解析失败");
  254. TEST_ASSERT_NULL_MESSAGE(
  255. RyanJsonParseOptions((const char *)embeddedNulInString, (uint32_t)(sizeof(embeddedNulInString) - 1U), RyanJsonFalse, NULL),
  256. "String 内嵌 NUL 且流式解析时也应解析失败");
  257. }
  258. void testLoadFailureRunner(void)
  259. {
  260. UnitySetTestFile(__FILE__);
  261. RUN_TEST(testLoadFailureNullInput);
  262. RUN_TEST(testLoadFailureWhitespaceOnlySlice);
  263. RUN_TEST(testLoadFailureHugeNumberOverflow);
  264. RUN_TEST(testLoadFailureExponentAccumulatorOverflow);
  265. RUN_TEST(testLoadParseOptionsFailure);
  266. RUN_TEST(testLoadParseOptionsSequentialValidThenInvalidIsolation);
  267. RUN_TEST(testLoadParseOptionsBinaryTailFailureIsolation);
  268. RUN_TEST(testLoadParseOptionsSequentialInvalidThenTailResync);
  269. RUN_TEST(testLoadParseOptionsFailureKeepsEndPtrStable);
  270. RUN_TEST(testLoadFailureOomParse);
  271. RUN_TEST(testLoadFailureAllocatedKeyCleanupOnValueError);
  272. RUN_TEST(testLoadParseOptionsSequentialOomRecovery);
  273. RUN_TEST(testLoadFailureInvalidUtf8AndEmbeddedNull);
  274. }