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
+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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
* S8 内存入口 == 文件入口(逐字节),峰值同级
|
||||
* S9 回退:CMYK JPEG(4 分量)、1bpp BMP、Adam7 交错 PNG → 后端拒绝,stb 仍成功;
|
||||
* 截断/垃圾 → NULL(不崩)
|
||||
* S10-S16 缩解阶/restart/DHT/tRNS/step/低熵/progressive-restart 等专项
|
||||
* S17 32 位尺寸算术:畸形 BMP 声明尺寸(w=2^27…、24bpp 回绕点、h=INT_MIN)与 PNG
|
||||
* tw*th*4 回绕点 → 一律 NULL 不崩(ILP32 崩溃回归,M26 r1 P1)
|
||||
*
|
||||
* 夹具:本文件内置生成脚本(Pillow),首次运行时写到夹具目录并执行。
|
||||
* 编译(本 sdk 目录下;--wrap 用于给 malloc/free 家族打点):
|
||||
@@ -20,6 +23,17 @@
|
||||
* -lm -lz \
|
||||
* -Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=free
|
||||
* 运行:./test_meow_img_stream "$PREFIX/tmp/meow_img_stream" (exit 0=PASS / 1=FAIL)
|
||||
*
|
||||
* ILP32 回归(S17 的真覆盖;宿主 LP64 的 long/size_t 是 64 位,看不到 32 位回绕崩溃):
|
||||
* 用 zig 的 arm-linux-musleabi 目标(与 mipsel 同数据模型:long/size_t 均 4 字节),
|
||||
* 需要一份 32 位 libz.a(设备上的 libz.a 就是 mipsel 32 位;本机可从 zlib 源码
|
||||
* 用同一 zig cc --target=arm-linux-musleabi -c 各 .c 后 ar 打包)。命令:
|
||||
* zig cc --target=arm-linux-musleabi -static -O2 -I. -Iinclude -DMEOW_UI_HOST_TEST \
|
||||
* -o test_meow_img_stream_arm test_meow_img_stream.c meow_ui_img.c meow_jpeg.c \
|
||||
* meow_png_stream.c stb_image.c -lm <libz_arm.a> \
|
||||
* -Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=free
|
||||
* ./test_meow_img_stream_arm "$PREFIX/tmp/meow_img_stream"
|
||||
* (aarch64 Android 设备可直跑 32 位 ARM 静态二进制;2026-11-06 实测 S1-S17 全绿。)
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include "meow_ui.h"
|
||||
@@ -566,6 +580,60 @@ static size_t build_sof_only(unsigned char *buf, int w, int h, int ncomp)
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ---- S17 用的畸形头构造器(54 字节 BMP / 仅 IHDR 的 PNG) ---- */
|
||||
static size_t bmp_build(unsigned char *b, unsigned w, unsigned hbits, unsigned bpp)
|
||||
{
|
||||
size_t off = 54;
|
||||
memset(b, 0, off);
|
||||
b[0] = 'B'; b[1] = 'M';
|
||||
b[2] = (unsigned char)(off & 0xFF); b[3] = (unsigned char)((off >> 8) & 0xFF);
|
||||
b[10] = (unsigned char)(off & 0xFF); b[11] = (unsigned char)((off >> 8) & 0xFF);
|
||||
b[14] = 40; /* BITMAPINFOHEADER */
|
||||
b[18] = (unsigned char)(w & 0xFF); b[19] = (unsigned char)((w >> 8) & 0xFF);
|
||||
b[20] = (unsigned char)((w >> 16) & 0xFF); b[21] = (unsigned char)((w >> 24) & 0xFF);
|
||||
b[22] = (unsigned char)(hbits & 0xFF); b[23] = (unsigned char)((hbits >> 8) & 0xFF);
|
||||
b[24] = (unsigned char)((hbits >> 16) & 0xFF); b[25] = (unsigned char)((hbits >> 24) & 0xFF);
|
||||
b[26] = 1; /* planes */
|
||||
b[28] = (unsigned char)(bpp & 0xFF);
|
||||
return off;
|
||||
}
|
||||
|
||||
/* 正例对照:合法的 64×64 32bpp BMP(topdown=1 时 h 写成负数) */
|
||||
static size_t bmp_build_64(unsigned char *b, int topdown)
|
||||
{
|
||||
const unsigned w = 64, h = 64, bpp = 32;
|
||||
size_t n = bmp_build(b, w, topdown ? (unsigned)(-64) : h, bpp);
|
||||
unsigned x, y;
|
||||
b[2] = (unsigned char)((n + w * h * 4) & 0xFF); /* 文件大小(解析器不校验) */
|
||||
b[3] = (unsigned char)(((n + w * h * 4) >> 8) & 0xFF);
|
||||
for (y = 0; y < h; y++)
|
||||
for (x = 0; x < w; x++, n += 4) {
|
||||
b[n + 0] = (unsigned char)(x * 4);
|
||||
b[n + 1] = (unsigned char)(y * 4);
|
||||
b[n + 2] = 0x40;
|
||||
b[n + 3] = 0xFF;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t png_ihdr_only(unsigned char *b, unsigned w, unsigned h)
|
||||
{
|
||||
static const unsigned char sig[8] = { 0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n' };
|
||||
size_t p = 0;
|
||||
memcpy(b, sig, 8);
|
||||
p = 8;
|
||||
b[p++] = 0; b[p++] = 0; b[p++] = 0; b[p++] = 13; /* IHDR 长度 */
|
||||
memcpy(b + p, "IHDR", 4); p += 4;
|
||||
b[p++] = (unsigned char)(w >> 24); b[p++] = (unsigned char)((w >> 16) & 0xFF);
|
||||
b[p++] = (unsigned char)((w >> 8) & 0xFF); b[p++] = (unsigned char)(w & 0xFF);
|
||||
b[p++] = (unsigned char)(h >> 24); b[p++] = (unsigned char)((h >> 16) & 0xFF);
|
||||
b[p++] = (unsigned char)((h >> 8) & 0xFF); b[p++] = (unsigned char)(h & 0xFF);
|
||||
b[p++] = 8; /* bit depth */
|
||||
b[p++] = 2; /* color type RGB */
|
||||
b[p++] = 0; b[p++] = 0; b[p++] = 0;
|
||||
return p; /* 故意不带后续块 */
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@@ -1117,6 +1185,95 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- S17 32 位尺寸算术(ILP32 崩溃回归,M26 r1 P1) ----
|
||||
* 54 字节 BMP 头声明 w=2^27、bpp=32 时,ILP32 上 w*bpp = 2^32 → rowsize 回绕成 0 →
|
||||
* malloc(0) → 采样索引 row + sx*4 越界读 → SIGSEGV。LP64 宿主不会回绕,所以本节在
|
||||
* 宿主的价值是「契约固定」:这些声明尺寸必须一律返回 NULL(且不崩);真正的 32 位
|
||||
* 覆盖用法见文件头「ILP32 回归」那条交叉编译命令。 */
|
||||
printf("[S17] 32 位尺寸算术:BMP 声明尺寸 / 采样偏移溢出(ILP32 崩溃回归)\n");
|
||||
{
|
||||
static const struct { const char *tag; unsigned w, hbits, bpp; int must_null; } sc[] = {
|
||||
{ "w=0x07FFFFFF bpp=32(回绕点前一点)", 0x07FFFFFFu, 1u, 32u, 1 },
|
||||
{ "w=0x08000000 bpp=32(2^27,旧代码崩点)", 0x08000000u, 1u, 32u, 1 },
|
||||
{ "w=0x10000000 bpp=32", 0x10000000u, 1u, 32u, 1 },
|
||||
{ "w=0x40000000 bpp=32", 0x40000000u, 1u, 32u, 1 },
|
||||
{ "w=0x0AAAAAAA bpp=24(w*24 回绕点前)", 0x0AAAAAAAu, 1u, 24u, 1 },
|
||||
{ "w=0x0AAAAAAB bpp=24(w*24 ≥ 2^32)", 0x0AAAAAABu, 1u, 24u, 1 },
|
||||
/* 合法尺寸(≤1<<20)但无像素数据:后端必须拒绝;公开入口按设计回退 stb,
|
||||
* stb 对截断 BMP 的行为不受本单约束,故只断言「不崩」,不要求 NULL。 */
|
||||
{ "w=1000000 bpp=32(合法尺寸但无像素数据)", 1000000u, 1u, 32u, 0 },
|
||||
{ "w=0x00100000 bpp=8(单边上限 1<<20)", 0x00100000u, 1u, 8u, 0 },
|
||||
{ "w=0x00100001 bpp=8(超单边上限)", 0x00100001u, 1u, 8u, 1 },
|
||||
{ "h=0x80000000(-INT_MIN 溢出点)", 0x01000000u, 0x80000000u, 32u, 1 },
|
||||
};
|
||||
for (size_t i = 0; i < sizeof sc / sizeof sc[0]; i++) {
|
||||
unsigned char buf[54];
|
||||
int tw = 0, th = 0;
|
||||
unsigned char *px = NULL;
|
||||
meow_img *im;
|
||||
char name[200];
|
||||
size_t base, pk;
|
||||
char *fp = path_join("evil_dims.bmp");
|
||||
FILE *f;
|
||||
|
||||
bmp_build(buf, sc[i].w, sc[i].hbits, sc[i].bpp);
|
||||
snprintf(name, sizeof name, "%s → 后端拒绝", sc[i].tag);
|
||||
base = peak_reset();
|
||||
CHECK(meow_bmp_decode_stream(NULL, buf, sizeof buf, 720, &tw, &th, &px) == 0, name);
|
||||
pk = peak_since(base);
|
||||
CHECK(px == NULL, "拒绝时不返回像素缓冲");
|
||||
if (sc[i].must_null) CHECK(pk < PEAK_2500KB, "越界声明在算术前就被拒(不分配)");
|
||||
|
||||
f = fopen(fp, "wb");
|
||||
CHECK(f != NULL, "写畸形 BMP 到夹具目录");
|
||||
if (f) { fwrite(buf, 1, sizeof buf, f); fclose(f); }
|
||||
|
||||
im = meow_img_load_file_dim(fp, 720);
|
||||
snprintf(name, sizeof name, "%s → _dim 不崩%s", sc[i].tag,
|
||||
sc[i].must_null ? "且返回 NULL" : "");
|
||||
CHECK(im == NULL || !sc[i].must_null, name);
|
||||
meow_img_free(im);
|
||||
|
||||
im = meow_img_load_mem_dim(buf, (int)sizeof buf, 720);
|
||||
snprintf(name, sizeof name, "%s → _mem_dim 不崩%s", sc[i].tag,
|
||||
sc[i].must_null ? "且返回 NULL" : "");
|
||||
CHECK(im == NULL || !sc[i].must_null, name);
|
||||
meow_img_free(im);
|
||||
free(fp);
|
||||
}
|
||||
|
||||
/* 正例对照:合法 64×64 32bpp 必须仍能解(含 h<0 的正上方行序) */
|
||||
{
|
||||
unsigned char *big = (unsigned char *)malloc(54 + 64 * 64 * 4);
|
||||
int tw = 0, th = 0;
|
||||
unsigned char *px = NULL;
|
||||
size_t n;
|
||||
CHECK(big != NULL, "分配正例 BMP 缓冲");
|
||||
if (big) {
|
||||
n = bmp_build_64(big, 0);
|
||||
CHECK(meow_bmp_decode_stream(NULL, big, n, 32, &tw, &th, &px) == 1 &&
|
||||
tw == 32 && th == 32, "正例:64x64 32bpp 正序 → 32x32");
|
||||
free(px); px = NULL; tw = th = 0;
|
||||
n = bmp_build_64(big, 1);
|
||||
CHECK(meow_bmp_decode_stream(NULL, big, n, 32, &tw, &th, &px) == 1 &&
|
||||
tw == 32 && th == 32, "正例:64x64 32bpp 倒序(h<0)→ 32x32");
|
||||
free(px);
|
||||
free(big);
|
||||
}
|
||||
}
|
||||
|
||||
/* PNG:tw*th*4 在 ILP32 上回绕成 0 的坐标点(65535² 头 + maxdim=32768) */
|
||||
{
|
||||
unsigned char pb[64];
|
||||
int tw = 0, th = 0;
|
||||
unsigned char *px = NULL;
|
||||
size_t pn = png_ihdr_only(pb, 65535, 65535);
|
||||
CHECK(meow_png_decode_stream(NULL, pb, pn, 32768, &tw, &th, &px) == 0,
|
||||
"PNG 65535² maxdim=32768 → 拒绝(目标图字节数会回绕)");
|
||||
CHECK(px == NULL, "PNG 拒绝时不返回像素缓冲");
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- S8 内存入口 ---- */
|
||||
printf("[S8] 内存入口 meow_img_load_mem_dim\n");
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user