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:
1 parent
ee5796747d
commit
281f4153ee
3 files changed
+300
-47
No files matched your search
@@ -22,6 +22,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h> /* LONG_MAX:BMP 按行 seek 的 32 位偏移上限 */
|
||||
|
||||
/* ---------------- zlib ABI ---------------- */
|
||||
#if defined(__has_include)
|
||||
@@ -59,6 +60,21 @@ extern const char *zlibVersion(void);
|
||||
# define Z_NO_FLUSH 0
|
||||
#endif
|
||||
|
||||
/* ---------------- ILP32 尺寸算术守卫 ----------------
|
||||
* mipsel / arm-linux-musleabi 上 long 与 size_t 都是 32 位:把「声明尺寸 × bpp」这类
|
||||
* 乘积直接放在 long 里会回绕成小值或 0。实测(ARM32):54 字节 BMP 头声明 w=2^27、
|
||||
* bpp=32 → w*bpp = 2^32 = 0 → rowsize = 0 → malloc(0) → 采样索引 row + sx*4 越界读
|
||||
* → SIGSEGV;同一文件在 LP64 宿主返回 NULL,所以宿主测试天然看不到这类。
|
||||
* 因此:凡「由文件头算出来的字节数/偏移」一律升到 unsigned long long 再验,
|
||||
* 超限即返回 0,由调用方回退 stb(安全失败,与 M26 之前的行为一致)。
|
||||
* 单边上限 1<<20(1,048,576):远超真实 BMP/PNG 声明尺寸,同时保证行字节数
|
||||
* (≤4 MiB)、行偏移与目标 RGBA 都不会在 32 位溢出。 */
|
||||
#define MZP_MAX_DIM (1u << 20)
|
||||
static int mzp_bytes_ok(unsigned long long n)
|
||||
{
|
||||
return n > 0 && n <= (unsigned long long)(size_t)-1;
|
||||
}
|
||||
|
||||
/* ---------------- 字节源 ---------------- */
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
@@ -202,12 +218,15 @@ static int png_emit_row(png_ctx *c)
|
||||
}
|
||||
}
|
||||
if (c->next_ty < c->th) {
|
||||
long sy = (long)c->next_ty * (long)c->h / c->th;
|
||||
if (sy == (long)c->y) {
|
||||
unsigned char *o = c->out + (size_t)c->next_ty * c->tw * 4;
|
||||
long long sy = (long long)c->next_ty * (long long)c->h / c->th; /* 64 位:ILP32 上 *h 会溢出 */
|
||||
if (sy == (long long)c->y) {
|
||||
unsigned char *o = c->out +
|
||||
(size_t)((unsigned long long)c->next_ty * (unsigned long long)c->tw * 4u);
|
||||
int tx;
|
||||
for (tx = 0; tx < c->tw; tx++) {
|
||||
int sx = (int)((long)tx * (long)c->w / c->tw);
|
||||
long long sxl = (long long)tx * (long long)c->w / c->tw;
|
||||
int sx = (int)(sxl < 0 ? 0
|
||||
: (sxl >= (long long)c->w ? (long long)c->w - 1 : sxl));
|
||||
unsigned char px[4];
|
||||
png_get_px(c, dst, sx, px);
|
||||
o[tx * 4] = px[0]; o[tx * 4 + 1] = px[1];
|
||||
@@ -315,9 +334,14 @@ int meow_png_decode_stream(FILE *f, const unsigned char *mem, size_t len,
|
||||
memset(c.cur, 0, c.rowbytes);
|
||||
for (i = 0; i < 256; i++) c.trns_a[i] = 255;
|
||||
meow_img_target_dims((int)c.w, (int)c.h, maxdim, &c.tw, &c.th);
|
||||
c.out = (unsigned char *)malloc((size_t)c.tw * c.th * 4);
|
||||
if (!c.out) goto done;
|
||||
memset(c.out, 0, (size_t)c.tw * c.th * 4);
|
||||
{
|
||||
/* ILP32 上 tw*th*4 会回绕(32768×32768×4 = 2^32 → 0 → malloc(0) 后越界写) */
|
||||
unsigned long long ob = (unsigned long long)c.tw * (unsigned long long)c.th * 4u;
|
||||
if (!mzp_bytes_ok(ob)) goto done;
|
||||
c.out = (unsigned char *)malloc((size_t)ob);
|
||||
if (!c.out) goto done;
|
||||
memset(c.out, 0, (size_t)ob);
|
||||
}
|
||||
memset(&c.z, 0, sizeof c.z);
|
||||
if (inflateInit_(&c.z, zlibVersion(), (int)sizeof c.z) != Z_OK) goto done;
|
||||
c.z_ready = 1;
|
||||
@@ -423,16 +447,25 @@ int meow_bmp_decode_stream(FILE *f, const unsigned char *mem, size_t len,
|
||||
if (dibsize < 40 || dibsize > sizeof dib) return 0; /* BITMAPCOREHEADER/OS2 → stb */
|
||||
memcpy(dib, dh, 4);
|
||||
if (!ps_read(&src, dib + 4, dibsize - 4)) return 0;
|
||||
w = (long)(int)le32(dib + 4);
|
||||
h = (long)(int)le32(dib + 8);
|
||||
bpp = le16(dib + 14);
|
||||
compression = le32(dib + 16);
|
||||
colors = (dibsize >= 36) ? le32(dib + 32) : 0;
|
||||
if (compression != 0) return 0; /* RLE/BITFIELDS → stb */
|
||||
if (w <= 0 || h == 0) return 0;
|
||||
if (bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32) return 0;
|
||||
topdown = h < 0;
|
||||
if (h < 0) h = -h;
|
||||
/* 声明尺寸过 32 位算术闸(M26 r1 P1):w×bpp 与 tx×w 在 ILP32 上会回绕成小值/负数,
|
||||
* 直接导致 malloc(0) + 越界读。h=INT_MIN 时 -h 同样溢出,故升到 long long 再取绝对值。 */
|
||||
{
|
||||
int wi = (int)le32(dib + 4);
|
||||
int hi = (int)le32(dib + 8);
|
||||
long long hll;
|
||||
if (wi <= 0 || hi == 0) return 0;
|
||||
topdown = hi < 0;
|
||||
hll = topdown ? -(long long)hi : (long long)hi;
|
||||
if ((unsigned long long)wi > MZP_MAX_DIM || hll <= 0 ||
|
||||
(unsigned long long)hll > MZP_MAX_DIM) return 0; /* 离奇声明尺寸 → 回退 stb */
|
||||
w = (long)wi;
|
||||
h = (long)hll;
|
||||
}
|
||||
if ((long)(w > h ? w : h) <= maxdim) return 0; /* 小图走旧入口 */
|
||||
|
||||
if (bpp <= 8) {
|
||||
@@ -446,26 +479,38 @@ int meow_bmp_decode_stream(FILE *f, const unsigned char *mem, size_t len,
|
||||
for (; i < 256; i++) pal[i][0] = pal[i][1] = pal[i][2] = 0;
|
||||
}
|
||||
|
||||
rowsize = (w * bpp + 31) / 32 * 4;
|
||||
{
|
||||
/* 行字节数 64 位算(ILP32 上 w*bpp 在 w≥2^27/bpp 时回绕成 0) */
|
||||
unsigned long long rb = ((unsigned long long)w * bpp + 31u) / 32u * 4u;
|
||||
if (!mzp_bytes_ok(rb)) return 0;
|
||||
rowsize = (long)rb; /* w ≤ MZP_MAX_DIM → ≤ 4 MiB */
|
||||
}
|
||||
meow_img_target_dims((int)w, (int)h, maxdim, &tw_, &th_);
|
||||
row = (unsigned char *)malloc((size_t)rowsize);
|
||||
out = (unsigned char *)malloc((size_t)tw_ * th_ * 4);
|
||||
{
|
||||
unsigned long long ob = (unsigned long long)tw_ * (unsigned long long)th_ * 4u;
|
||||
if (mzp_bytes_ok(ob)) out = (unsigned char *)malloc((size_t)ob);
|
||||
}
|
||||
if (!row || !out) goto done;
|
||||
|
||||
for (int ty = 0; ty < th_; ty++) {
|
||||
int sy = (int)((long)ty * h / th_);
|
||||
long file_row = topdown ? sy : (h - 1 - sy);
|
||||
long off = (long)dataoff + file_row * rowsize;
|
||||
unsigned char *o = out + (size_t)ty * tw_ * 4;
|
||||
long long sy = (long long)ty * h / th_;
|
||||
long long file_row = topdown ? sy : ((long long)h - 1 - sy);
|
||||
long long off = (long long)dataoff + file_row * (long long)rowsize;
|
||||
unsigned char *o = out + (size_t)((unsigned long long)ty * (unsigned long long)tw_ * 4u);
|
||||
if (src.mem) {
|
||||
if (off < 0 || (size_t)off + (size_t)rowsize > src.memlen) goto done;
|
||||
memcpy(row, src.mem + off, (size_t)rowsize);
|
||||
if (off < 0 ||
|
||||
(unsigned long long)off + (unsigned long long)rowsize > (unsigned long long)src.memlen)
|
||||
goto done;
|
||||
memcpy(row, src.mem + (size_t)off, (size_t)rowsize);
|
||||
} else {
|
||||
if (fseek(src.f, off, SEEK_SET) != 0) goto done;
|
||||
if (off < 0 || off > (long long)LONG_MAX) goto done; /* fseek 只有 32 位偏移 */
|
||||
if (fseek(src.f, (long)off, SEEK_SET) != 0) goto done;
|
||||
if (fread(row, 1, (size_t)rowsize, src.f) != (size_t)rowsize) goto done;
|
||||
}
|
||||
for (int tx = 0; tx < tw_; tx++) {
|
||||
int sx = (int)((long)tx * w / tw_);
|
||||
long long sxl = (long long)tx * w / tw_;
|
||||
int sx = (int)(sxl < 0 ? 0 : (sxl >= (long long)w ? (long long)w - 1 : sxl));
|
||||
unsigned char *p = o + (size_t)tx * 4;
|
||||
if (bpp == 24) {
|
||||
const unsigned char *q = row + sx * 3;
|
||||
|
||||
Reference in new issue
Block a user