fix(sdk): M26 r1 P1 — ILP32 尺寸算术溢出(畸形 BMP 经公开入口 SIGSEGV)+ 加 S17 回归

评审在 ARM32(ILP32,与 mipsel 同数据模型)实测:54 字节 BMP 头声明 w=2^27、bpp=32
时 w*bpp 回绕成 0 → rowsize=0 → malloc(0) → 采样索引 row+sx*4 越界读 → SIGSEGV,
经 meow_img_load_file_dim 端到端可触发;同一文件在 LP64 宿主返回 NULL(不溢出),
合并前走 stb 也是 NULL。即这是相对旧路径的鲁棒性回归。

修复(只加闸,正常路径数值不变):

- meow_png_stream.c(P1 本体)
  · 新增 ILP32 守卫 MZP_MAX_DIM=1<<20 与 mzp_bytes_ok()(unsigned long long 验算,
    超过 SIZE_MAX 即拒)
  · BMP:声明尺寸先升 int/long long 校验(w/h 单边 ≤1<<20;h=INT_MIN 用
    -(long long)hi 取绝对值,不再有 -h 溢出);rowsize 用 64 位算并验;
    行偏移 off / 采样下标 sx 改 long long,sx 夹在 [0,w-1];按行 fseek 前加
    off > LONG_MAX 拒绝;目标 RGBA 字节数 64 位验后再 malloc
  · PNG:IHDR 尺寸原已 ≤65535,补上 tw*th*4 的 64 位校验(32768²×4 = 2^32 会回绕),
    emit_row 的 sy/sx 与输出行偏移改 64 位并夹紧
- meow_jpeg.c(顺手审计同类 32 位算术,评审要求「不要只修点名那处」)
  · mj_layout 返回 unsigned long long(单平面/三平面之和都可能越 2^32),
    越界返回 MJ_LAYOUT_BAD → mj_sof 各处重新布局后都检查
  · target_bytes 与分量 plane malloc 改 64 位验算;目标图字节超 MJ_PEAK_CAP 直接拒
  · mj_output:输出缓冲 64 位验;y*vh / x*vw 采样改 long long 并夹紧
  · meow_img_target_dims / mj_pick_n 的 (long)h*maxdim、8*maxdim 改 long long

- test_meow_img_stream.c:新增 S17(10 组畸形 BMP 声明尺寸 + 2 个合法正例 +
  PNG 回绕点),断言「后端拒绝 + 公开入口不崩(越界尺寸另断言 NULL 且不分配)」;
  文件头补 ILP32 交叉编译命令(宿主 LP64 看不到 32 位回绕,测试注释里明写这一点)

验证:同一测试在 zig arm-linux-musleabi(sizeof(long)=4)下 S1-S17 全绿(206 checks,
exit 0);把后端换回 r1 提交的旧实现,同一 ARM32 二进制 exit 139(说明 S17 真能抓);
宿主 LP64 S1-S17 全绿、B1-B9 全绿;静态库 --verify 仍 78 成员 / 全 MIPS32r2。

同步提醒:这些行与公开仓 ~/Proj/MeowOS-SDK/sdk/{meow_png_stream.c,meow_jpeg.c} 逐字节
同源,同一补丁必须由控制塔同步过去,否则立刻产生双边漂移。
This commit is contained in:
AskaEth committed 2026-11-06 21:27:03 +08:00
1 parent ee5796747d
commit 281f4153ee
3 files changed
+300 -47

No files matched your search

