From 404e88553c1aa9d1832d4897b100303a9322ec93 Mon Sep 17 00:00:00 2001 From: Sidhartha Tiwari Date: Sat, 30 May 2026 22:59:57 +0530 Subject: [PATCH] opj_tcd: fix out-of-bounds read in opj_tcd_get_decoded_tile_size() when the reduce factor equals numresolutions (#1631) This patch addresses an OOB read triggered when minimum_num_resolutions underflows to 0. It fixes the setter opj_j2k_set_decoded_resolution_factor() to validate against all components before mutating decoder state, and updates the boundary check in opj_tcd_init_tile() to use <= to ensure clamping. --- src/lib/openjp2/j2k.c | 8 ++++++-- src/lib/openjp2/tcd.c | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c index ad76aca7d..2fbcd515a 100644 --- a/src/lib/openjp2/j2k.c +++ b/src/lib/openjp2/j2k.c @@ -12553,12 +12553,11 @@ OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k, { OPJ_UINT32 it_comp; - p_j2k->m_cp.m_specific_param.m_dec.m_reduce = res_factor; - if (p_j2k->m_private_image) { if (p_j2k->m_private_image->comps) { if (p_j2k->m_specific_param.m_decoder.m_default_tcp) { if (p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps) { + /* Validate against all components before mutating state. */ for (it_comp = 0 ; it_comp < p_j2k->m_private_image->numcomps; it_comp++) { OPJ_UINT32 max_res = p_j2k->m_specific_param.m_decoder.m_default_tcp->tccps[it_comp].numresolutions; @@ -12567,6 +12566,9 @@ OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k, "Resolution factor is greater than the maximum resolution in the component.\n"); return OPJ_FALSE; } + } + p_j2k->m_cp.m_specific_param.m_dec.m_reduce = res_factor; + for (it_comp = 0 ; it_comp < p_j2k->m_private_image->numcomps; it_comp++) { p_j2k->m_private_image->comps[it_comp].factor = res_factor; } return OPJ_TRUE; @@ -12575,6 +12577,8 @@ OPJ_BOOL opj_j2k_set_decoded_resolution_factor(opj_j2k_t *p_j2k, } } + /* Image not available yet; validated later at COD parsing time. */ + p_j2k->m_cp.m_specific_param.m_dec.m_reduce = res_factor; return OPJ_FALSE; } diff --git a/src/lib/openjp2/tcd.c b/src/lib/openjp2/tcd.c index 5d466fb4d..aa6c25e9c 100644 --- a/src/lib/openjp2/tcd.c +++ b/src/lib/openjp2/tcd.c @@ -877,7 +877,8 @@ static INLINE OPJ_BOOL opj_tcd_init_tile(opj_tcd_t *p_tcd, OPJ_UINT32 p_tile_no, /*fprintf(stderr, "\tTile compo border = %d,%d,%d,%d\n", l_tilec->x0, l_tilec->y0,l_tilec->x1,l_tilec->y1);*/ l_tilec->numresolutions = l_tccp->numresolutions; - if (l_tccp->numresolutions < l_cp->m_specific_param.m_dec.m_reduce) { + /* Use <= to prevent minimum_num_resolutions from underflowing to 0. */ + if (l_tccp->numresolutions <= l_cp->m_specific_param.m_dec.m_reduce) { l_tilec->minimum_num_resolutions = 1; } else { l_tilec->minimum_num_resolutions = l_tccp->numresolutions -