diff --git a/patch_v2/ncm_app/core/ncm_tag.c b/patch_v2/ncm_app/core/ncm_tag.c index a439839..f79f56e 100644 --- a/patch_v2/ncm_app/core/ncm_tag.c +++ b/patch_v2/ncm_app/core/ncm_tag.c @@ -561,9 +561,10 @@ int ncm_tag_embed_file(const char *path, const char *ext, const ncm_tag_meta_t * { char tmp[NCM_TAG_PATH_MAX]; struct stat st; - unsigned char magic[4]; + unsigned char magic[12]; unsigned char *buf; int in, out, rc, is_flac, n; + ssize_t mgot; if (!path || !path[0] || !m) return NCM_ERR_ARG; if (!m->title[0] && !m->artist[0] && !m->album[0]) return NCM_OK; /* 无元数据:不动文件 */ @@ -579,12 +580,15 @@ int ncm_tag_embed_file(const char *path, const char *ext, const ncm_tag_meta_t * if (in < 0) return NCM_ERR_IO; if (fstat(in, &st) != 0 || st.st_size <= 0) { close(in); return NCM_ERR_IO; } - if (read_full(in, magic, sizeof magic) != (ssize_t)sizeof magic) { close(in); return NCM_ERR_IO; } + mgot = read_full(in, magic, sizeof magic); + if (mgot < 4) { close(in); return NCM_ERR_IO; } if (lseek(in, 0, SEEK_SET) < 0) { close(in); return NCM_ERR_IO; } is_flac = (memcmp(magic, "fLaC", 4) == 0); /* 声明 flac 却无 fLaC 魔数 = 残体/损坏:拒绝写(调用方 commit 裸流降级)。 - * 声明 mp3 而内容实为 FLAC:按容器写 VORBIS_COMMENT(真机降级产物同理)。 */ + * 声明 mp3 而内容实为 FLAC:按容器写 VORBIS_COMMENT(真机降级产物同理)。 + * MP4/M4A(第 4..7 字节 "ftyp"):ID3v2 头会破坏 MP4 容器 → 拒绝(裸流降级)。 */ if (!is_flac && ext && strcasecmp(ext, "flac") == 0) { close(in); return NCM_ERR_ARG; } + if (!is_flac && mgot >= 8 && memcmp(magic + 4, "ftyp", 4) == 0) { close(in); return NCM_ERR_ARG; } buf = (unsigned char *)malloc(NCM_TAG_CHUNK); if (!buf) { close(in); return NCM_ERR_OOM; } diff --git a/patch_v2/ncm_app/test/test_ncm_tag.c b/patch_v2/ncm_app/test/test_ncm_tag.c index 81df582..a7f1403 100644 --- a/patch_v2/ncm_app/test/test_ncm_tag.c +++ b/patch_v2/ncm_app/test/test_ncm_tag.c @@ -347,6 +347,13 @@ static void test_edges(void) snprintf(tmp, sizeof tmp, "%s.tag.tmp", path); CHECK(stat(tmp, &st) != 0, "no .tag.tmp left behind"); } + /* MP4/M4A("....ftyp")不是 mp3:ID3v2 前缀会破坏 MP4 容器 → 必须拒绝(降级裸流) */ + snprintf(path, sizeof path, TMPDIR_PREFIX "/fake.m4a.mp3"); + file_put(path, "\x00\x00\x00\x20""ftypisom\x00\x00\x00\x00", 16); + CHECK(ncm_tag_embed_file(path, "mp3", &m) != 0, "MP4 container rejected (no ID3 prefix)"); + before = file_slurp(path, &blen); + CHECK(blen == 16 && memcmp(before + 4, "ftyp", 4) == 0, "MP4 file unchanged"); + free(before); /* 不存在的文件 */ CHECK(ncm_tag_embed_file(TMPDIR_PREFIX "/nope.mp3", "mp3", &m) != 0, "missing file -> error"); CHECK(ncm_tag_read_file(TMPDIR_PREFIX "/nope.mp3", &m) == NCM_ERR_NOENT,