+76 -25
View File
@@ -36,10 +36,10 @@ void meow_img_target_dims(int w, int h, int maxdim, int *tw, int *th)
{
if (w >= h) {
*tw = maxdim;
*th = (int)((long)h * maxdim / w);
*th = (int)((long long)h * maxdim / w); /* 64 位:ILP32 上 h*maxdim 会溢出 */
} else {
*th = maxdim;
*tw = (int)((long)w * maxdim / h);
*tw = (int)((long long)w * maxdim / h);
}
if (*tw < 1) *tw = 1;
if (*th < 1) *th = 1;
@@ -390,21 +390,25 @@ static int mj_ceil_div(int a, int b) { return (a + b - 1) / b; }
* 这种图上 stb 旧路径的峰值本来就受控,直接交回它,档位对外表现不变。 */
static int mj_pick_n(int longside, int maxdim)
{
int n = (8 * maxdim + longside - 1) / longside;
long long n = ((long long)8 * maxdim + longside - 1) / longside; /* 64 位:maxdim 可到 INT_MAX */
if (n < 1) n = 1;
if (n > 7) return 0;
return n;
return (int)n;
}
/* 按各分量当前的 n 计算块网格与平面尺寸,返回平面总字节。 */
static unsigned mj_layout(mj_img *img)
/* 按各分量当前的 n 计算块网格与平面尺寸,返回平面总字节。
* 返回 64 位:ILP32 上单平面 pw*ph 与 3 个平面之和都可能越过 2^32。
* MJ_LAYOUT_BAD 表示该乘积在 32 位 size_t 上无法表示(调用方直接拒绝)。 */
#define MJ_LAYOUT_BAD ((unsigned long long)-1)
static unsigned long long mj_layout(mj_img *img)
{
unsigned total = 0;
unsigned long long total = 0;
int i;
for (i = 0; i < img->ncomp; i++) {
mj_comp *c = &img->c[i];
int sw = mj_ceil_div(img->width * c->h, img->hmax);
int sh = mj_ceil_div(img->height * c->v, img->vmax);
unsigned long long plane;
c->ow = mj_ceil_div(sw, 8);
c->oh = mj_ceil_div(sh, 8);
c->pw = (img->mcus_x * c->h > c->ow ? img->mcus_x * c->h : c->ow) * c->n;
@@ -413,7 +417,9 @@ static unsigned mj_layout(mj_img *img)
c->vh = mj_ceil_div(sh * c->n, 8);
if (c->vw > c->pw) c->vw = c->pw;
if (c->vh > c->ph) c->vh = c->ph;
total += (unsigned)c->pw * (unsigned)c->ph;
plane = (unsigned long long)c->pw * (unsigned long long)c->ph;
if (plane > (unsigned long long)(size_t)-1) return MJ_LAYOUT_BAD;
total += plane;
}
return total;
}
@@ -421,7 +427,7 @@ static unsigned mj_layout(mj_img *img)
static int mj_sof(mj_img *img, int marker, const unsigned char *d, int len, int maxdim)
{
int i, n, longside, base_n;
unsigned int plane_bytes, target_bytes;
unsigned long long plane_bytes, target_bytes;
int tw, th;
if (marker != 0xC0 && marker != 0xC1 && marker != 0xC2) return 0; /* 算术/无损/差分 */
@@ -456,10 +462,14 @@ static int mj_sof(mj_img *img, int marker, const unsigned char *d, int len, int
img->mcus_y = mj_ceil_div(img->height, 8 * img->vmax);
meow_img_target_dims(img->width, img->height, maxdim, &tw, &th);
target_bytes = (unsigned)tw * (unsigned)th * 4u;
/* 64 位算目标图字节:ILP32 上 tw*th*4 在 tw=th=32768 时正好回绕成 0,
* 会让下面的预算闸失效并去 malloc(0)(M26 r1 顺手审计)。 */
target_bytes = (unsigned long long)(unsigned)tw * (unsigned long long)(unsigned)th * 4u;
if (target_bytes > MJ_PEAK_CAP) return 0;
for (i = 0; i < n; i++) img->c[i].n = base_n;
plane_bytes = mj_layout(img);
if (plane_bytes == MJ_LAYOUT_BAD) return 0;
/* 4:4:4 的色度平面与亮度等大(正常 4:2:0 只 1/4):亮度平面超过 400KB 时色度降一档,
* 视觉近无损,省下的正是色度要多占的两份。 */
@@ -467,6 +477,7 @@ static int mj_sof(mj_img *img, int marker, const unsigned char *d, int len, int
plane_bytes / 3 > 400u * 1024u) {
for (i = 1; i < n; i++) if (img->c[i].n > 1) img->c[i].n >>= 1;
plane_bytes = mj_layout(img);
if (plane_bytes == MJ_LAYOUT_BAD) return 0;
}
/* 预算 2.4MB(平面 + 目标图):超了先降色度(亮度保持清晰),再整体降档。
@@ -474,13 +485,17 @@ static int mj_sof(mj_img *img, int marker, const unsigned char *d, int len, int
if (plane_bytes + target_bytes > 2400u * 1024u) {
int ch = 0;
for (i = 1; i < n; i++) if (img->c[i].n > 1) { img->c[i].n >>= 1; ch = 1; }
if (ch) plane_bytes = mj_layout(img);
if (ch) {
plane_bytes = mj_layout(img);
if (plane_bytes == MJ_LAYOUT_BAD) return 0;
}
}
/* 最后一道防线:异常巨图整体降档(最多降到 N=1),不至于一次吃掉几十 MB。 */
while (plane_bytes + target_bytes > MJ_PEAK_CAP && base_n > 1) {
base_n >>= 1;
for (i = 0; i < n; i++) if (img->c[i].n > base_n) img->c[i].n = base_n;
plane_bytes = mj_layout(img);
if (plane_bytes == MJ_LAYOUT_BAD) return 0;
}
/* 硬闸:降到 N=1 仍超上限(畸形/超大声明尺寸,如 23 字节的 SOF 65535² → 亮度面
* 8192² = 64MiB)直接拒绝 → 返回 0 → 回退 stb(stb 对这类文件是 KB 级安全失败)。
@@ -489,7 +504,10 @@ static int mj_sof(mj_img *img, int marker, const unsigned char *d, int len, int
for (i = 0; i < n; i++) {
mj_comp *c = &img->c[i];
size_t bytes = (size_t)c->pw * (size_t)c->ph;
unsigned long long bytes64 = (unsigned long long)c->pw * (unsigned long long)c->ph;
size_t bytes;
if (bytes64 == 0 || bytes64 > (unsigned long long)(size_t)-1) return 0;
bytes = (size_t)bytes64;
c->plane = (unsigned char *)malloc(bytes);
if (!c->plane) return 0;
memset(c->plane, 128, bytes); /* 中性灰底,系数累加即 IDCT */
@@ -753,7 +771,8 @@ static void mj_scan_ac_prog(mj_img *img, const mj_scan *sc)
static unsigned char *mj_output(mj_img *img, int tw, int th, int rgb_mode)
{
unsigned char *out = (unsigned char *)malloc((size_t)tw * th * 4);
unsigned long long ob = (unsigned long long)tw * (unsigned long long)th * 4u;
unsigned char *out;
mj_comp *cy = &img->c[0];
mj_comp *cb = img->ncomp >= 3 ? &img->c[1] : NULL;
mj_comp *cr = img->ncomp >= 3 ? &img->c[2] : NULL;
@@ -761,30 +780,62 @@ static unsigned char *mj_output(mj_img *img, int tw, int th, int rgb_mode)
const unsigned char *pb = cb ? cb->plane : NULL;
const unsigned char *pc = cr ? cr->plane : NULL;
int x, y;
if (ob == 0 || ob > (unsigned long long)(size_t)-1) return NULL;
out = (unsigned char *)malloc((size_t)ob);
if (!out) return NULL;
for (y = 0; y < th; y++) {
int sy = (int)((long)y * cy->vh / th);
/* 64 位:ILP32 上 y*vh、x*vw 都可能越过 2^31(th/vh 各 3 万量级即可) */
long long syl = (long long)y * cy->vh / th;
int sy = (int)(syl < 0 ? 0 : (syl >= (long long)cy->vh ? (long long)cy->vh - 1 : syl));
const unsigned char *ry = py + (size_t)sy * cy->pw;
const unsigned char *rb = pb ? pb + (size_t)((long)y * cb->vh / th) * cb->pw : NULL;
const unsigned char *rc = pc ? pc + (size_t)((long)y * cr->vh / th) * cr->pw : NULL;
unsigned char *dst = out + (size_t)y * tw * 4;
const unsigned char *rb = NULL, *rc = NULL;
if (pb) {
long long s = (long long)y * cb->vh / th;
if (s >= (long long)cb->vh) s = (long long)cb->vh - 1;
if (s < 0) s = 0;
rb = pb + (size_t)s * cb->pw;
}
if (pc) {
long long s = (long long)y * cr->vh / th;
if (s >= (long long)cr->vh) s = (long long)cr->vh - 1;
if (s < 0) s = 0;
rc = pc + (size_t)s * cr->pw;
}
unsigned char *dst = out + (size_t)((unsigned long long)y * (unsigned long long)tw * 4u);
for (x = 0; x < tw; x++) {
int yv = ry[(int)((long)x * cy->vw / tw)];
long long yxl = (long long)x * cy->vw / tw;
int yv;
if (yxl < 0) yxl = 0;
if (yxl >= (long long)cy->vw) yxl = (long long)cy->vw - 1;
yv = ry[(int)yxl];
unsigned char *o = dst + (size_t)x * 4;
if (!rb) {
o[0] = o[1] = o[2] = (unsigned char)yv;
o[3] = 255;
} else if (rgb_mode) {
long long ux = (long long)x * cb->vw / tw;
long long vx = (long long)x * cr->vw / tw;
if (ux < 0) ux = 0;
if (ux >= (long long)cb->vw) ux = (long long)cb->vw - 1;
if (vx < 0) vx = 0;
if (vx >= (long long)cr->vw) vx = (long long)cr->vw - 1;
o[0] = (unsigned char)yv;
o[1] = rb[(int)((long)x * cb->vw / tw)];
o[2] = rc[(int)((long)x * cr->vw / tw)];
o[1] = rb[(int)ux];
o[2] = rc[(int)vx];
o[3] = 255;
} else {
int u = (int)rb[(int)((long)x * cb->vw / tw)] - 128;
int v = (int)rc[(int)((long)x * cr->vw / tw)] - 128;
int r = yv + ((MJ_FIX_R * v) >> 12);
int g = yv - ((MJ_FIX_G * u + MJ_FIX_G2 * v) >> 12);
int b = yv + ((MJ_FIX_B * u) >> 12);
long long ux = (long long)x * cb->vw / tw;
long long vx = (long long)x * cr->vw / tw;
int u, v, r, g, b;
if (ux < 0) ux = 0;
if (ux >= (long long)cb->vw) ux = (long long)cb->vw - 1;
if (vx < 0) vx = 0;
if (vx >= (long long)cr->vw) vx = (long long)cr->vw - 1;
u = (int)rb[(int)ux] - 128;
v = (int)rc[(int)vx] - 128;
r = yv + ((MJ_FIX_R * v) >> 12);
g = yv - ((MJ_FIX_G * u + MJ_FIX_G2 * v) >> 12);
b = yv + ((MJ_FIX_B * u) >> 12);
o[0] = (unsigned char)(r < 0 ? 0 : (r > 255 ? 255 : r));
o[1] = (unsigned char)(g < 0 ? 0 : (g > 255 ? 255 : g));
o[2] = (unsigned char)(b < 0 ? 0 : (b > 255 ? 255 : b));