sdk: 修 ILP32 尺寸算术溢出 — 畸形 BMP 经公开入口 SIGSEGV(与主仓同源补丁)
meow_png_stream.c 的 BMP 路径用 32 位 long 算 rowsize=(w*bpp+31)/32*4,
w≥2^27 且 bpp=32 时 w*bpp 回绕 → rowsize=0 → malloc(0),随后采样下标
sx=(int)((long)tx*w/tw) 同样溢出成负数,row+sx*4 越界读。
实测(zig cc --target=arm-linux-musleabi,ILP32,与 mipsel 同数据模型):
修复前 w=0x07FFFFFF → r=0(安全)
w=0x08000000 → SIGSEGV(139) 只需 54 字节 BMP 头
w=0x10000000/0x40000000 → SIGSEGV
同一文件在 LP64 宿主 → NULL,宿主测试天然看不到这一类
修复前走 stb 路径 → NULL,即合并图像栈后属行为退化
改法(只加闸,合法尺寸的数值路径不变):
- MZP_MAX_DIM (1<<20) 守卫 + mzp_bytes_ok() 64 位校验
- 声明尺寸先取 int 再升 long long;h=INT_MIN 用 -(long long)hi 取绝对值
- rowsize / 行偏移 off / 采样下标 sx 全部升 64 位,sx 夹在 [0,w-1]
- fseek 前 off > LONG_MAX 拒绝;目标 RGBA 字节数 64 位验后再 malloc
- meow_jpeg.c 同类审计:mj_layout() 返回 unsigned long long + MJ_LAYOUT_BAD、
mj_sof() 的 target_bytes/plane、mj_output() 的 y*vh/x*vw、
meow_img_target_dims() 与 mj_pick_n() 的 h*maxdim 全部升 64 位
新增 S17 回归(10 组畸形 BMP + 2 个合法正例 + 1 个 PNG 回绕点)。
可证伪性:旧代码 + 新测试在 ARM32 上 exit 139,新代码 ALL PASS。
S17 的 32 位覆盖需 ARM32 交叉编译(宿主 LP64 只能固定 NULL 契约),
复现命令写在 test_meow_img_stream.c 文件头的「ILP32 回归」小节。
验证:本仓 test_meow_img_stream ALL PASS;sdk/build_sdk.sh 34 成员;
tuku 四套脚本全绿(run_tests 644 PASS / zip 106 / cbz 296 / webtoon PASS)。
This commit is contained in:
1 parent
27a3750e36
commit
342c612358
3 files changed
+300
-47
No files matched your search
+76
-25
@@ -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));
|
||||
|
||||
Reference in new issue
Block a user