init
This commit is contained in:
commit
e54390a9f8
85 files changed
+29923
No files matched your search
+122
@@ -0,0 +1,122 @@
|
||||
/* reduce.c - 把 fuzz 找到的失败输入最小化到最短复现样例
|
||||
*
|
||||
* 判定(oracle):同一段输入,"一次性喂" 与 "逐字节喂" 的渲染结果不一致。
|
||||
* 用法: build/reduce build/md_fail.bin
|
||||
*/
|
||||
#include "markdown.h"
|
||||
#include "util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 注意:md_feed 只接受 NUL 结尾的字符串,而最小化过程中的缓冲区
|
||||
* 是"长度受限"的,所以必须先做一份带结尾符的副本。 */
|
||||
static char *render(const char *src, size_t len, size_t chunk)
|
||||
{
|
||||
char *text = xmalloc(len + 1);
|
||||
memcpy(text, src, len);
|
||||
text[len] = '\0';
|
||||
|
||||
char *buf = NULL;
|
||||
size_t size = 0;
|
||||
FILE *f = open_memstream(&buf, &size);
|
||||
if (!f) { free(text); return NULL; }
|
||||
MdRenderer *r = md_new(1);
|
||||
md_set_out(r, f);
|
||||
if (chunk == 0) {
|
||||
md_feed(r, text);
|
||||
} else {
|
||||
for (size_t i = 0; i < len; i += chunk) {
|
||||
size_t n = len - i < chunk ? len - i : chunk;
|
||||
char *t = xstrndup(text + i, n);
|
||||
md_feed(r, t);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
md_finish(r);
|
||||
md_free(r);
|
||||
fclose(f);
|
||||
free(text);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* 只要"一次性"与"逐字节"不一致就算失败 */
|
||||
static int fails(const char *src, size_t len)
|
||||
{
|
||||
if (len == 0) return 0;
|
||||
/* md_feed 以 NUL 结尾,输入里不能有 NUL */
|
||||
for (size_t i = 0; i < len; i++) if (src[i] == '\0') return 0;
|
||||
char *a = render(src, len, 0);
|
||||
char *b = render(src, len, 1);
|
||||
int bad = (a && b && strcmp(a, b) != 0);
|
||||
if (bad) {
|
||||
/* 确认真的不是"空输出 vs 空输出"这种假阳性 */
|
||||
if (strcmp(a, b) == 0) bad = 0;
|
||||
}
|
||||
free(a); free(b);
|
||||
return bad;
|
||||
}
|
||||
|
||||
static void show(const char *label, const char *buf, size_t len)
|
||||
{
|
||||
printf("%s (%zu 字节): ", label, len);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
unsigned char c = (unsigned char)buf[i];
|
||||
if (c == '\n') printf("\\n");
|
||||
else if (c == '\t') printf("\\t");
|
||||
else if (c == 0x1b) printf("<ESC>");
|
||||
else if (c >= 0x20 && c < 0x7f) putchar(c);
|
||||
else printf("\\x%02x", c);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) { fputs("用法: reduce <失败输入文件>\n", stderr); return 2; }
|
||||
size_t len = 0;
|
||||
char *buf = read_entire_file(argv[1], &len);
|
||||
if (!buf) { perror("读取失败"); return 2; }
|
||||
if (!fails(buf, len)) { puts("该输入当前不复现失败"); free(buf); return 1; }
|
||||
|
||||
puts("== 原始失败输入 ==");
|
||||
show(" ", buf, len);
|
||||
{
|
||||
char *a = render(buf, len, 0), *b = render(buf, len, 1);
|
||||
show(" 一次性", a, strlen(a));
|
||||
show(" 逐字节", b, strlen(b));
|
||||
free(a); free(b);
|
||||
}
|
||||
|
||||
/* 贪心块删除 */
|
||||
size_t gran = len / 2;
|
||||
while (gran >= 1) {
|
||||
size_t i = 0;
|
||||
while (i + gran <= len) {
|
||||
char *save = xmalloc(gran);
|
||||
memcpy(save, buf + i, gran);
|
||||
memmove(buf + i, buf + i + gran, len - i - gran);
|
||||
if (fails(buf, len - gran)) {
|
||||
len -= gran;
|
||||
} else {
|
||||
memmove(buf + i + gran, buf + i, len - i - gran);
|
||||
memcpy(buf + i, save, gran);
|
||||
i += gran;
|
||||
}
|
||||
free(save);
|
||||
}
|
||||
if (gran == 1) break;
|
||||
gran /= 2;
|
||||
}
|
||||
|
||||
printf("\n== 最小复现样例 (%zu 字节) ==\n", len);
|
||||
show(" ", buf, len);
|
||||
{
|
||||
char *a = render(buf, len, 0), *b = render(buf, len, 1);
|
||||
show(" 一次性", a, strlen(a));
|
||||
show(" 逐字节", b, strlen(b));
|
||||
free(a); free(b);
|
||||
}
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user