style: 应用 clang-format
This commit is contained in:
1 parent
016a45bc6e
commit
d1612f1d31
65 files changed
+7288
-5983
No files matched your search
+122
-72
@@ -1,9 +1,9 @@
|
||||
/* fuzz.c - 对抗性模糊测试: JSON 解析器 + Markdown 渲染器
|
||||
*
|
||||
* 这两个模块是"解析不可信输入"的地方 (服务端响应、模型输出),
|
||||
* 也是最可能出崩溃/越界/泄漏的地方. 所以用随机输入去砸它们.
|
||||
* 这两个模块是"解析不可信输入"的地方 (服务端响应、模型输出),
|
||||
* 也是最可能出崩溃/越界/泄漏的地方. 所以用随机输入去砸它们.
|
||||
*
|
||||
* 检查的不变量:
|
||||
* 检查的不变量:
|
||||
* JSON
|
||||
* 1. 任何输入都不能崩溃、不能泄漏 (靠 ASan/UBSan/LeakSanitizer)
|
||||
* 2. 解析成功 => dump 再 parse 再 dump, 两次 dump 必须一致 (幂等)
|
||||
@@ -19,15 +19,14 @@
|
||||
#include "markdown.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static uint64_t RNG;
|
||||
|
||||
static uint32_t rnd(void)
|
||||
{
|
||||
static uint32_t rnd(void) {
|
||||
RNG ^= RNG << 13;
|
||||
RNG ^= RNG >> 7;
|
||||
RNG ^= RNG << 17;
|
||||
@@ -41,13 +40,24 @@ static uint32_t rnd_below(uint32_t n) { return n ? rnd() % n : 0; }
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static const char *STR_POOL[] = {
|
||||
"", "a", "hello", "中文", "😀", "line\nbreak", "tab\there", "quote\"x",
|
||||
"back\\slash", "ctrl\x01\x1f", "surrogate\u00e9", "aaaaaaaaaaaaaaaaaaaa",
|
||||
"/", "\x7f", "\xc3\xa9\xe4\xb8\xad",
|
||||
"",
|
||||
"a",
|
||||
"hello",
|
||||
"中文",
|
||||
"😀",
|
||||
"line\nbreak",
|
||||
"tab\there",
|
||||
"quote\"x",
|
||||
"back\\slash",
|
||||
"ctrl\x01\x1f",
|
||||
"surrogate\u00e9",
|
||||
"aaaaaaaaaaaaaaaaaaaa",
|
||||
"/",
|
||||
"\x7f",
|
||||
"\xc3\xa9\xe4\xb8\xad",
|
||||
};
|
||||
|
||||
static void gen_json_string(StrBuf *sb)
|
||||
{
|
||||
static void gen_json_string(StrBuf *sb) {
|
||||
const char *s = STR_POOL[rnd_below(sizeof STR_POOL / sizeof STR_POOL[0])];
|
||||
char *q = json_quote_string(s);
|
||||
sb_puts(sb, q);
|
||||
@@ -55,27 +65,37 @@ static void gen_json_string(StrBuf *sb)
|
||||
}
|
||||
|
||||
/* 递归生成合法 JSON */
|
||||
static void gen_json(StrBuf *sb, int depth)
|
||||
{
|
||||
static void gen_json(StrBuf *sb, int depth) {
|
||||
uint32_t kind = rnd_below(depth > 3 ? 4 : 6);
|
||||
switch (kind) {
|
||||
case 0: sb_puts(sb, "null"); break;
|
||||
case 1: sb_puts(sb, rnd() & 1 ? "true" : "false"); break;
|
||||
case 0:
|
||||
sb_puts(sb, "null");
|
||||
break;
|
||||
case 1:
|
||||
sb_puts(sb, rnd() & 1 ? "true" : "false");
|
||||
break;
|
||||
case 2: {
|
||||
char buf[64];
|
||||
int mode = (int)rnd_below(3);
|
||||
if (mode == 0) snprintf(buf, sizeof buf, "%d", (int)rnd() - 1000);
|
||||
else if (mode == 1) snprintf(buf, sizeof buf, "%.6g", (double)rnd() / 7.0);
|
||||
else snprintf(buf, sizeof buf, "%de%d", (int)rnd_below(1000), (int)rnd_below(20) - 10);
|
||||
if (mode == 0)
|
||||
snprintf(buf, sizeof buf, "%d", (int)rnd() - 1000);
|
||||
else if (mode == 1)
|
||||
snprintf(buf, sizeof buf, "%.6g", (double)rnd() / 7.0);
|
||||
else
|
||||
snprintf(buf, sizeof buf, "%de%d", (int)rnd_below(1000),
|
||||
(int)rnd_below(20) - 10);
|
||||
sb_puts(sb, buf);
|
||||
break;
|
||||
}
|
||||
case 3: gen_json_string(sb); break;
|
||||
case 3:
|
||||
gen_json_string(sb);
|
||||
break;
|
||||
case 4: {
|
||||
sb_putc(sb, '[');
|
||||
uint32_t n = rnd_below(5);
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
if (i) sb_putc(sb, ',');
|
||||
if (i)
|
||||
sb_putc(sb, ',');
|
||||
gen_json(sb, depth + 1);
|
||||
}
|
||||
sb_putc(sb, ']');
|
||||
@@ -85,7 +105,8 @@ static void gen_json(StrBuf *sb, int depth)
|
||||
sb_putc(sb, '{');
|
||||
uint32_t n = rnd_below(5);
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
if (i) sb_putc(sb, ',');
|
||||
if (i)
|
||||
sb_putc(sb, ',');
|
||||
gen_json_string(sb);
|
||||
sb_putc(sb, ':');
|
||||
gen_json(sb, depth + 1);
|
||||
@@ -97,8 +118,7 @@ static void gen_json(StrBuf *sb, int depth)
|
||||
}
|
||||
|
||||
/* 返回 1 表示这一轮失败 */
|
||||
static int fuzz_json_once(char *err, size_t errlen)
|
||||
{
|
||||
static int fuzz_json_once(char *err, size_t errlen) {
|
||||
StrBuf sb;
|
||||
sb_init(&sb);
|
||||
|
||||
@@ -107,36 +127,43 @@ static int fuzz_json_once(char *err, size_t errlen)
|
||||
/* 变异模式: 先造合法 JSON, 再随机破坏 */
|
||||
gen_json(&sb, 0);
|
||||
size_t n = sb.len;
|
||||
if (n == 0) { sb_free(&sb); return 0; }
|
||||
/* 每次突变都必须基于"当前"长度. 早期版本用了突变前记下的旧长度,
|
||||
* 截断之后再按下标写就会覆盖掉结尾的 '\0', 后续 strlen 冲出分配区,
|
||||
* 让 ASan 在 json_parse 里报出假阳性 —— 模糊测试器自己不可信比没有更糟. */
|
||||
if (n == 0) {
|
||||
sb_free(&sb);
|
||||
return 0;
|
||||
}
|
||||
/* 每次突变都必须基于"当前"长度. 早期版本用了突变前记下的旧长度,
|
||||
* 截断之后再按下标写就会覆盖掉结尾的 '\0', 后续 strlen 冲出分配区,
|
||||
* 让 ASan 在 json_parse 里报出假阳性 —— 模糊测试器自己不可信比没有更糟.
|
||||
*/
|
||||
uint32_t muts = 1 + rnd_below(4);
|
||||
for (uint32_t i = 0; i < muts && sb.len > 0; i++) {
|
||||
uint32_t op = rnd_below(3);
|
||||
if (op == 0) { /* 翻一个字节 (下标恒 < len) */
|
||||
if (op == 0) { /* 翻一个字节 (下标恒 < len) */
|
||||
sb.data[rnd_below((uint32_t)sb.len)] = (char)(rnd() & 0xFF);
|
||||
} else if (op == 1) { /* 截断 */
|
||||
} else if (op == 1) { /* 截断 */
|
||||
sb.len = rnd_below((uint32_t)sb.len);
|
||||
} else { /* 插入一个字节 */
|
||||
} else { /* 插入一个字节 */
|
||||
if (sb.len < 4096) {
|
||||
size_t pos = rnd_below((uint32_t)sb.len + 1);
|
||||
sb_reserve(&sb, 1);
|
||||
memmove(sb.data + pos + 1, sb.data + pos, sb.len - pos + 1);
|
||||
sb.data[pos] = (char)"{}[]\",:0123456789.eE+-truefalsn"[rnd_below(30)];
|
||||
sb.data[pos] =
|
||||
(char)"{}[]\",:0123456789.eE+-truefalsn"[rnd_below(30)];
|
||||
sb.len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 无论怎么突变, 结尾符必须是好的 */
|
||||
if (sb.data) sb.data[sb.len] = '\0';
|
||||
if (sb.data)
|
||||
sb.data[sb.len] = '\0';
|
||||
n = sb.len;
|
||||
} else {
|
||||
/* 纯随机字节 */
|
||||
uint32_t n = rnd_below(256);
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
/* 偏向 JSON 元字符, 提高触发率 */
|
||||
static const char meta[] = "{}[]\",:0123456789.eE+-truefalsn \t\n\\/u";
|
||||
static const char meta[] =
|
||||
"{}[]\",:0123456789.eE+-truefalsn \t\n\\/u";
|
||||
char c = (rnd() & 3) ? meta[rnd_below(sizeof meta - 1)]
|
||||
: (char)(rnd() & 0xFF);
|
||||
sb_putc(&sb, c);
|
||||
@@ -175,14 +202,19 @@ static int fuzz_json_once(char *err, size_t errlen)
|
||||
}
|
||||
d2 = json_dump(v2);
|
||||
if (strcmp(d1, d2) != 0) {
|
||||
snprintf(err, errlen, "dump 不幂等: \n 输入 %.200s\n 一次 %.200s\n 二次 %.200s",
|
||||
input, d1, d2);
|
||||
free(d1); free(d2);
|
||||
json_free(v); json_free(v2);
|
||||
snprintf(
|
||||
err, errlen,
|
||||
"dump 不幂等: \n 输入 %.200s\n 一次 %.200s\n 二次 %.200s",
|
||||
input, d1, d2);
|
||||
free(d1);
|
||||
free(d2);
|
||||
json_free(v);
|
||||
json_free(v2);
|
||||
sb_free(&sb);
|
||||
return 1;
|
||||
}
|
||||
free(d1); free(d2);
|
||||
free(d1);
|
||||
free(d2);
|
||||
json_free(v2);
|
||||
json_free(v);
|
||||
}
|
||||
@@ -197,12 +229,12 @@ static int fuzz_json_once(char *err, size_t errlen)
|
||||
#define MD_RENDER_LIMIT (1u << 20)
|
||||
|
||||
/* 渲染到内存; chunk==0 表示一次性 */
|
||||
static char *md_render(const char *src, size_t len, size_t chunk)
|
||||
{
|
||||
static char *md_render(const char *src, size_t len, size_t chunk) {
|
||||
char *buf = NULL;
|
||||
size_t size = 0;
|
||||
FILE *f = open_memstream(&buf, &size);
|
||||
if (!f) return NULL;
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
MdRenderer *r = md_new(1);
|
||||
md_set_out(r, f);
|
||||
@@ -222,16 +254,14 @@ static char *md_render(const char *src, size_t len, size_t chunk)
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int markdown_metachar(int c)
|
||||
{
|
||||
static int markdown_metachar(int c) {
|
||||
/* 这些字符会触发渲染器改写输出; 其余字符必须原样通过 */
|
||||
return c == '*' || c == '_' || c == '`' || c == '[' || c == ']' ||
|
||||
c == '#' || c == '>' || c == '-' || c == '+' || c == '|' ||
|
||||
c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
static int fuzz_md_once(char *err, size_t errlen)
|
||||
{
|
||||
static int fuzz_md_once(char *err, size_t errlen) {
|
||||
StrBuf sb;
|
||||
sb_init(&sb);
|
||||
|
||||
@@ -240,7 +270,7 @@ static int fuzz_md_once(char *err, size_t errlen)
|
||||
size_t alen = sizeof alpha - 1;
|
||||
|
||||
uint32_t n = rnd_below(400);
|
||||
int pure = (int)rnd_below(4) == 0; /* 1/4 概率生成"无元字符"的干净文本 */
|
||||
int pure = (int)rnd_below(4) == 0; /* 1/4 概率生成"无元字符"的干净文本 */
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
char c;
|
||||
if (pure) {
|
||||
@@ -249,41 +279,57 @@ static int fuzz_md_once(char *err, size_t errlen)
|
||||
} while (markdown_metachar((unsigned char)c) || c == '\0');
|
||||
} else {
|
||||
uint32_t mode = rnd_below(10);
|
||||
if (mode < 7) c = alpha[rnd_below((uint32_t)alen)];
|
||||
else if (mode < 9) c = (char)(0x20 + rnd_below(0x5F));
|
||||
else c = (char)(rnd() & 0xFF); /* 偶尔来点非法 UTF-8 */
|
||||
if (mode < 7)
|
||||
c = alpha[rnd_below((uint32_t)alen)];
|
||||
else if (mode < 9)
|
||||
c = (char)(0x20 + rnd_below(0x5F));
|
||||
else
|
||||
c = (char)(rnd() & 0xFF); /* 偶尔来点非法 UTF-8 */
|
||||
}
|
||||
if (c == '\0') c = 'x';
|
||||
if (c == '\0')
|
||||
c = 'x';
|
||||
sb_putc(&sb, c);
|
||||
}
|
||||
|
||||
const char *src = sb.data ? sb.data : "";
|
||||
size_t len = sb.len;
|
||||
if (len == 0) { sb_free(&sb); return 0; }
|
||||
if (len == 0) {
|
||||
sb_free(&sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *whole = md_render(src, len, 0);
|
||||
if (!whole) { sb_free(&sb); return 0; }
|
||||
if (!whole) {
|
||||
sb_free(&sb);
|
||||
return 0;
|
||||
}
|
||||
if (strlen(whole) > MD_RENDER_LIMIT * 4) {
|
||||
snprintf(err, errlen, "输出异常膨胀: 输入 %zu 字节 => %zu 字节",
|
||||
len, strlen(whole));
|
||||
free(whole); sb_free(&sb);
|
||||
snprintf(err, errlen, "输出异常膨胀: 输入 %zu 字节 => %zu 字节", len,
|
||||
strlen(whole));
|
||||
free(whole);
|
||||
sb_free(&sb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 分片不变性 */
|
||||
size_t chunks[4];
|
||||
for (int k = 0; k < 4; k++) chunks[k] = 1 + rnd_below(8);
|
||||
for (int k = 0; k < 4; k++)
|
||||
chunks[k] = 1 + rnd_below(8);
|
||||
for (int k = 0; k < 4; k++) {
|
||||
char *part = md_render(src, len, chunks[k]);
|
||||
if (!part) continue;
|
||||
if (!part)
|
||||
continue;
|
||||
if (strcmp(whole, part) != 0) {
|
||||
snprintf(err, errlen,
|
||||
"分片不变性被破坏 (%zu 字节/片)",
|
||||
chunks[k]);
|
||||
snprintf(err, errlen, "分片不变性被破坏 (%zu 字节/片)", chunks[k]);
|
||||
/* 落盘, 方便用 build/reduce 做最小化 */
|
||||
FILE *fp = fopen("build/md_fail.bin", "wb");
|
||||
if (fp) { fwrite(src, 1, len, fp); fclose(fp); }
|
||||
free(part); free(whole); sb_free(&sb);
|
||||
if (fp) {
|
||||
fwrite(src, 1, len, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
free(part);
|
||||
free(whole);
|
||||
sb_free(&sb);
|
||||
return 1;
|
||||
}
|
||||
free(part);
|
||||
@@ -292,9 +338,11 @@ static int fuzz_md_once(char *err, size_t errlen)
|
||||
/* 无元字符 => 必须逐字节原样 */
|
||||
if (pure) {
|
||||
if (strcmp(whole, src) != 0) {
|
||||
snprintf(err, errlen, "纯净文本被改写了: \n 输入 %.300s\n 输出 %.300s",
|
||||
src, whole);
|
||||
free(whole); sb_free(&sb);
|
||||
snprintf(err, errlen,
|
||||
"纯净文本被改写了: \n 输入 %.300s\n 输出 %.300s", src,
|
||||
whole);
|
||||
free(whole);
|
||||
sb_free(&sb);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -306,23 +354,24 @@ static int fuzz_md_once(char *err, size_t errlen)
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
long iters = (argc > 1) ? strtol(argv[1], NULL, 10) : 3000;
|
||||
RNG = (argc > 2) ? strtoull(argv[2], NULL, 10) : 0x9E3779B97F4A7C15ULL;
|
||||
if (!RNG) RNG = 1;
|
||||
if (!RNG)
|
||||
RNG = 1;
|
||||
|
||||
char err[512];
|
||||
long json_fail = 0, md_fail = 0;
|
||||
|
||||
printf("模糊测试: JSON %ld 轮 + Markdown %ld 轮 (种子 %llu)\n",
|
||||
iters, iters, (unsigned long long)RNG);
|
||||
printf("模糊测试: JSON %ld 轮 + Markdown %ld 轮 (种子 %llu)\n", iters,
|
||||
iters, (unsigned long long)RNG);
|
||||
|
||||
for (long i = 0; i < iters; i++) {
|
||||
err[0] = '\0';
|
||||
if (fuzz_json_once(err, sizeof err)) {
|
||||
printf(" ✗ JSON 第 %ld 轮失败: %s\n", i, err);
|
||||
if (++json_fail >= 3) break;
|
||||
if (++json_fail >= 3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf(" JSON %ld 轮, %ld 次失败\n", iters, json_fail);
|
||||
@@ -331,7 +380,8 @@ int main(int argc, char **argv)
|
||||
err[0] = '\0';
|
||||
if (fuzz_md_once(err, sizeof err)) {
|
||||
printf(" ✗ Markdown 第 %ld 轮失败: %s\n", i, err);
|
||||
if (++md_fail >= 3) break;
|
||||
if (++md_fail >= 3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf(" Markdown %ld 轮, %ld 次失败\n", iters, md_fail);
|
||||
|
||||
Reference in new issue
Block a user