PR #24819 opened by Forgejo_Fairy URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24819 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24819.patch
Dense PIZ Huffman tables can require subtable offsets beyond the generic VLC decoder’s signed 16-bit range. The sample in #24027 consequently decodes to an all-zero frame on master while ffmpeg exits successfully. The decoding regression was verified by building 767d3f7dcf81 and its immediate parent, 8213f0a7f7e6. Adapt JUI YU LIN’s submitted decoder patch to use the OpenEXR primary table and long-code candidate lists, with GetBitContext for bounded input consumption and integer output bounds. Preserve the run symbol at iM and retain the OpenEXR attribution and licence. The decoder and dense-sample test commits preserve the submitter’s authorship. Also collect execute2() worker results and propagate block failures, including an error returned directly by execute2(). This prevents failed scanline or tile jobs from being returned as successful frames. Add the submitted dense-table FATE test and a sample-independent Huffman test covering run expansion, truncated input, output bounds, 58-bit codes at all alignments, the full literal alphabet plus its run symbol, and an oversubscribed table. Expected results are stored in tests/ref/fate. Validation on Linux/AArch64, GCC 13.3.0: - The issue sample matches OpenEXR 3.5.1 byte for byte, with framecrc 0x5c1d402d. Decoder thread counts 1/2/4/8/16 agree. - `make -j8 fate-exr fate-exr-huffman SAMPLES=/opt/fate-suite` passes all 76 tests, including all existing PIZ and DWA cases. - All 76 tests also pass with AddressSanitizer and UndefinedBehaviorSanitizer. - Six generated PIZ files match OpenEXR at native float or half-float depth with one and four decoder threads. - Replacing the sample’s little-endian im field at offset 8558 with 65537 produces no frame and exit status 69; the submitted decoder patch alone still returns a black frame and exit status 0. - Sixty-four deterministic table/payload mutations completed without sanitizer reports, crashes or timeouts. This is limited smoke coverage. The dense FATE test requires the [sample attached to #24027](https://code.ffmpeg.org/attachments/972692ff-a17b-42f9-b469-6315d599b7a2) at `exr/rgb_scanline_float_piz_dense_448x32.exr`. Its SHA-256 is `9331d98568d774faf36c50b80f1963f121631527dd4246b353af4165bb014c69`. It was installed locally for testing; merge should wait until it is available in fate-suite. Sample submission goes to [email protected]. Fixes #24027. From 173f0147376e364278e5769a751b10a83eaaaebf Mon Sep 17 00:00:00 2001 From: JUI YU LIN <[email protected]> Date: Tue, 29 Sep 2026 21:35:27 +0000 Subject: [PATCH 1/4] avcodec/exr: use a dedicated PIZ Huffman decoding table Dense PIZ codebooks can require VLC subtable offsets beyond the signed 16-bit range. Replace the generic VLC table with the OpenEXR primary table and long-code candidate lists, keeping the run symbol at iM. Adapt the submitted decoder to use GetBitContext for both short and long codes. Check the declared bit count before consuming symbols or run lengths, use integer output bounds, and require the expected output length. Retain the OpenEXR attribution and licence. Fixes the dense-table regression introduced by 767d3f7dcf818a88b7fa9c510b8721f247faee3c. Based on the patch submitted by JUI YU LIN in issue #24027. Assisted-by: Fairy --- libavcodec/exr.c | 257 +++++++++++++++++++++++++++++++---------------- 1 file changed, 171 insertions(+), 86 deletions(-) diff --git a/libavcodec/exr.c b/libavcodec/exr.c index 0e9c734a10..82fe8d7680 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -22,6 +22,40 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/* + * huf_build_dec_table() and huf_decode() below are derived from + * hufBuildDecTable() and hufDecode() in src/lib/OpenEXR/ImfHuf.cpp of the + * OpenEXR reference implementation, distributed under the following terms: + * + * Copyright (c) Contributors to the OpenEXR Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + /** * @file * OpenEXR decoder @@ -92,11 +126,11 @@ enum ExrTileLevelRound { EXR_TILE_ROUND_UNKNOWN, }; -typedef struct HuffEntry { - uint8_t len; - uint16_t sym; - uint32_t code; -} HuffEntry; +typedef struct HufDec { + int len; /* short code: code length; long code: 0 */ + int lit; /* short code: literal; long code: number of candidates */ + int *p; /* long code: candidate symbol indices (short code: NULL) */ +} HufDec; typedef struct EXRChannel { int xsub, ysub; @@ -138,10 +172,8 @@ typedef struct EXRThreadData { int channel_line_size; - int run_sym; - HuffEntry *he; uint64_t *freq; - VLC vlc; + HufDec *hdec; /* Huffman decode table (PIZ), HUF_DECSIZE entries */ } EXRThreadData; typedef struct EXRContext { @@ -299,6 +331,8 @@ static void apply_lut(const uint16_t *lut, uint16_t *dst, int dsize) #define HUF_ENCBITS 16 // literal (value) bit length #define HUF_ENCSIZE ((1 << HUF_ENCBITS) + 1) // encoding table size +#define HUF_DECBITS 14 // decoding table size exponent (>= 8), as in OpenEXR +#define HUF_DECSIZE (1 << HUF_DECBITS) static void huf_canonical_code_table(uint64_t *freq) { @@ -371,83 +405,133 @@ static int huf_unpack_enc_table(GetByteContext *gb, return 0; } -static int huf_build_dec_table(const EXRContext *s, - EXRThreadData *td, int im, int iM) +static int huf_build_dec_table(EXRThreadData *td, int im, int iM) { - int j = 0; + HufDec *hdec; + int i, ret = 0; - td->run_sym = -1; - for (int i = im; i < iM; i++) { - td->he[j].sym = i; - td->he[j].len = td->freq[i] & 63; - td->he[j].code = td->freq[i] >> 6; - if (td->he[j].len > 32) { - avpriv_request_sample(s->avctx, "Too big code length"); - return AVERROR_PATCHWELCOME; + if (!td->hdec) { + td->hdec = av_calloc(HUF_DECSIZE, sizeof(*td->hdec)); + if (!td->hdec) + return AVERROR(ENOMEM); + } + hdec = td->hdec; + + for (i = 0; i < HUF_DECSIZE; i++) { + av_freep(&hdec[i].p); + hdec[i].len = 0; + hdec[i].lit = 0; + } + + for (i = im; i <= iM; i++) { + uint64_t c = td->freq[i] >> 6; + int len = td->freq[i] & 63; + + if (c >> len) { + /* code wider than its length */ + ret = AVERROR_INVALIDDATA; + goto fail; } - if (td->he[j].len > 0) - j++; - else - td->run_sym = i; - } - if (im > 0) - td->run_sym = 0; - else if (iM < 65535) - td->run_sym = 65535; + if (len > HUF_DECBITS) { + /* long code: add a candidate to the secondary entry */ + HufDec *pl = &hdec[c >> (len - HUF_DECBITS)]; + int *p; - if (td->run_sym == -1) { - avpriv_request_sample(s->avctx, "No place for run symbol"); - return AVERROR_PATCHWELCOME; - } + if (pl->len) { + ret = AVERROR_INVALIDDATA; + goto fail; + } - td->he[j].sym = td->run_sym; - td->he[j].len = td->freq[iM] & 63; - if (td->he[j].len > 32) { - avpriv_request_sample(s->avctx, "Too big code length"); - return AVERROR_PATCHWELCOME; - } - td->he[j].code = td->freq[iM] >> 6; - j++; + p = av_realloc_array(pl->p, pl->lit + 1, sizeof(*pl->p)); + if (!p) { + ret = AVERROR(ENOMEM); + goto fail; + } + pl->p = p; + pl->p[pl->lit++] = i; + } else if (len) { + /* short code: fill the primary entries it covers */ + HufDec *pl = &hdec[c << (HUF_DECBITS - len)]; - ff_vlc_free(&td->vlc); - return ff_vlc_init_sparse(&td->vlc, 12, j, - &td->he[0].len, sizeof(td->he[0]), sizeof(td->he[0].len), - &td->he[0].code, sizeof(td->he[0]), sizeof(td->he[0].code), - &td->he[0].sym, sizeof(td->he[0]), sizeof(td->he[0].sym), 0); -} - -static int huf_decode(VLC *vlc, GetByteContext *gb, int nbits, int run_sym, - int no, uint16_t *out) -{ - GetBitContext gbit; - int oe = 0; - - init_get_bits(&gbit, gb->buffer, nbits); - while (get_bits_left(&gbit) > 0 && oe < no) { - uint16_t x = get_vlc2(&gbit, vlc->table, 12, 3); - - if (x == run_sym) { - int run = get_bits(&gbit, 8); - uint16_t fill; - - if (oe == 0 || oe + run > no) - return AVERROR_INVALIDDATA; - - fill = out[oe - 1]; - - while (run-- > 0) - out[oe++] = fill; - } else { - out[oe++] = x; + for (uint64_t n = 1 << (HUF_DECBITS - len); n > 0; n--, pl++) { + if (pl->len || pl->p) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + pl->len = len; + pl->lit = i; + } } } return 0; +fail: + for (i = 0; i < HUF_DECSIZE; i++) + av_freep(&hdec[i].p); + return ret; } -static int huf_uncompress(const EXRContext *s, - EXRThreadData *td, +static int huf_decode(EXRThreadData *td, const uint8_t *in, int nbits, + int run_sym, int no, uint16_t *out) +{ + GetBitContext gb; + int written = 0; + int ret = init_get_bits(&gb, in, nbits); + + if (ret < 0) + return ret; + + while (get_bits_left(&gb) > 0) { + const HufDec *entry = &td->hdec[show_bits(&gb, HUF_DECBITS)]; + int symbol; + + if (entry->len) { + if (entry->len > get_bits_left(&gb)) + return AVERROR_INVALIDDATA; + skip_bits(&gb, entry->len); + symbol = entry->lit; + } else { + int j; + + for (j = 0; j < entry->lit; j++) { + GetBitContext peek = gb; + int len = td->freq[entry->p[j]] & 63; + + if (len <= get_bits_left(&gb) && + get_bits64(&peek, len) == td->freq[entry->p[j]] >> 6) { + gb = peek; + break; + } + } + if (j == entry->lit) + return AVERROR_INVALIDDATA; + symbol = entry->p[j]; + } + + if (symbol == run_sym) { + int run; + uint16_t fill; + + if (!written || get_bits_left(&gb) < 8) + return AVERROR_INVALIDDATA; + run = get_bits(&gb, 8); + if (run > no - written) + return AVERROR_INVALIDDATA; + fill = out[written - 1]; + while (run--) + out[written++] = fill; + } else { + if (written >= no) + return AVERROR_INVALIDDATA; + out[written++] = symbol; + } + } + + return written == no ? 0 : AVERROR_INVALIDDATA; +} + +static int huf_uncompress(EXRThreadData *td, GetByteContext *gb, uint16_t *dst, int dst_size) { @@ -460,32 +544,30 @@ static int huf_uncompress(const EXRContext *s, bytestream2_skip(gb, 4); nBits = bytestream2_get_le32(gb); if (im < 0 || im >= HUF_ENCSIZE || - iM < 0 || iM >= HUF_ENCSIZE) + iM < im || iM >= HUF_ENCSIZE) return AVERROR_INVALIDDATA; bytestream2_skip(gb, 4); - if (!td->freq) + if (!td->freq) { td->freq = av_malloc_array(HUF_ENCSIZE, sizeof(*td->freq)); - if (!td->he) - td->he = av_calloc(HUF_ENCSIZE, sizeof(*td->he)); - if (!td->freq || !td->he) { - ret = AVERROR(ENOMEM); - return ret; + if (!td->freq) + return AVERROR(ENOMEM); } memset(td->freq, 0, sizeof(*td->freq) * HUF_ENCSIZE); if ((ret = huf_unpack_enc_table(gb, im, iM, td->freq)) < 0) return ret; - if (nBits > 8 * bytestream2_get_bytes_left(gb)) { + if (nBits > 8ULL * bytestream2_get_bytes_left(gb)) { ret = AVERROR_INVALIDDATA; return ret; } - if ((ret = huf_build_dec_table(s, td, im, iM)) < 0) + /* run-length code is the pseudo-symbol at index iM, as in OpenEXR */ + if ((ret = huf_build_dec_table(td, im, iM)) < 0) return ret; - return huf_decode(&td->vlc, gb, nBits, td->run_sym, dst_size, dst); + return huf_decode(td, gb->buffer, nBits, iM, dst_size, dst); } static inline void wdec14(uint16_t l, uint16_t h, uint16_t *a, uint16_t *b) @@ -637,7 +719,7 @@ static int piz_uncompress(const EXRContext *s, const uint8_t *src, int ssize, maxval = reverse_lut(td->bitmap, td->lut); bytestream2_skip(&gb, 4); - ret = huf_uncompress(s, td, &gb, tmp, dsize / sizeof(uint16_t)); + ret = huf_uncompress(td, &gb, tmp, dsize / sizeof(uint16_t)); if (ret) return ret; @@ -1068,7 +1150,7 @@ static int dwa_uncompress(const EXRContext *s, const uint8_t *src, int compresse switch (ac_compression) { case 0: - ret = huf_uncompress(s, td, &agb, (int16_t *)td->ac_data, ac_count); + ret = huf_uncompress(td, &agb, (int16_t *)td->ac_data, ac_count); if (ret < 0) return ret; break; @@ -2331,13 +2413,16 @@ static av_cold int decode_end(AVCodecContext *avctx) av_freep(&td->tmp); av_freep(&td->bitmap); av_freep(&td->lut); - av_freep(&td->he); av_freep(&td->freq); av_freep(&td->ac_data); av_freep(&td->dc_data); av_freep(&td->rle_data); av_freep(&td->rle_raw_data); - ff_vlc_free(&td->vlc); + if (td->hdec) { + for (int j = 0; j < HUF_DECSIZE; j++) + av_freep(&td->hdec[j].p); + av_freep(&td->hdec); + } } av_freep(&s->thread_data); -- 2.52.0 From 25225f34df66efab97a4d2104d91d1e5b34fbe3d Mon Sep 17 00:00:00 2001 From: JUI YU LIN <[email protected]> Date: Tue, 29 Sep 2026 21:35:27 +0000 Subject: [PATCH 2/4] tests/fate/image: cover dense PIZ Huffman tables The 448x32 float-noise sample requires more VLC subtable entries than the old decoder could address. Compare its decoded frame checksum. Sample supplied in issue #24027: exr/rgb_scanline_float_piz_dense_448x32.exr SHA-256: 9331d98568d774faf36c50b80f1963f121631527dd4246b353af4165bb014c69 Assisted-by: Fairy --- tests/fate/image.mak | 5 +++++ tests/ref/fate/exr-rgb-scanline-float-piz-dense | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/ref/fate/exr-rgb-scanline-float-piz-dense diff --git a/tests/fate/image.mak b/tests/fate/image.mak index b855588fb5..4487a8b9a9 100644 --- a/tests/fate/image.mak +++ b/tests/fate/image.mak @@ -274,6 +274,11 @@ fate-exr-rgb-scanline-half-piz-dw-t01: CMD = framecrc -i $(TARGET_SAMPLES)/exr/r FATE_EXR += fate-exr-rgb-scanline-float-piz-48x32 fate-exr-rgb-scanline-float-piz-48x32: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_float_piz_48x32.exr -vf scale -pix_fmt gbrpf32le +# A dense Huffman table (many codes longer than 14 bits), which the previous +# VLC-based decoder could not build. +FATE_EXR += fate-exr-rgb-scanline-float-piz-dense +fate-exr-rgb-scanline-float-piz-dense: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_float_piz_dense_448x32.exr -vf scale -pix_fmt gbrpf32le + FATE_EXR += fate-exr-rgb-scanline-none-negative-red fate-exr-rgb-scanline-none-negative-red: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_none_negative_red.exr -vf scale -pix_fmt gbrpf32le diff --git a/tests/ref/fate/exr-rgb-scanline-float-piz-dense b/tests/ref/fate/exr-rgb-scanline-float-piz-dense new file mode 100644 index 0000000000..c63ddb93a2 --- /dev/null +++ b/tests/ref/fate/exr-rgb-scanline-float-piz-dense @@ -0,0 +1,6 @@ +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 448x32 +#sar 0: 1/1 +0, 0, 0, 1, 172032, 0x5c1d402d -- 2.52.0 From 634bed660ae5e0d5ccc2257aec8acae26a5a9381 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 29 Sep 2026 21:37:48 +0000 Subject: [PATCH 3/4] avcodec/exr: propagate scanline and tile decoding errors Collect execute2() job results and return the first block error instead of returning a successful frame with undecoded regions. Dense PIZ tables exposed this in issue #24027; malformed blocks in other compression modes were also silently accepted. With the attached sample's Huffman im changed to 65537, decoding now returns AVERROR_INVALIDDATA, writes no frame, and the single-file ffmpeg command exits with status 69 instead of 0. All 75 EXR FATE tests pass. Assisted-by: Fairy --- libavcodec/exr.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libavcodec/exr.c b/libavcodec/exr.c index 82fe8d7680..496a7647a7 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -2173,6 +2173,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, int planes; int out_line_size; int nb_blocks; /* nb scanline or nb tile */ + int *block_errors; uint64_t start_offset_table; uint64_t start_next_scanline; @@ -2361,7 +2362,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, s->picture = picture; - avctx->execute2(avctx, decode_block, s->thread_data, NULL, nb_blocks); + block_errors = av_malloc_array(nb_blocks, sizeof(*block_errors)); + if (!block_errors) + return AVERROR(ENOMEM); + + ret = avctx->execute2(avctx, decode_block, s->thread_data, block_errors, nb_blocks); + for (i = 0; ret >= 0 && i < nb_blocks; i++) + ret = block_errors[i]; + av_free(block_errors); + if (ret < 0) + return ret; ymax = FFMAX(0, s->ymax + 1); // Zero out the end if ymax+1 is not h -- 2.52.0 From 790a464176726267a7f76479bea1688bf081572b Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Tue, 29 Sep 2026 21:38:28 +0000 Subject: [PATCH 4/4] tests/fate: exercise EXR Huffman bounds and long codes Cover run-length expansion, truncated input, output bounds, 58-bit codes at all bit alignments, all 65536 literals plus the run symbol, and an oversubscribed table. Print actual decoding results for FATE comparison. Assisted-by: Fairy --- libavcodec/Makefile | 1 + libavcodec/tests/.gitignore | 1 + libavcodec/tests/exr.c | 112 ++++++++++++++++++++++++++++++++++++ tests/fate/libavcodec.mak | 4 ++ tests/ref/fate/exr-huffman | 16 ++++++ 5 files changed, 134 insertions(+) create mode 100644 libavcodec/tests/exr.c create mode 100644 tests/ref/fate/exr-huffman diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 7785638c77..9a35ea1f9c 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1386,6 +1386,7 @@ TESTPROGS = avcodec \ jpeg2000dwt \ mathops \ +TESTPROGS-$(CONFIG_EXR_DECODER) += exr TESTPROGS-$(CONFIG_AC3DSP) += ac3 TESTPROGS-$(CONFIG_APV_DECODER) += apv TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER) += av1_levels diff --git a/libavcodec/tests/.gitignore b/libavcodec/tests/.gitignore index 8e4f26c2ad..74035569e1 100644 --- a/libavcodec/tests/.gitignore +++ b/libavcodec/tests/.gitignore @@ -9,6 +9,7 @@ /celp_math /codec_desc /dct +/exr /golomb /hashtable /h264_levels diff --git a/libavcodec/tests/exr.c b/libavcodec/tests/exr.c new file mode 100644 index 0000000000..4cb8c10d49 --- /dev/null +++ b/libavcodec/tests/exr.c @@ -0,0 +1,112 @@ +/* + * EXR Huffman decoder tests + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> + +#include "libavcodec/exr.c" +#include "libavcodec/put_bits.h" + +static void decode_test(EXRThreadData *td, const char *name, + const uint8_t *data, int bits, int run_symbol, int count) +{ + uint16_t out[16] = { 0 }; + int ret = huf_decode(td, data, bits, run_symbol, count, out); + + printf("%s: %s", name, ret < 0 ? av_err2str(ret) : "ok"); + if (ret >= 0) + for (int i = 0; i < count; i++) + printf(" %u", out[i]); + printf("\n"); +} + +static int build_table(EXRThreadData *td, int im, int iM) +{ + huf_canonical_code_table(td->freq); + return huf_build_dec_table(td, im, iM); +} + +int main(void) +{ + EXRThreadData td = { 0 }; + uint8_t data[16 + AV_INPUT_BUFFER_PADDING_SIZE] = { 0 }; + PutBitContext pb; + int ret = 1; + + td.freq = av_calloc(HUF_ENCSIZE, sizeof(*td.freq)); + if (!td.freq) + goto end; + + td.freq[42] = td.freq[43] = 1; + if (build_table(&td, 42, 43) < 0) + goto end; + init_put_bits(&pb, data, 16); + put_bits(&pb, 1, 0); + put_bits(&pb, 1, 1); + put_bits(&pb, 8, 2); + flush_put_bits(&pb); + decode_test(&td, "run", data, 10, 43, 3); + decode_test(&td, "truncated run", data, 9, 43, 3); + decode_test(&td, "output overrun", data, 10, 43, 2); + decode_test(&td, "output underfill", data, 1, 43, 2); + data[0] = 0x80; + decode_test(&td, "run without literal", data, 9, 43, 3); + + /* A complete codebook with code lengths from 1 through 58. */ + memset(td.freq, 0, HUF_ENCSIZE * sizeof(*td.freq)); + for (int i = 0; i < 58; i++) + td.freq[i] = i + 1; + td.freq[58] = 58; + if (build_table(&td, 0, 58) < 0) + goto end; + for (int prefix = 0; prefix < 8; prefix++) { + char name[32]; + + memset(data, 0, sizeof(data)); + data[0] = (uint8_t)(0xff << (8 - prefix)); + snprintf(name, sizeof(name), "58-bit code, prefix %d", prefix); + decode_test(&td, name, data, 58 + prefix, 58, prefix + 1); + } + memset(data, 0, sizeof(data)); + decode_test(&td, "truncated long code", data, 57, 58, 1); + + /* Every 16-bit literal is present; the run symbol needs 17 bits. */ + for (int i = 0; i < HUF_ENCSIZE; i++) + td.freq[i] = i < 65535 ? 16 : 17; + if (build_table(&td, 0, 65536) < 0) + goto end; + init_put_bits(&pb, data, 16); + put_bits(&pb, 17, 0); + put_bits(&pb, 17, 1); + put_bits(&pb, 8, 2); + flush_put_bits(&pb); + decode_test(&td, "full alphabet", data, 42, 65536, 3); + + memset(td.freq, 0, HUF_ENCSIZE * sizeof(*td.freq)); + td.freq[0] = td.freq[1] = td.freq[2] = 1; + printf("oversubscribed table: %s\n", av_err2str(build_table(&td, 0, 2))); + ret = 0; +end: + if (td.hdec) + for (int i = 0; i < HUF_DECSIZE; i++) + av_freep(&td.hdec[i].p); + av_free(td.hdec); + av_free(td.freq); + return ret; +} diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak index f6858c4921..f304ec24ea 100644 --- a/tests/fate/libavcodec.mak +++ b/tests/fate/libavcodec.mak @@ -42,6 +42,10 @@ fate-codec_desc: libavcodec/tests/codec_desc$(EXESUF) fate-codec_desc: CMD = run libavcodec/tests/codec_desc$(EXESUF) fate-codec_desc: CMP = null +FATE_LIBAVCODEC-$(CONFIG_EXR_DECODER) += fate-exr-huffman +fate-exr-huffman: libavcodec/tests/exr$(EXESUF) +fate-exr-huffman: CMD = run libavcodec/tests/exr$(EXESUF) + FATE_LIBAVCODEC-$(CONFIG_GOLOMB) += fate-golomb fate-golomb: libavcodec/tests/golomb$(EXESUF) fate-golomb: CMD = run libavcodec/tests/golomb$(EXESUF) diff --git a/tests/ref/fate/exr-huffman b/tests/ref/fate/exr-huffman new file mode 100644 index 0000000000..8d8d18a044 --- /dev/null +++ b/tests/ref/fate/exr-huffman @@ -0,0 +1,16 @@ +run: ok 42 42 42 +truncated run: Invalid data found when processing input +output overrun: Invalid data found when processing input +output underfill: Invalid data found when processing input +run without literal: Invalid data found when processing input +58-bit code, prefix 0: ok 57 +58-bit code, prefix 1: ok 0 57 +58-bit code, prefix 2: ok 0 0 57 +58-bit code, prefix 3: ok 0 0 0 57 +58-bit code, prefix 4: ok 0 0 0 0 57 +58-bit code, prefix 5: ok 0 0 0 0 0 57 +58-bit code, prefix 6: ok 0 0 0 0 0 0 57 +58-bit code, prefix 7: ok 0 0 0 0 0 0 0 57 +truncated long code: Invalid data found when processing input +full alphabet: ok 65535 65535 65535 +oversubscribed table: Invalid data found when processing input -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
