From 21611f681419aa941c3ae7f53fdf29825af147f1 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Mon, 6 Jul 2026 16:14:52 +0900 Subject: [PATCH 01/58] protection patch Signed-off-by: kp5.choi@samsung.com --- app/oapv_app_dec.c | 7 +++---- inc/oapv.h | 1 + src/oapv.c | 34 ++++++++++++++++++++++++++-------- src/oapv_bs.c | 22 +++++++++++----------- src/oapv_bs.h | 6 +++++- src/oapv_metadata.c | 5 ++++- src/oapv_util.h | 29 +++++++++++++++++++++++++++++ src/oapv_vlc.c | 39 +++++++++++++++++++++++++++++++++------ 8 files changed, 112 insertions(+), 31 deletions(-) diff --git a/app/oapv_app_dec.c b/app/oapv_app_dec.c index 2a40904..044390c 100644 --- a/app/oapv_app_dec.c +++ b/app/oapv_app_dec.c @@ -34,8 +34,7 @@ #include "oapv_app_args.h" #include "oapv_app_y4m.h" -#define MAX_BS_BUF 128 * 1024 * 1024 /* byte */ -#define MAX_NUM_METADATA_PLDS 128 /* max number of metadata payloads per access unit */ +#define MAX_BS_BUF 128 * 1024 * 1024 /* byte */ // check generic frame or not #define IS_NON_AUX_FRM(frm) (((frm)->pbu_type == OAPV_PBU_TYPE_PRIMARY_FRAME) || ((frm)->pbu_type == OAPV_PBU_TYPE_NON_PRIMARY_FRAME)) @@ -599,8 +598,8 @@ int dec_api_set_0(args_var_t *args_var, FILE *fp_bs, int is_y4m) } /* validate number of metadata payloads */ - if(num_plds < 0 || num_plds > MAX_NUM_METADATA_PLDS) { - logerr("ERR: invalid number of metadata payloads (%d), valid range is 0-%d\n", num_plds, MAX_NUM_METADATA_PLDS); + if(num_plds < 0 || num_plds > OAPV_MAX_NUM_META_PAYLOADS) { + logerr("ERR: invalid number of metadata payloads (%d), valid range is 0-%d\n", num_plds, OAPV_MAX_NUM_META_PAYLOADS); goto END; } diff --git a/inc/oapv.h b/inc/oapv.h index 5388a02..4b71222 100644 --- a/inc/oapv.h +++ b/inc/oapv.h @@ -376,6 +376,7 @@ struct oapv_frm { #define OAPV_MAX_NUM_FRAMES (16) // max number of frames in an access unit #define OAPV_MAX_NUM_METAS (16) // max number of metadata in an access unit +#define OAPV_MAX_NUM_META_PAYLOADS (128) // max number of metadata payloads per access unit typedef struct oapv_frms oapv_frms_t; struct oapv_frms { diff --git a/src/oapv.c b/src/oapv.c index b15ca9e..6d25ad9 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1100,6 +1100,12 @@ oapve_t oapve_create(oapve_cdesc_t *cdesc, int *err) int ret; DUMP_CREATE(1); + + if(!((cdesc->threads > 0 && cdesc->threads <= OAPV_MAX_THREADS) || cdesc->threads == OAPV_CDESC_THREADS_AUTO)) { + if(err) *err = OAPV_ERR_INVALID_ARGUMENT; + return NULL; + } + /* memory allocation for ctx and core structure */ ctx = (oapve_ctx_t *)enc_ctx_alloc(); if(ctx != NULL) { @@ -1407,7 +1413,7 @@ static int dec_set_tile_info(oapvd_tile_t* tile, int w_pel, int h_pel, int tile_ static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_tile_info_t * part, oapv_imgb_t *imgb) { - int i; + int i, ret; // the input image buffer must match the frame format signaled in the // bitstream; a mismatch (e.g. caused by a resolution change without @@ -1490,9 +1496,10 @@ static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_tile_info_t * part, oapv_imgb_ ctx->num_tile_cols = (ctx->w + (tile_w - 1)) / tile_w; ctx->num_tile_rows = (ctx->h + (tile_h - 1)) / tile_h; - ctx->num_tiles = ctx->num_tile_cols * ctx->num_tile_rows; - oapv_assert_rv((ctx->num_tile_cols <= OAPV_MAX_TILE_COLS) && (ctx->num_tile_rows <= OAPV_MAX_TILE_ROWS), OAPV_ERR_MALFORMED_BITSTREAM); + ret = oapv_validate_tile_topology(ctx->num_tile_cols, ctx->num_tile_rows, &ctx->num_tiles); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); + dec_set_tile_info(ctx->tile, ctx->w, ctx->h, tile_w, tile_h, ctx->num_tile_cols, ctx->num_tiles); for(i = 0; i < ctx->num_tiles; i++) { @@ -1505,7 +1512,9 @@ static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_tile_info_t * part, oapv_imgb_ ctx->tile[i].stat = DEC_TILE_STAT_DO(DEC_TILE_STAT_SKIP); /* bypass decoding */ } for(i = 0; i < part->num_tiles; i++) { - ctx->tile[part->pos_tiles[i].idx].stat = DEC_TILE_STAT_DO(DEC_TILE_STAT_DECODE); + int idx = part->pos_tiles[i].idx; + oapv_assert_rv(idx >= 0 && idx < ctx->num_tiles, OAPV_ERR_MALFORMED_BITSTREAM); + ctx->tile[idx].stat = DEC_TILE_STAT_DO(DEC_TILE_STAT_DECODE); } } else { @@ -1816,8 +1825,10 @@ oapvd_t oapvd_create(oapvd_cdesc_t *cdesc, int *err) DUMP_CREATE(0); ctx = NULL; - /* check if any decoder argument is correctly set */ - oapv_assert_gv((cdesc->threads > 0 && cdesc->threads <= OAPV_MAX_THREADS) || cdesc->threads == OAPV_CDESC_THREADS_AUTO , ret, OAPV_ERR_INVALID_ARGUMENT, ERR); + if(!((cdesc->threads > 0 && cdesc->threads <= OAPV_MAX_THREADS) || cdesc->threads == OAPV_CDESC_THREADS_AUTO)) { + if(err) *err = OAPV_ERR_INVALID_ARGUMENT; + return NULL; + } /* memory allocation for ctx and core structure */ ctx = (oapvd_ctx_t *)dec_ctx_alloc(); @@ -1866,6 +1877,7 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid oapv_pbuh_t pbuh; int ret = OAPV_OK; u32 pbu_size; + u32 signature; u32 cur_read_size = 0; int nfrms = 0; @@ -1875,7 +1887,8 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid // read signature ('aPv1') oapv_assert_rv(bitb->ssize > 4, OAPV_ERR_MALFORMED_BITSTREAM); - u32 signature = oapv_bsr_read_direct(bitb->addr, 32); + ret = oapv_bsr_read_direct(bitb->addr, 32, &signature); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); oapv_assert_rv(signature == 0x61507631, OAPV_ERR_MALFORMED_BITSTREAM); cur_read_size += 4; stat->read += 4; @@ -2001,6 +2014,7 @@ int oapvd_config(oapvd_t did, int cfg, void *buf, int *size) int oapvd_info(void *au, int au_size, oapv_au_info_t *aui) { int ret, frm_count = 0; + u32 signature; u32 cur_read_size = 0; oapv_bs_t bs; @@ -2008,7 +2022,8 @@ int oapvd_info(void *au, int au_size, oapv_au_info_t *aui) // read signature ('aPv1') oapv_assert_rv(au_size > 4, OAPV_ERR_MALFORMED_BITSTREAM); - u32 signature = oapv_bsr_read_direct(au, 32); + ret = oapv_bsr_read_direct(au, 32, &signature); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); oapv_assert_rv(signature == 0x61507631, OAPV_ERR_MALFORMED_BITSTREAM); cur_read_size += 4; @@ -2111,6 +2126,9 @@ int oapvd_info_frame(void *pbu, int pbu_size, oapv_frm_info_t *frm_info, oapv_ti tile_cols = (pic_w + (tile_w - 1)) / tile_w; tile_rows = (pic_h + (tile_h - 1)) / tile_h; + ret = oapv_validate_tile_topology(tile_cols, tile_rows, NULL); + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + oapv_tile_pos_t * tpos = tile_info->pos_tiles; for(i = 0; i < tile_rows; i++) { diff --git a/src/oapv_bs.c b/src/oapv_bs.c index 48d2984..25545bf 100644 --- a/src/oapv_bs.c +++ b/src/oapv_bs.c @@ -174,6 +174,7 @@ static int bsr_flush(oapv_bs_t *bs, int byte) if(byte <= 0) { bs->code = 0; bs->leftbits = 0; + bs->is_eob = 1; return -1; } @@ -198,6 +199,7 @@ void oapv_bsr_init(oapv_bs_t *bs, u8 *buf, u32 size, oapv_bs_fn_flush_t fn_flush bs->end = buf + size; bs->code = 0; bs->leftbits = 0; + bs->is_eob = 0; bs->fn_flush = (fn_flush == NULL) ? bsr_flush : fn_flush; } @@ -218,12 +220,10 @@ void oapv_bsr_align8(oapv_bs_t *bs) void oapv_bsr_skip(oapv_bs_t *bs, int size) { - oapv_assert(size > 0 && size <= 32); - if(bs->leftbits < size) { size -= bs->leftbits; if(bs->fn_flush(bs, 8)) { - // oapv_trace("already reached the end of bitstream\n"); /* should be updated */ + // fn_flush set is_eob flag on error; caller must check it return; } } @@ -250,13 +250,11 @@ u32 oapv_bsr_read(oapv_bs_t *bs, int size) { u32 code = 0; - oapv_assert(size > 0); - if(bs->leftbits < size) { code = bs->code >> (64 - size); size -= bs->leftbits; if(bs->fn_flush(bs, 8)) { - oapv_trace("already reached the end of bitstream\n"); /* should be updated */ + // fn_flush set is_eob flag on error; return (u32)(-1) with is_eob marker return (u32)(-1); } } @@ -272,7 +270,7 @@ int oapv_bsr_read1(oapv_bs_t *bs) int code; if(bs->leftbits == 0) { if(bs->fn_flush(bs, 8)) { - oapv_trace("already reached the end of bitstream\n"); /* should be updated */ + // fn_flush set is_eob flag on error; return -1 with is_eob marker return -1; } } @@ -284,14 +282,15 @@ int oapv_bsr_read1(oapv_bs_t *bs) return code; } -u32 oapv_bsr_read_direct(void *addr, int len) +int oapv_bsr_read_direct(const void *addr, int len, u32 *out) { u32 code = 0; int shift = 24; - u8 *p = (u8 *)addr; + const u8 *p = (const u8 *)addr; int byte = (len + 7) >> 3; - oapv_assert(len <= 32); + if(!addr || !out || len <= 0 || len > 32) + return OAPV_ERR_INVALID_ARGUMENT; while(byte) { code |= *(p) << shift; @@ -299,7 +298,8 @@ u32 oapv_bsr_read_direct(void *addr, int len) byte--; p++; } - return (code >> (32 - len)); + *out = (code >> (32 - len)); + return OAPV_OK; } diff --git a/src/oapv_bs.h b/src/oapv_bs.h index 40f1a90..c8193b5 100644 --- a/src/oapv_bs.h +++ b/src/oapv_bs.h @@ -44,6 +44,7 @@ struct oapv_bs { u8 *end; // address of bitstream end u8 *beg; // address of bitstream begin u32 size; // size of input bitstream in byte + int is_eob; // set when read encounters unexpected end-of-bitstream oapv_bs_fn_flush_t fn_flush; // function pointer for flush operation int ndata[4]; // arbitrary data, if needs void *pdata[4]; // arbitrary address, if needs @@ -108,6 +109,9 @@ should set zero in that case. */ /*! Is end of bitstream ? */ #define BSR_IS_EOB(bs) (((bs)->cur >= (bs)->end && (bs)->leftbits==0)? 1: 0) +/*! Did read encounter unexpected end-of-bitstream? */ +#define BSR_IS_UNEXPECTED_EOB(bs) ((bs)->is_eob) + /*! Is bitstream byte aligned? */ #define BSR_IS_BYTE_ALIGN(bs) ((((bs)->leftbits & 0x7) == 0)? 1: 0) @@ -141,7 +145,7 @@ void *oapv_bsr_sink(oapv_bs_t *bs); void oapv_bsr_move(oapv_bs_t *bs, u8 *pos); u32 oapv_bsr_read(oapv_bs_t *bs, int size); int oapv_bsr_read1(oapv_bs_t *bs); -u32 oapv_bsr_read_direct(void *addr, int len); +int oapv_bsr_read_direct(const void *addr, int len, u32 *out); /////////////////////////////////////////////////////////////////////////////// // end of decoder code diff --git a/src/oapv_metadata.c b/src/oapv_metadata.c index 659d904..c2be7e9 100644 --- a/src/oapv_metadata.c +++ b/src/oapv_metadata.c @@ -386,6 +386,8 @@ int oapvm_get_all(oapvm_t mid, oapvm_payload_t *pld, int *num_plds) int num_payload = 0; for(int i = 0; i < ctx->num; i++) { num_payload += ctx->md_arr[i].mdp_num; + if(num_payload > OAPV_MAX_NUM_META_PAYLOADS || num_payload < 0) + return OAPV_ERR_REACHED_MAX; } *num_plds = num_payload; return OAPV_OK; @@ -396,7 +398,8 @@ int oapvm_get_all(oapvm_t mid, oapvm_payload_t *pld, int *num_plds) int group_id = cur_md->group_id; oapv_mdp_t *mdp = cur_md->md_payload; while(mdp != NULL) { - oapv_assert_rv(pld_cnt < *num_plds, OAPV_ERR_REACHED_MAX); + if(pld_cnt >= *num_plds) + return OAPV_ERR_REACHED_MAX; pld[pld_cnt].group_id = group_id; pld[pld_cnt].size = mdp->pld_size; pld[pld_cnt].data = mdp->pld_data; diff --git a/src/oapv_util.h b/src/oapv_util.h index 9226c5f..45d3e3a 100644 --- a/src/oapv_util.h +++ b/src/oapv_util.h @@ -74,6 +74,35 @@ #define oapv_align_value(val, align) ((((val) + (align) - 1) / (align)) * (align)) +// validate tile topology and compute number of tiles (overflow-safe). +// returns OAPV_OK and sets *num_tiles on success, error otherwise. +static inline int oapv_validate_tile_topology(int tile_cols, int tile_rows, int *num_tiles) +{ + if(tile_cols < 1 || tile_cols > OAPV_MAX_TILE_COLS) + return OAPV_ERR_MALFORMED_BITSTREAM; + if(tile_rows < 1 || tile_rows > OAPV_MAX_TILE_ROWS) + return OAPV_ERR_MALFORMED_BITSTREAM; + // both operands are bounded by 20, so the product cannot overflow int + int n = tile_cols * tile_rows; + if(n > OAPV_MAX_TILES) + return OAPV_ERR_MALFORMED_BITSTREAM; + if(num_tiles) + *num_tiles = n; + return OAPV_OK; +} + +// validate that (header + payload) fits within remaining bytes without +// integer wrap. operands are promoted to u64 so a crafted u32 payload +// size cannot bypass the check via overflow. +static inline int oapv_validate_payload_bounds(u64 remaining, u64 header, u64 payload) +{ + if(remaining < header) + return OAPV_ERR_MALFORMED_BITSTREAM; + if(payload > remaining - header) + return OAPV_ERR_MALFORMED_BITSTREAM; + return OAPV_OK; +} + static inline int chroma_format_idc_to_color_format(int chroma_format_idc) { return ((chroma_format_idc == 0) ? OAPV_CF_YCBCR400 diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 1e452fb..2d6b9e3 100644 --- a/src/oapv_vlc.c +++ b/src/oapv_vlc.c @@ -774,6 +774,7 @@ static int dec_vlc_q_matrix(oapv_bs_t *bs, oapv_fh_t *fh) static int dec_vlc_tile_info(oapv_bs_t *bs, oapv_fh_t *fh) { + int ret; int pic_w, pic_h, tile_w, tile_h, tile_cols, tile_rows; fh->tile_width_in_mbs = oapv_bsr_read(bs, 20); @@ -794,7 +795,8 @@ static int dec_vlc_tile_info(oapv_bs_t *bs, oapv_fh_t *fh) tile_cols = (pic_w + (tile_w - 1)) / tile_w; tile_rows = (pic_h + (tile_h - 1)) / tile_h; - oapv_assert_rv(tile_cols <= OAPV_MAX_TILE_COLS && tile_rows <= OAPV_MAX_TILE_ROWS, OAPV_ERR_MALFORMED_BITSTREAM) + ret = oapv_validate_tile_topology(tile_cols, tile_rows, NULL); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); fh->tile_size_present_in_fh_flag = oapv_bsr_read1(bs); DUMP_HLS(fh->tile_size_present_in_fh_flag, fh->tile_size_present_in_fh_flag); @@ -825,6 +827,9 @@ int oapvd_vlc_dc_coef(oapv_bs_t *bs, int *dc_diff, int *kparam_dc) *dc_diff = 0; *kparam_dc = OAPV_KPARAM_DC_MIN; } + + if(BSR_IS_UNEXPECTED_EOB(bs)) + return OAPV_ERR_MALFORMED_BITSTREAM; return OAPV_OK; } @@ -909,6 +914,9 @@ int oapvd_vlc_ac_coef(oapv_bs_t *bs, s16 *coef, int *kparam_ac) break; } } while(1); + + if(BSR_IS_UNEXPECTED_EOB(bs)) + return OAPV_ERR_MALFORMED_BITSTREAM; return OAPV_OK; } @@ -917,6 +925,7 @@ int oapvd_vlc_au_size(oapv_bs_t *bs, u32 *au_size) u32 size; size = oapv_bsr_read(bs, 32); oapv_assert_rv(size > 0 && size < 0xFFFFFFFF, OAPV_ERR_MALFORMED_BITSTREAM); + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); *au_size = size; return OAPV_OK; } @@ -927,6 +936,7 @@ int oapvd_vlc_pbu_size(oapv_bs_t *bs, u32 *pbu_size) size = oapv_bsr_read(bs, 32); DUMP_HLS(pbu_size, size); oapv_assert_rv(size > 0 && size < 0xFFFFFFFF, OAPV_ERR_MALFORMED_BITSTREAM); + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); *pbu_size = size; return OAPV_OK; } @@ -948,6 +958,8 @@ int oapvd_vlc_pbu_header(oapv_bs_t *bs, oapv_pbuh_t *pbuh) reserved_zero = oapv_bsr_read(bs, 8); DUMP_HLS(reserved_zero, reserved_zero); oapv_assert_rv(reserved_zero == 0, OAPV_ERR_MALFORMED_BITSTREAM); + + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); return OAPV_OK; } @@ -1005,6 +1017,7 @@ int oapvd_vlc_frame_info(oapv_bs_t *bs, oapv_fi_t *fi) oapv_assert_rv((fi->frame_width & 0x1) == 0, OAPV_ERR_INVALID_WIDTH); } + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); return OAPV_OK; } @@ -1030,6 +1043,9 @@ int oapvd_vlc_au_info(oapv_bs_t *bs, oapv_aui_t *aui) reserved_zero_8bits = oapv_bsr_read(bs, 8); DUMP_HLS(reserved_zero_8bits, reserved_zero_8bits); oapv_assert_rv(reserved_zero_8bits == 0, OAPV_ERR_MALFORMED_BITSTREAM); + + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); + /* byte align */ oapv_bsr_align8(bs); return OAPV_OK; @@ -1078,6 +1094,8 @@ int oapvd_vlc_frame_header(oapv_bs_t *bs, oapv_fh_t *fh) DUMP_HLS(reserved_zero, reserved_zero); oapv_assert_rv(reserved_zero == 0, OAPV_ERR_MALFORMED_BITSTREAM); + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); + /* byte align */ oapv_bsr_align8(bs); @@ -1100,6 +1118,7 @@ int oapvd_vlc_tile_size(oapv_bs_t *bs, u32 *tile_size) u32 size = oapv_bsr_read(bs, 32); DUMP_HLS(tile_size, size); oapv_assert_rv(size > 0, OAPV_ERR_MALFORMED_BITSTREAM); + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); *tile_size = size; return OAPV_OK; } @@ -1121,7 +1140,8 @@ int oapvd_vlc_tile_header(oapv_bs_t *bs, int num_comp, oapv_th_t *th, u32 tile_s for(int c = 0; c < num_comp; c++) { th->tile_data_size[c] = oapv_bsr_read(bs, 32); DUMP_HLS(th->tile_data_size, th->tile_data_size[c]); - oapv_assert_rv((tile_size - read_size) >= th->tile_data_size[c], OAPV_ERR_MALFORMED_BITSTREAM); + int ret = oapv_validate_payload_bounds(tile_size, read_size, th->tile_data_size[c]); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); read_size += th->tile_data_size[c]; } for(int c = 0; c < num_comp; c++) { @@ -1130,10 +1150,12 @@ int oapvd_vlc_tile_header(oapv_bs_t *bs, int num_comp, oapv_th_t *th, u32 tile_s } th->reserved_zero_8bits = oapv_bsr_read(bs, 8); DUMP_HLS(th->reserved_zero_8bits, th->reserved_zero_8bits); + oapv_assert_rv(th->reserved_zero_8bits == 0, OAPV_ERR_MALFORMED_BITSTREAM); + + oapv_assert_rv(!BSR_IS_UNEXPECTED_EOB(bs), OAPV_ERR_MALFORMED_BITSTREAM); + /* byte align */ oapv_bsr_align8(bs); - - oapv_assert_rv(th->reserved_zero_8bits == 0, OAPV_ERR_MALFORMED_BITSTREAM); return OAPV_OK; } @@ -1141,6 +1163,8 @@ int oapvd_vlc_tile_dummy_data(oapv_bs_t *bs) { while(BSR_GET_LEFT_BYTE(bs) > 0) { oapv_bsr_read(bs, 8); + if(BSR_IS_UNEXPECTED_EOB(bs)) + return OAPV_ERR_MALFORMED_BITSTREAM; } return OAPV_OK; } @@ -1161,6 +1185,7 @@ int oapvd_vlc_metadata(oapv_bs_t *bs, u32 pbu_size, oapvm_t mid, int group_id) t0 = 0; do { t0 = oapv_bsr_read(bs, 8); + oapv_assert_gv(!BSR_IS_UNEXPECTED_EOB(bs), ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); DUMP_HLS(payload_type, t0); oapv_assert_gv(metadata_size > 0, ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); metadata_size -= 1; @@ -1173,6 +1198,7 @@ int oapvd_vlc_metadata(oapv_bs_t *bs, u32 pbu_size, oapvm_t mid, int group_id) t0 = 0; do { t0 = oapv_bsr_read(bs, 8); + oapv_assert_gv(!BSR_IS_UNEXPECTED_EOB(bs), ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); DUMP_HLS(payload_size, t0); oapv_assert_gv(metadata_size > 0, ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); metadata_size -= 1; @@ -1216,9 +1242,10 @@ int oapvd_vlc_filler(oapv_bs_t *bs, u32 filler_size) int val; while(filler_size > 0) { val = oapv_bsr_read(bs, 8); - if(val != 0xFF) { + if(BSR_IS_UNEXPECTED_EOB(bs)) + return OAPV_ERR_MALFORMED_BITSTREAM; + if(val != 0xFF) return OAPV_ERR_MALFORMED_BITSTREAM; - } filler_size--; } return OAPV_OK; From 9cba8856bb7b2a0dc7f33633b643291621b246fd Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Tue, 7 Jul 2026 15:47:04 +0900 Subject: [PATCH 02/58] fixed memory leak Signed-off-by: kp5.choi@samsung.com --- CMakeLists.txt | 8 ++++++++ app/oapv_app_dec.c | 3 +++ 2 files changed, 11 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f040c92..6700bca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,8 @@ option(OAPV_BUILD_SHARED_LIB "Build oapv shared library" ON) option(ENABLE_COVERAGE "Enable code coverage measurement" OFF) +option(OAPV_ENABLE_SANITIZERS "Enable AddressSanitizer and UndefinedBehaviorSanitizer" OFF) + if(OAPV_BUILD_APPS AND OAPV_APP_STATIC_BUILD AND NOT OAPV_BUILD_STATIC_LIB) message(FATAL_ERROR "Cannot build static apps without static lib.") endif() @@ -130,6 +132,12 @@ elseif(UNIX OR MINGW) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") endif() + + if(OAPV_ENABLE_SANITIZERS) + # -fno-omit-frame-pointer overrides the earlier -fomit-frame-pointer + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address,undefined") + endif() else() message("Unknown compiler") endif() diff --git a/app/oapv_app_dec.c b/app/oapv_app_dec.c index 044390c..ed995a5 100644 --- a/app/oapv_app_dec.c +++ b/app/oapv_app_dec.c @@ -705,6 +705,9 @@ int dec_api_set_0(args_var_t *args_var, FILE *fp_bs, int is_y4m) if(imgb_w != NULL) imgb_w->release(imgb_w); + if(bs_buf != NULL) + free(bs_buf); + return 0; } From 4ff5f571301106b48f6ff27f1bc2e155a11a0fbd Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Tue, 7 Jul 2026 15:58:30 +0900 Subject: [PATCH 03/58] Validate tile QP range when parsing tile header Reject tile QP values outside the valid range while reading the tile header, instead of using them in later shift computations. --- src/oapv.c | 2 +- src/oapv_vlc.c | 4 +++- src/oapv_vlc.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/oapv.c b/src/oapv.c index 6d25ad9..178e866 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1590,7 +1590,7 @@ static int dec_tile(oapvd_core_t *core, oapvd_tile_t *tile) oapv_bs_t bs; // bs for 'tile()' syntax oapv_bsr_init(&bs, tile->bs_beg + OAPV_TILE_SIZE_LEN, tile->tile_size, NULL); - ret = oapvd_vlc_tile_header(&bs, ctx->num_c, &tile->th, tile->tile_size); + ret = oapvd_vlc_tile_header(&bs, ctx->num_c, &tile->th, tile->tile_size, ctx->bit_depth); oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); for(c = 0; c < ctx->num_c; c++) { diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 2d6b9e3..2c5122e 100644 --- a/src/oapv_vlc.c +++ b/src/oapv_vlc.c @@ -1123,7 +1123,7 @@ int oapvd_vlc_tile_size(oapv_bs_t *bs, u32 *tile_size) return OAPV_OK; } -int oapvd_vlc_tile_header(oapv_bs_t *bs, int num_comp, oapv_th_t *th, u32 tile_size) +int oapvd_vlc_tile_header(oapv_bs_t *bs, int num_comp, oapv_th_t *th, u32 tile_size, int bit_depth) { u32 read_size = 0; @@ -1146,6 +1146,8 @@ int oapvd_vlc_tile_header(oapv_bs_t *bs, int num_comp, oapv_th_t *th, u32 tile_s } for(int c = 0; c < num_comp; c++) { th->tile_qp[c] = oapv_bsr_read(bs, 8); + oapv_assert_rv(th->tile_qp[c] >= MIN_QUANT && th->tile_qp[c] <= MAX_QUANT(bit_depth), + OAPV_ERR_MALFORMED_BITSTREAM); DUMP_HLS(th->tile_qp, th->tile_qp[c]); } th->reserved_zero_8bits = oapv_bsr_read(bs, 8); diff --git a/src/oapv_vlc.h b/src/oapv_vlc.h index 435515b..b04d12c 100644 --- a/src/oapv_vlc.h +++ b/src/oapv_vlc.h @@ -63,7 +63,7 @@ int oapvd_vlc_au_info(oapv_bs_t* bs, oapv_aui_t* aui); int oapvd_vlc_frame_header(oapv_bs_t* bs, oapv_fh_t* fh); int oapvd_vlc_frame_info(oapv_bs_t* bs, oapv_fi_t *fi); int oapvd_vlc_tile_size(oapv_bs_t *bs, u32 *tile_size); -int oapvd_vlc_tile_header(oapv_bs_t *bs, int num_comp, oapv_th_t *th, u32 tiles_data_size); +int oapvd_vlc_tile_header(oapv_bs_t *bs, int num_comp, oapv_th_t *th, u32 tiles_data_size, int bit_depth); int oapvd_vlc_tile_dummy_data(oapv_bs_t* bs); int oapvd_vlc_metadata(oapv_bs_t* bs, u32 pbu_size, oapvm_t mid, int group_id); int oapvd_vlc_filler(oapv_bs_t* bs, u32 filler_size); From a995224986aca5b0445b13162f37b34f64d581e0 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Tue, 7 Jul 2026 16:20:46 +0900 Subject: [PATCH 04/58] Cast to unsigned char before toupper() in check_file_name_type Avoid passing negative signed char values to toupper(). --- app/oapv_app_y4m.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/oapv_app_y4m.h b/app/oapv_app_y4m.h index 02aa7c2..9e41998 100644 --- a/app/oapv_app_y4m.h +++ b/app/oapv_app_y4m.h @@ -321,9 +321,9 @@ static int check_file_name_type(char * fname) return -1; } strncpy(fext, fname + strlen(fname) - 3, sizeof(fext) - 1); - fext[0] = toupper(fext[0]); - fext[1] = toupper(fext[1]); - fext[2] = toupper(fext[2]); + fext[0] = toupper((unsigned char)fext[0]); + fext[1] = toupper((unsigned char)fext[1]); + fext[2] = toupper((unsigned char)fext[2]); if(strcmp(fext, "YUV") == 0) { return 0; From 1813adf3aa7a10184cbece6774cb05dd48a6134e Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Tue, 7 Jul 2026 16:35:30 +0900 Subject: [PATCH 05/58] Bound AC coefficient run length to remaining block capacity Reject run values that would advance the scan position past the block before accumulating them, avoiding signed overflow of the offset. --- src/oapv_vlc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 2c5122e..5383457 100644 --- a/src/oapv_vlc.c +++ b/src/oapv_vlc.c @@ -871,7 +871,7 @@ int oapvd_vlc_ac_coef(oapv_bs_t *bs, s16 *coef, int *kparam_ac) run = dec_vlc_read(bs, k_run); } - oapv_assert_rv(run >= 0, OAPV_ERR_MALFORMED_BITSTREAM); + oapv_assert_rv(run >= 0 && run <= OAPV_BLK_D - scan_pos_offset, OAPV_ERR_MALFORMED_BITSTREAM); // here, no need to set 'zero-run' in coef; it's already initialized to zero. From 832ee20834e680483727e3ec37074013864c2f0d Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Tue, 7 Jul 2026 17:01:21 +0900 Subject: [PATCH 06/58] Make thread-pool sync object deletion NULL-safe Return early when the sync object is already NULL, and guard the caller in the decoder teardown path, so an early-exit before the object is created does not dereference a NULL pointer. --- src/oapv.c | 4 +++- src/oapv_tpool.c | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/oapv.c b/src/oapv.c index 178e866..a408ad7 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1735,7 +1735,9 @@ static void dec_flush(oapvd_ctx_t *ctx) } } - oapv_tpool_sync_obj_delete(&(ctx->sync_obj)); + if(ctx->sync_obj != NULL) { + oapv_tpool_sync_obj_delete(&(ctx->sync_obj)); + } for(int i = 0; i < ctx->threads; i++) { dec_core_free(ctx->core[i]); diff --git a/src/oapv_tpool.c b/src/oapv_tpool.c index 8be0740..b0fca8d 100644 --- a/src/oapv_tpool.c +++ b/src/oapv_tpool.c @@ -308,6 +308,7 @@ tpool_result_t oapv_tpool_sync_obj_delete(oapv_sync_obj_t *sobj) { thread_mutex_t *imutex = (thread_mutex_t *)(*sobj); + if(imutex == NULL) return TPOOL_SUCCESS; // nothing to delete // delete the mutex pthread_mutex_destroy(&imutex->lmutex); @@ -612,6 +613,7 @@ oapv_sync_obj_t oapv_tpool_sync_obj_create() tpool_result_t oapv_tpool_sync_obj_delete(oapv_sync_obj_t *sobj) { thread_mutex_t *imutex = (thread_mutex_t *)(*sobj); + if(imutex == NULL) return TPOOL_SUCCESS; // nothing to delete #if WINDOWS_MUTEX_SYNC // release the mutex CloseHandle(imutex->lmutex); From 54edcf7a92b7d018fa5f5ea7968602302af34693 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Tue, 7 Jul 2026 17:05:38 +0900 Subject: [PATCH 07/58] Free payload buffer when metadata group limit is reached The max-group guard returned directly, leaking the already-allocated payload buffer; route it through the cleanup path instead. --- src/oapv_metadata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oapv_metadata.c b/src/oapv_metadata.c index c2be7e9..461e41f 100644 --- a/src/oapv_metadata.c +++ b/src/oapv_metadata.c @@ -299,7 +299,7 @@ int oapvm_set(oapvm_t mid, int group_id, int type, void *data, int size) oapv_md_t *md = meta_find_md(ctx, group_id); if(md == NULL) { - oapv_assert_rv(ctx->num < OAPV_MAX_NUM_METAS, OAPV_ERR_REACHED_MAX); + oapv_assert_gv(ctx->num < OAPV_MAX_NUM_METAS, ret, OAPV_ERR_REACHED_MAX, ERR); md = &ctx->md_arr[ctx->num]; md->group_id = group_id; md->mdp_num = 0; From 4235d99cc05f13ebffcac6d0a6db02798178d8a9 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Tue, 7 Jul 2026 17:28:15 +0900 Subject: [PATCH 08/58] Reject unsupported bit depths in image copy Validate source and destination bit depths are within the supported 8-16 range before dispatching the copy, preventing out-of-range shift counts and destination buffer size mismatches. --- app/oapv_app_util.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/oapv_app_util.h b/app/oapv_app_util.h index 9e19057..ec10752 100644 --- a/app/oapv_app_util.h +++ b/app/oapv_app_util.h @@ -657,6 +657,11 @@ static void imgb_cpy(oapv_imgb_t *dst, oapv_imgb_t *src) bd_src = OAPV_CS_GET_BIT_DEPTH(src->cs); bd_dst = OAPV_CS_GET_BIT_DEPTH(dst->cs); + if(bd_src < 8 || bd_src > 16 || bd_dst < 8 || bd_dst > 16) { + logerr("ERROR: unsupported bit depth in image copy\n"); + return; + } + if(src->cs == dst->cs) { imgb_cpy_plane(dst, src); } From 9f492423c2f68b4d5f9390e5ed16e64a55bbdaf0 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 09:22:32 +0900 Subject: [PATCH 09/58] Bound --config count in argument parsing --- app/oapv_app_args.h | 1 + 1 file changed, 1 insertion(+) diff --git a/app/oapv_app_args.h b/app/oapv_app_args.h index d464e51..48ff8d8 100644 --- a/app/oapv_app_args.h +++ b/app/oapv_app_args.h @@ -465,6 +465,7 @@ static int args_parse(args_parser_t *args, int argc, const char *argv[], for(i = 1; i < argc; i++) { if(!strcmp(argv[i], "--" ARGS_KEY_LONG_CONFIG)) { if(i + 1 < argc) { + if(num_configs >= ARGS_MAX_NUM_CONF_FILES) break; num_configs++; pos_conf_files[num_configs - 1] = i + 1; } From 3cd1f6708f76f2d31f982172f9975e8e08f3d22a Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 09:30:25 +0900 Subject: [PATCH 10/58] Reject NULL image buffer in frame prepare --- src/oapv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oapv.c b/src/oapv.c index a408ad7..e2fd0fb 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1415,6 +1415,8 @@ static int dec_frm_prepare(oapvd_ctx_t *ctx, oapv_tile_info_t * part, oapv_imgb_ { int i, ret; + oapv_assert_rv(imgb != NULL, OAPV_ERR_MALFORMED_BITSTREAM); + // the input image buffer must match the frame format signaled in the // bitstream; a mismatch (e.g. caused by a resolution change without // reallocation) is rejected as an invalid argument From 9d7136f43c1e653ea1787cb69303229e95e00be0 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 09:56:07 +0900 Subject: [PATCH 11/58] Bound Y4M frame dimensions --- app/oapv_app_y4m.h | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/oapv_app_y4m.h b/app/oapv_app_y4m.h index 9e41998..8af1fd7 100644 --- a/app/oapv_app_y4m.h +++ b/app/oapv_app_y4m.h @@ -84,19 +84,27 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) switch(p[0]) { case 'W': { if(sscanf(p + 1, "%d", &y4m->w) != 1) - return OAPV_ERR; + return -1; + if(y4m->w <= 0 || y4m->w > IMGB_MAX_W) { + logerr("ERROR: y4m frame width %d is out of range (1..%d)\n", y4m->w, IMGB_MAX_W); + return -1; + } found_w = 1; break; } case 'H': { if(sscanf(p + 1, "%d", &y4m->h) != 1) - return OAPV_ERR; + return -1; + if(y4m->h <= 0 || y4m->h > IMGB_MAX_H) { + logerr("ERROR: y4m frame height %d is out of range (1..%d)\n", y4m->h, IMGB_MAX_H); + return -1; + } found_h = 1; break; } case 'F': { if(sscanf(p + 1, "%d:%d", &fps_n, &fps_d) != 2) - return OAPV_ERR; + return -1; y4m->fps_num = fps_n; y4m->fps_den = fps_d; break; @@ -107,12 +115,12 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) } case 'A': { if(sscanf(p + 1, "%d:%d", &pix_ratio_n, &pix_ratio_d) != 2) - return OAPV_ERR; + return -1; break; } case 'C': { if(q - p > 16) - return OAPV_ERR; + return -1; memcpy(colorspace, p + 1, q - p - 1); colorspace[q - p - 1] = '\0'; found_cf = 1; @@ -124,7 +132,7 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) if(!(found_w == 1 && found_h == 1)) { logerr("Mandatory width and height values were not found in y4m header"); - return OAPV_ERR; + return -1; } if(!found_cf) { @@ -177,7 +185,7 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) y4m->color_format = OAPV_CF_UNKNOWN; y4m->bit_depth = -1; } - return OAPV_OK; + return 0; } int y4m_header_parser(FILE *ip_y4m, y4m_params_t *y4m) From a5cce73b5f618654296f214e5dc1e55b2df4c9b2 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 10:01:01 +0900 Subject: [PATCH 12/58] Bound encoder frame dimensions --- app/oapv_app_enc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c index 9ef77ef..27ca7f1 100644 --- a/app/oapv_app_enc.c +++ b/app/oapv_app_enc.c @@ -1117,6 +1117,13 @@ int main(int argc, const char **argv) goto ERR; } + if(param->w <= 0 || param->w > IMGB_MAX_W || param->h <= 0 || param->h > IMGB_MAX_H) { + logerr("ERR: frame dimensions %dx%d out of range (max %dx%d)\n", + param->w, param->h, IMGB_MAX_W, IMGB_MAX_H); + ret = -1; + goto ERR; + } + for(int i = 0; i < num_frames; i++) { if(args_var->input_depth == codec_depth) { ifrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); From 8d90ba8a8894e4c0c7ee1df65046f18332e52f0e Mon Sep 17 00:00:00 2001 From: Fyodor Kyslov <86332758+fkyslov@users.noreply.github.com> Date: Wed, 8 Jul 2026 01:28:14 +0000 Subject: [PATCH 13/58] Fix heap buffer overflow in libopenapv encoder (#212) Add bounds checking to BSW_FLUSH_4BYTE and BSW_FLUSH_8BYTE macros in oapv_vlc.c to prevent writing past the end of the bitstream buffer during VLC encoding. Add bounds checking in enc_frame in oapv.c to ensure the cumulative tile bitstream size does not exceed the target bitstream buffer end before copying tile bitstreams. These changes prevent heap buffer overflows in the encoder. Bug: 501452526 Test: Manual verification with PoC binaries on Cuttlefish Flag: EXEMPT CVE_FIX Change-Id: Ib40bc500096b6fda93e5802d97b306e4320ba6eb --- src/oapv.c | 1 + src/oapv_vlc.c | 32 ++++++++++++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/oapv.c b/src/oapv.c index e2fd0fb..3874675 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1026,6 +1026,7 @@ static int enc_frame(oapve_ctx_t *ctx, oapv_bs_t *bs) /****************************************************/ for(int i = 0; i < ctx->num_tiles; i++) { + oapv_assert_gv(bs_tile_pos + ctx->tile[i].bs_size <= bs->end, ret, OAPV_ERR_OUT_OF_BS_BUF, ERR); oapv_mcpy(bs_tile_pos, ctx->tile[i].bs_buf, ctx->tile[i].bs_size); bs_tile_pos = bs_tile_pos + ctx->tile[i].bs_size; ctx->fh.tile_size[i] = ctx->tile[i].bs_size - OAPV_TILE_SIZE_LEN; diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 5383457..0a6deac 100644 --- a/src/oapv_vlc.c +++ b/src/oapv_vlc.c @@ -37,23 +37,31 @@ #if ENABLE_ENCODER /////////////////////////////////////////////////////////////////////////////// #define BSW_FLUSH_4BYTE(bs) { \ - *(bs)->cur++ = ((bs)->code >> 24) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 16) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 8) & 0xFF; \ - *(bs)->cur++ = ((bs)->code) & 0xFF; \ + if ((bs)->cur + 4 <= (bs)->end) { \ + *(bs)->cur++ = ((bs)->code >> 24) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 16) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 8) & 0xFF; \ + *(bs)->cur++ = ((bs)->code) & 0xFF; \ + } else { \ + (bs)->cur += 4; \ + } \ (bs)->code = 0; \ (bs)->leftbits = 32; \ } #define BSW_FLUSH_8BYTE(bs) { \ - *(bs)->cur++ = ((bs)->code >> 56) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 48) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 40) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 32) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 24) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 16) & 0xFF; \ - *(bs)->cur++ = ((bs)->code >> 8) & 0xFF; \ - *(bs)->cur++ = ((bs)->code) & 0xFF; \ + if ((bs)->cur + 8 <= (bs)->end) { \ + *(bs)->cur++ = ((bs)->code >> 56) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 48) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 40) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 32) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 24) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 16) & 0xFF; \ + *(bs)->cur++ = ((bs)->code >> 8) & 0xFF; \ + *(bs)->cur++ = ((bs)->code) & 0xFF; \ + } else { \ + (bs)->cur += 8; \ + } \ (bs)->code = 0; \ (bs)->leftbits = 64; \ } From 6672f0115462c6378ee889db7b25c983527a3577 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 11:00:02 +0900 Subject: [PATCH 14/58] Reject invalid Y4M frame rate --- app/oapv_app_y4m.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/oapv_app_y4m.h b/app/oapv_app_y4m.h index 8af1fd7..a19c99a 100644 --- a/app/oapv_app_y4m.h +++ b/app/oapv_app_y4m.h @@ -105,6 +105,10 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) case 'F': { if(sscanf(p + 1, "%d:%d", &fps_n, &fps_d) != 2) return -1; + if(fps_n <= 0 || fps_d <= 0) { + logerr("ERROR: invalid y4m frame rate %d:%d\n", fps_n, fps_d); + return -1; + } y4m->fps_num = fps_n; y4m->fps_den = fps_d; break; From 42a228bba6afb93d434acea6786239ed490fd3bd Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 11:13:05 +0900 Subject: [PATCH 15/58] Use ERR: prefix consistently in log messages --- app/oapv_app_dec.c | 14 +++++++------- app/oapv_app_enc.c | 4 ++-- app/oapv_app_util.h | 20 ++++++++++---------- app/oapv_app_y4m.h | 22 +++++++++++----------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/oapv_app_dec.c b/app/oapv_app_dec.c index ed995a5..245090b 100644 --- a/app/oapv_app_dec.c +++ b/app/oapv_app_dec.c @@ -237,7 +237,7 @@ static int read_bitstream(FILE *fp, unsigned char *bs_buf, int *bs_buf_size) return 0; } else if(ret < 0) { - logerr("Cannot read bitstream size!\n"); + logerr("ERR: Cannot read bitstream size!\n"); return -1; } if(au_size > 0) { @@ -249,7 +249,7 @@ static int read_bitstream(FILE *fp, unsigned char *bs_buf, int *bs_buf_size) while(au_size > 0) { /* read byte */ if(1 != fread(&b, 1, 1, fp)) { - logerr("Cannot read bitstream!\n"); + logerr("ERR: Cannot read bitstream!\n"); return -1; } bs_buf[read_size] = b; @@ -260,12 +260,12 @@ static int read_bitstream(FILE *fp, unsigned char *bs_buf, int *bs_buf_size) } else { /* size field present but zero: malformed bitstream */ - logerr("Cannot read bitstream size!\n"); + logerr("ERR: Cannot read bitstream size!\n"); return -1; } } else { - logerr("Cannot seek bitstream!\n"); + logerr("ERR: Cannot seek bitstream!\n"); return -1; } return read_size + 4; @@ -280,7 +280,7 @@ static int set_extra_config(oapvd_t id, args_var_t *args_vars) size = 4; ret = oapvd_config(id, OAPV_CFG_SET_USE_FRM_HASH, &value, &size); if(OAPV_FAILED(ret)) { - logerr("failed to set config for using frame hash\n"); + logerr("ERR: failed to set config for using frame hash\n"); return -1; } } @@ -289,7 +289,7 @@ static int set_extra_config(oapvd_t id, args_var_t *args_vars) size = 4; ret = oapvd_config(id, OAPV_CFG_SET_DISABLE_COMPANDING, &value, &size); if(OAPV_FAILED(ret)) { - logerr("failed to set config for disabling companding process\n"); + logerr("ERR: failed to set config for disabling companding process\n"); return -1; } } @@ -742,7 +742,7 @@ static int read_u32(FILE *fp, unsigned int * val) static int read_pbu(FILE *fp, unsigned char *pbu, unsigned int pbu_size) { if(pbu_size != fread(pbu, 1, pbu_size, fp)) { - logerr("Cannot read PDU!\n"); + logerr("ERR: Cannot read PDU!\n"); return -1; } return 0; diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c index 27ca7f1..36540bd 100644 --- a/app/oapv_app_enc.c +++ b/app/oapv_app_enc.c @@ -706,7 +706,7 @@ static int family_to_bitrate(char * family, oapve_param_t *param) #define UPDATE_A_PARAM_W_KEY_VAL(param, key, val) \ if(strlen(val) > 0) { \ if(OAPV_FAILED(oapve_param_parse(param, key, val))) { \ - logerr("input value (%s) of %s is invalid\n", val, key); \ + logerr("ERR: input value (%s) of %s is invalid\n", val, key); \ return -1; \ } \ } @@ -782,7 +782,7 @@ static int parse_master_display(const char* data_string, oapvm_payload_mdcv_t *m // Check if sscanf successfully assigned all expected fields (10 numerical values). const int expected_fields = 10; if (assigned_fields != expected_fields) { - logerr("Parsing error: master display color volume information"); + logerr("ERR: Parsing error: master display color volume information"); return -1; } return 0; // Success diff --git a/app/oapv_app_util.h b/app/oapv_app_util.h index ec10752..7a086ab 100644 --- a/app/oapv_app_util.h +++ b/app/oapv_app_util.h @@ -322,7 +322,7 @@ oapv_imgb_t *imgb_create(int w, int h, int cs) /* reject invalid or out-of-range resolution */ if(w <= 0 || h <= 0 || w > IMGB_MAX_W || h > IMGB_MAX_H || bd <= 0) { - logerr("invalid image parameter (w=%d, h=%d, byte-depth=%d)\n", w, h, bd); + logerr("ERR: invalid image parameter (w=%d, h=%d, byte-depth=%d)\n", w, h, bd); goto ERR; } @@ -385,7 +385,7 @@ oapv_imgb_t *imgb_create(int w, int h, int cs) return imgb; ERR: - logerr("cannot create image buffer\n"); + logerr("ERR: cannot create image buffer\n"); if(imgb) { for(int i = 0; i < OAPV_MAX_CC; i++) { if(imgb->a[i]) @@ -407,11 +407,11 @@ static int imgb_read(FILE *fp, oapv_imgb_t *img, int width, int height, int is_y if(6 != fread(t_buf, 1, 6, fp)) return -1; if(memcmp(t_buf, "FRAME", 5)) { - logerr("Loss of framing in Y4M input data\n"); + logerr("ERR: Loss of framing in Y4M input data\n"); return -1; } if(t_buf[5] != '\n') { - logerr("Error parsing Y4M frame header\n"); + logerr("ERR: parsing Y4M frame header\n"); return -1; } } @@ -431,7 +431,7 @@ static int imgb_read(FILE *fp, oapv_imgb_t *img, int width, int height, int is_y f_h = height; } else { - logerr("unsupported bit-depth (%d)\n", bit_depth); + logerr("ERR: unsupported bit-depth (%d)\n", bit_depth); return -1; } @@ -500,7 +500,7 @@ static int imgb_write(char *fname, oapv_imgb_t *imgb) fp = fopen(fname, "ab"); if(fp == NULL) { - logerr("cannot open file = %s\n", fname); + logerr("ERR: cannot open file = %s\n", fname); return -1; } if(bit_depth == 8 && (chroma_format == OAPV_CF_YCBCR400 || chroma_format == OAPV_CF_YCBCR420 || chroma_format == OAPV_CF_YCBCR422 || @@ -514,7 +514,7 @@ static int imgb_write(char *fname, oapv_imgb_t *imgb) bd = 2; } else { - logerr("cannot support the color space\n"); + logerr("ERR: cannot support the color space\n"); fclose(fp); return -1; } @@ -658,7 +658,7 @@ static void imgb_cpy(oapv_imgb_t *dst, oapv_imgb_t *src) bd_dst = OAPV_CS_GET_BIT_DEPTH(dst->cs); if(bd_src < 8 || bd_src > 16 || bd_dst < 8 || bd_dst > 16) { - logerr("ERROR: unsupported bit depth in image copy\n"); + logerr("ERR: unsupported bit depth in image copy\n"); return; } @@ -757,7 +757,7 @@ static int write_data(char *fname, unsigned char *data, int size) fp = fopen(fname, "ab"); if(fp == NULL) { - logerr("cannot open the output file=%s\n", fname); + logerr("ERR: cannot open the output file=%s\n", fname); return -1; } fwrite(data, 1, size, fp); @@ -770,7 +770,7 @@ static int clear_data(char *fname) FILE *fp; fp = fopen(fname, "wb"); if(fp == NULL) { - logerr("cannot remove file (%s)\n", fname); + logerr("ERR: cannot remove file (%s)\n", fname); return -1; } fclose(fp); diff --git a/app/oapv_app_y4m.h b/app/oapv_app_y4m.h index a19c99a..b85164a 100644 --- a/app/oapv_app_y4m.h +++ b/app/oapv_app_y4m.h @@ -86,7 +86,7 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) if(sscanf(p + 1, "%d", &y4m->w) != 1) return -1; if(y4m->w <= 0 || y4m->w > IMGB_MAX_W) { - logerr("ERROR: y4m frame width %d is out of range (1..%d)\n", y4m->w, IMGB_MAX_W); + logerr("ERR: y4m frame width %d is out of range (1..%d)\n", y4m->w, IMGB_MAX_W); return -1; } found_w = 1; @@ -96,7 +96,7 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) if(sscanf(p + 1, "%d", &y4m->h) != 1) return -1; if(y4m->h <= 0 || y4m->h > IMGB_MAX_H) { - logerr("ERROR: y4m frame height %d is out of range (1..%d)\n", y4m->h, IMGB_MAX_H); + logerr("ERR: y4m frame height %d is out of range (1..%d)\n", y4m->h, IMGB_MAX_H); return -1; } found_h = 1; @@ -106,7 +106,7 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) if(sscanf(p + 1, "%d:%d", &fps_n, &fps_d) != 2) return -1; if(fps_n <= 0 || fps_d <= 0) { - logerr("ERROR: invalid y4m frame rate %d:%d\n", fps_n, fps_d); + logerr("ERR: invalid y4m frame rate %d:%d\n", fps_n, fps_d); return -1; } y4m->fps_num = fps_n; @@ -135,7 +135,7 @@ static int y4m_parse_tags(y4m_params_t *y4m, char *tags) } if(!(found_w == 1 && found_h == 1)) { - logerr("Mandatory width and height values were not found in y4m header"); + logerr("ERR: Mandatory width and height values were not found in y4m header"); return -1; } @@ -211,20 +211,20 @@ int y4m_header_parser(FILE *ip_y4m, y4m_params_t *y4m) } /*We skipped too much header data.*/ if(i == (head_size - 1)) { - logerr("Error parsing header; not a YUV2MPEG2 file?\n"); + logerr("ERR: parsing header; not a YUV2MPEG2 file?\n"); return -1; } buffer[i] = '\0'; if(memcmp(buffer, "YUV4MPEG", 8)) { - logerr("Incomplete magic for YUV4MPEG file.\n"); + logerr("ERR: Incomplete magic for YUV4MPEG file.\n"); return -1; } if(buffer[8] != '2') { - logerr("Incorrect YUV input file version; YUV4MPEG2 required.\n"); + logerr("ERR: Incorrect YUV input file version; YUV4MPEG2 required.\n"); } ret = y4m_parse_tags(y4m, buffer + 5); if(ret < 0) { - logerr("Error parsing YUV4MPEG2 header.\n"); + logerr("ERR: parsing YUV4MPEG2 header.\n"); return ret; } return 0; @@ -284,7 +284,7 @@ static int write_y4m_header(char *fname, oapv_imgb_t *imgb) } if(strlen(c_buf) == 0) { - logerr("Color format is not supported by y4m\n"); + logerr("ERR: Color format is not supported by y4m\n"); return -1; } @@ -294,7 +294,7 @@ static int write_y4m_header(char *fname, oapv_imgb_t *imgb) fp = fopen(fname, "ab"); if(fp == NULL) { - logerr("cannot open file = %s\n", fname); + logerr("ERR: cannot open file = %s\n", fname); return -1; } if(buff_len != fwrite(buf, 1, buff_len, fp)) { @@ -310,7 +310,7 @@ static int write_y4m_frame_header(char *fname) FILE *fp; fp = fopen(fname, "ab"); if(fp == NULL) { - logerr("cannot open file = %s\n", fname); + logerr("ERR: cannot open file = %s\n", fname); return -1; } if(6 != fwrite("FRAME\n", 1, 6, fp)) { From de60bba97fc50531ba986f3ad1cbdefc88b3d4df Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 11:15:24 +0900 Subject: [PATCH 16/58] Avoid overflow in PSNR calculation --- app/oapv_app_util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/oapv_app_util.h b/app/oapv_app_util.h index 7a086ab..5211b85 100644 --- a/app/oapv_app_util.h +++ b/app/oapv_app_util.h @@ -713,7 +713,7 @@ static void measure_psnr(oapv_imgb_t *org, oapv_imgb_t *rec, double psnr[4], int for(j = 0; j < org->h[i]; j++) { for(k = 0; k < org->w[i]; k++) { - sum[i] += (o[k] - r[k]) * (o[k] - r[k]); + sum[i] += (double)(o[k] - r[k]) * (o[k] - r[k]); } o += org->s[i]; @@ -739,7 +739,7 @@ static void measure_psnr(oapv_imgb_t *org, oapv_imgb_t *rec, double psnr[4], int sum[i] += (((int)o[k] - (int)r[k]) >> 6) * (((int)o[k] - (int)r[k]) >> 6); } else { - sum[i] += (o[k] - r[k]) * (o[k] - r[k]); + sum[i] += (double)(o[k] - r[k]) * (o[k] - r[k]); } } o = (unsigned short *)((unsigned char *)o + org->s[i]); From bd2bc31df2de276f44ad3b4ee2fe56d1bf8bb486 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 11:21:12 +0900 Subject: [PATCH 17/58] Clip RDO coefficient candidate to s16 range --- src/oapv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oapv.c b/src/oapv.c index 3874675..f7919f1 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -358,7 +358,7 @@ static double enc_block_rdo_slow(oapve_ctx_t *ctx, oapve_core_t *core, int log2_ } } - s16 test_coef = org_coef + map_idx_diff[i]; + s16 test_coef = (s16)oapv_clip3(-32768, 32767, org_coef + map_idx_diff[i]); coeff[scanp[j]] = test_coef; int step_diff = q_step * map_idx_diff[i]; ctx->fn_itx_adj[0](rec_ups, rec_tmp, j, step_diff, 9); @@ -475,7 +475,7 @@ static double enc_block_rdo_placebo(oapve_ctx_t* ctx, oapve_core_t* core, int lo coef_cur.cost = best_cost; for(int i = 1; i < adj_rng; i++) { s16 test_diff = org_coef == 0 ? (i == 1 ? 1 : -1) : (org_coef > 0 ? i : -i); - s16 test_coef = org_coef + test_diff; + s16 test_coef = (s16)oapv_clip3(-32768, 32767, org_coef + test_diff); oapv_mcpy(coeff, best_coeff, sizeof(s16) * OAPV_BLK_D); coeff[scanp[j]] = test_coef; From adaeeae1c675a2a07b65def0baf9d8534760cc16 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 11:27:13 +0900 Subject: [PATCH 18/58] Widen tile cost accumulator to avoid overflow --- src/oapv_rc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oapv_rc.c b/src/oapv_rc.c index fed8b04..1bdc3e7 100644 --- a/src/oapv_rc.c +++ b/src/oapv_rc.c @@ -33,7 +33,7 @@ int oapve_rc_get_tile_cost(oapve_ctx_t* ctx, oapve_core_t* core, oapve_tile_t* tile) { - int sum = 0; + s64 sum = 0; s16* org = NULL; s16* pic = NULL; int org_s; From 235d8b5060d35d387ca625df1699eb51280f5e4d Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 13:45:32 +0900 Subject: [PATCH 19/58] Clean up only initialized thread objects on error --- src/oapv_tpool.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/oapv_tpool.c b/src/oapv_tpool.c index b0fca8d..275a4aa 100644 --- a/src/oapv_tpool.c +++ b/src/oapv_tpool.c @@ -133,26 +133,26 @@ static oapv_thread_t tpool_create_thread(oapv_tpool_t *tp, int thread_id) // intialize conditional variable and mutexes result = pthread_mutex_init(&tctx->c_section, NULL); if(result) { - goto TERROR; // error handling + goto ERR_FREE; // nothing initialized yet } result = pthread_cond_init(&tctx->w_event, NULL); if(result) { - goto TERROR; + goto ERR_MUTEX; } result = pthread_cond_init(&tctx->r_event, NULL); if(result) { - goto TERROR; + goto ERR_WCOND; } // initialize the worker thread attribute and set the type to joinable result = pthread_attr_init(&tctx->tAttribute); if(result) { - goto TERROR; + goto ERR_RCOND; } result = pthread_attr_setdetachstate(&tctx->tAttribute, PTHREAD_CREATE_JOINABLE); if(result) { - goto TERROR; + goto ERR_ATTR; } tctx->task = NULL; @@ -164,21 +164,27 @@ static oapv_thread_t tpool_create_thread(oapv_tpool_t *tp, int thread_id) // create the worker thread result = pthread_create(&tctx->t_handle, &tctx->tAttribute, tpool_worker_thread, (void *)(tctx)); if(result) { - goto TERROR; + goto ERR_ATTR; } // deinit the attribue pthread_attr_destroy(&tctx->tAttribute); return (oapv_thread_t)tctx; -TERROR: - pthread_mutex_destroy(&tctx->c_section); - pthread_cond_destroy(&tctx->w_event); - pthread_cond_destroy(&tctx->r_event); + // error handling: destroy only the objects that were successfully initialized, + // falling through to release everything created before the failing step +ERR_ATTR: pthread_attr_destroy(&tctx->tAttribute); +ERR_RCOND: + pthread_cond_destroy(&tctx->r_event); +ERR_WCOND: + pthread_cond_destroy(&tctx->w_event); +ERR_MUTEX: + pthread_mutex_destroy(&tctx->c_section); +ERR_FREE: free(tctx); - return NULL; // error handling, can't create a worker thread with proper initialization + return NULL; // can't create a worker thread with proper initialization } static tpool_result_t tpool_assign_task(oapv_thread_t thread_id, oapv_fn_thread_entry_t entry, void *arg) From 25e881a12e8649a7abe066f9e5334fd31726e0d3 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 15:08:44 +0900 Subject: [PATCH 20/58] Validate bitrate and fps parameter strings --- src/oapv_param.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index 81b5022..e03655c 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -95,6 +95,7 @@ static int kbps_str_to_int(const char *str) char *s = (char *)str; if(strchr(s, 'K') || strchr(s, 'k')) { char *tmp = strtok(s, "Kk "); + if(tmp == NULL) return -1; char *endptr; float fval; errno = 0; @@ -106,6 +107,7 @@ static int kbps_str_to_int(const char *str) } else if(strchr(s, 'M') || strchr(s, 'm')) { char *tmp = strtok(s, "Mm "); + if(tmp == NULL) return -1; char *endptr; float fval; errno = 0; @@ -117,6 +119,7 @@ static int kbps_str_to_int(const char *str) } else if(strchr(s, 'G') || strchr(s, 'g')) { char *tmp = strtok(s, "Gg "); + if(tmp == NULL) return -1; char *endptr; float fval; errno = 0; @@ -248,7 +251,10 @@ int oapve_param_parse(oapve_param_t *param, const char *name, const char *value } NAME_CMP("fps") { if(strpbrk(value, "/") != NULL) { - sscanf(value, "%d/%d", ¶m->fps_num, ¶m->fps_den); + if(sscanf(value, "%d/%d", ¶m->fps_num, ¶m->fps_den) != 2 || + param->fps_num <= 0 || param->fps_den <= 0) { + return OAPV_ERR_INVALID_ARGUMENT; + } } else if(strpbrk(value, ".") != NULL) { GET_FLOAT_OR_ERR(value, tf0, OAPV_ERR_INVALID_ARGUMENT); From bbd3ed36df59bafeaddda54010466344797747d5 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 15:08:44 +0900 Subject: [PATCH 21/58] Discard non-finite rate-control updates --- src/oapv_rc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/oapv_rc.c b/src/oapv_rc.c index 1bdc3e7..c86570a 100644 --- a/src/oapv_rc.c +++ b/src/oapv_rc.c @@ -214,11 +214,17 @@ void oapve_rc_update_after_pic(oapve_ctx_t* ctx, double cost) double diff_lambda = (ctx->rc_param.beta) * (log((double)total_bits) - log(((double)ctx->param->bitrate * 1000 / ((double)ctx->param->fps_num / ctx->param->fps_den)))); diff_lambda = oapv_clip3(-0.125, 0.125, 0.25 * diff_lambda); - ctx->rc_param.alpha = (ctx->rc_param.alpha) * exp(diff_lambda); - ctx->rc_param.beta = (ctx->rc_param.beta) + diff_lambda / ln_bpp; + double alpha_new = (ctx->rc_param.alpha) * exp(diff_lambda); + double beta_new = (ctx->rc_param.beta) + diff_lambda / ln_bpp; - ctx->rc_param.alpha = oapv_clip3(0.05, 500, ctx->rc_param.alpha); - ctx->rc_param.beta = oapv_clip3(0.1, 3, ctx->rc_param.beta); + alpha_new = oapv_clip3(0.05, 500, alpha_new); + beta_new = oapv_clip3(0.1, 3, beta_new); + + // discard non-finite results (NaN/Inf) so they don't corrupt persistent RC state + if(isfinite(alpha_new) && isfinite(beta_new)) { + ctx->rc_param.alpha = alpha_new; + ctx->rc_param.beta = beta_new; + } } ctx->rc_param.is_updated = 1; } From 49a2ae399f80965792dde9385e255aea5d729c4f Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 15:38:52 +0900 Subject: [PATCH 22/58] Bound tile size to picture dimensions --- src/oapv_param.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/oapv_param.c b/src/oapv_param.c index e03655c..1b3628a 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -507,6 +507,7 @@ static int enc_update_param_tile(oapve_ctx_t* ctx, oapve_param_t* param) int tile_w, tile_h; oapv_assert_rv(param->tile_w >= OAPV_MIN_TILE_W && param->tile_h >= OAPV_MIN_TILE_H, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(param->tile_w <= ctx->w && param->tile_h <= ctx->h, OAPV_ERR_INVALID_ARGUMENT); oapv_assert_rv((param->tile_w & (OAPV_MB_W - 1)) == 0 && (param->tile_h & (OAPV_MB_H - 1)) == 0, OAPV_ERR_INVALID_ARGUMENT); if (oapv_div_round_up(ctx->w, param->tile_w) > OAPV_MAX_TILE_COLS) { From 495b446cd99e3aefe16d70f3bc71972923f8a3ec Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 16:01:18 +0900 Subject: [PATCH 23/58] Validate frame dimensions right after parsing --- app/oapv_app_enc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c index 36540bd..013906a 100644 --- a/app/oapv_app_enc.c +++ b/app/oapv_app_enc.c @@ -719,6 +719,11 @@ static int update_param(args_var_t *vars, oapve_param_t *param) UPDATE_A_PARAM_W_KEY_VAL(param, "width", vars->width); UPDATE_A_PARAM_W_KEY_VAL(param, "height", vars->height); + if(param->w <= 0 || param->w > IMGB_MAX_W || param->h <= 0 || param->h > IMGB_MAX_H) { + logerr("ERR: frame dimensions %dx%d out of range (max %dx%d)\n", + param->w, param->h, IMGB_MAX_W, IMGB_MAX_H); + return -1; + } UPDATE_A_PARAM_W_KEY_VAL(param, "fps", vars->fps); UPDATE_A_PARAM_W_KEY_VAL(param, "qp", vars->qp); @@ -1117,13 +1122,6 @@ int main(int argc, const char **argv) goto ERR; } - if(param->w <= 0 || param->w > IMGB_MAX_W || param->h <= 0 || param->h > IMGB_MAX_H) { - logerr("ERR: frame dimensions %dx%d out of range (max %dx%d)\n", - param->w, param->h, IMGB_MAX_W, IMGB_MAX_H); - ret = -1; - goto ERR; - } - for(int i = 0; i < num_frames; i++) { if(args_var->input_depth == codec_depth) { ifrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); From 6e59b6188af5a9563bac51211d4509854dcbfb85 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Wed, 8 Jul 2026 16:39:43 +0900 Subject: [PATCH 24/58] Sanitize lambda before QP conversion in rate control --- src/oapv_rc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/oapv_rc.c b/src/oapv_rc.c index c86570a..4e42579 100644 --- a/src/oapv_rc.c +++ b/src/oapv_rc.c @@ -189,6 +189,9 @@ void oapve_rc_get_qp(oapve_ctx_t* ctx, oapve_tile_t* tile, int frame_qp, int* qp double min_lambda = exp(((double)(min_qp - 0.49) - 13.7122) / 4.2005); const int LAMBDA_PREC = 1000000; + if(!isfinite(est_lambda)) { + est_lambda = min_lambda; + } est_lambda = oapv_clip3(min_lambda, max_lambda, est_lambda); est_lambda = (double)((s64)(est_lambda * (double)LAMBDA_PREC + 0.5)) / (double)LAMBDA_PREC; *qp = (int)(4.2005 * log(est_lambda) + 13.7122 + 0.5); From 4129be36a5819b3898458e456a829e30a06494e3 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Thu, 9 Jul 2026 10:27:34 +0900 Subject: [PATCH 25/58] Validate caller buffer allocation before padding --- src/oapv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/oapv.c b/src/oapv.c index f7919f1..d41d24b 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -919,6 +919,20 @@ static int enc_frm_prepare(oapve_ctx_t *ctx, oapve_param_t *param, oapv_imgb_t * } ctx->fn_imgb_pad = imgb_pad; } + + // reject caller buffers too small for the aligned padding extents + int is_planar2 = (OAPV_CS_GET_FORMAT(imgb_i->cs) == OAPV_CF_PLANAR2); + for(int c = 0; c < imgb_i->np; c++) { + int need_w = ctx->w >> (is_planar2 ? 0 : ctx->c_sft[c][0]); + int need_h = ctx->h >> (is_planar2 ? 0 : ctx->c_sft[c][1]); + if((s64)imgb_i->s[c] < (s64)need_w * (int)sizeof(pel)) { + return OAPV_ERR_INVALID_WIDTH; + } + if((s64)imgb_i->bsize[c] < (s64)need_h * imgb_i->s[c]) { + return OAPV_ERR_INVALID_HEIGHT; + } + } + // padding input picture, if needs ctx->fn_imgb_pad(imgb_i, ctx->w, ctx->h, ctx->c_sft); From 4ee2dd9047e046d806842356c231c367f0a74f91 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Thu, 9 Jul 2026 11:07:32 +0900 Subject: [PATCH 26/58] Add imgb validation and return error from md5 helper --- src/oapv.c | 4 ++-- src/oapv_util.c | 38 +++++++++++++++++++++++++++++++++----- src/oapv_util.h | 3 ++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/oapv.c b/src/oapv.c index d41d24b..8e3599c 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1966,7 +1966,7 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid /* READ FILLER HERE !!! */ if(OAPV_SUCCEEDED(ret) && ctx->use_frm_hash) { - oapv_imgb_set_md5(ctx->imgb); + ret = oapv_imgb_set_md5(ctx->imgb); } else { oapv_imgb_clr_md5(ctx->imgb); @@ -2237,7 +2237,7 @@ int oapvd_decode_frame(oapvd_t did, oapv_bitb_t *bitb, oapv_imgb_t *imgb, oapvd_ /* READ FILLER HERE !!! */ if(OAPV_SUCCEEDED(ret) && ctx->use_frm_hash) { - oapv_imgb_set_md5(imgb); + ret = oapv_imgb_set_md5(imgb); } else { oapv_imgb_clr_md5(imgb); diff --git a/src/oapv_util.c b/src/oapv_util.c index 708f7b5..e2b1f08 100644 --- a/src/oapv_util.c +++ b/src/oapv_util.c @@ -258,12 +258,38 @@ static unsigned char uuid_frm_hash[16] = { 0x98, 0x0d, 0x9b, 0x9e, 0x39, 0x20, 0x28, 0x49 }; -void oapv_imgb_set_md5(oapv_imgb_t *imgb) +// checks imgb self-consistency; returns OAPV_OK or a specific error code +int oapv_imgb_valid(oapv_imgb_t *imgb) { + if(imgb == NULL) { + return OAPV_ERR_INVALID_ARGUMENT; + } + if(imgb->np < 1 || imgb->np > OAPV_MAX_CC) { + return OAPV_ERR_INVALID_ARGUMENT; + } + for(int i = 0; i < imgb->np; i++) { + if(imgb->a[i] == NULL) { + return OAPV_ERR_INVALID_ARGUMENT; + } + if(imgb->aw[i] < 0 || (s64)imgb->s[i] < (s64)imgb->aw[i] * 2) { + return OAPV_ERR_INVALID_WIDTH; + } + if(imgb->ah[i] < 0 || (s64)imgb->bsize[i] < (s64)imgb->ah[i] * imgb->s[i]) { + return OAPV_ERR_INVALID_HEIGHT; + } + } + return OAPV_OK; +} +int oapv_imgb_set_md5(oapv_imgb_t *imgb) +{ oapv_md5_t md5[N_C]; - int i, j; - oapv_assert(imgb != NULL); + int i, j, ret; + + ret = oapv_imgb_valid(imgb); + if(OAPV_FAILED(ret)) { + return ret; + } memset(imgb->hash, 0, sizeof(imgb->hash)); for(i = 0; i < imgb->np; i++) { @@ -275,6 +301,7 @@ void oapv_imgb_set_md5(oapv_imgb_t *imgb) md5_finish(&md5[i], imgb->hash[i]); } + return OAPV_OK; } void oapv_imgb_clr_md5(oapv_imgb_t *imgb) @@ -284,14 +311,15 @@ void oapv_imgb_clr_md5(oapv_imgb_t *imgb) int oapv_set_md5_pld(oapvm_t mid, int group_id, oapv_imgb_t *rec) { - oapv_imgb_set_md5(rec); + int ret = oapv_imgb_set_md5(rec); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); u8 *mdp_data = oapv_malloc((16 * rec->np) + 16); oapv_assert_rv(mdp_data != NULL, OAPV_ERR_OUT_OF_MEMORY); memcpy(mdp_data, uuid_frm_hash, 16); for(int i = 0; i < rec->np; i++) { memcpy(mdp_data + ((i + 1) * 16), rec->hash[i], 16); } - int ret = oapvm_set(mid, group_id, OAPV_METADATA_USER_DEFINED, mdp_data, 16 * rec->np + 16); + ret = oapvm_set(mid, group_id, OAPV_METADATA_USER_DEFINED, mdp_data, 16 * rec->np + 16); oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); oapv_mfree(mdp_data); return OAPV_OK; diff --git a/src/oapv_util.h b/src/oapv_util.h index 45d3e3a..0ca3c53 100644 --- a/src/oapv_util.h +++ b/src/oapv_util.h @@ -171,7 +171,8 @@ typedef struct } oapv_md5_t; /* MD5 Functions */ -void oapv_imgb_set_md5(oapv_imgb_t *imgb); +int oapv_imgb_valid(oapv_imgb_t *imgb); +int oapv_imgb_set_md5(oapv_imgb_t *imgb); void oapv_imgb_clr_md5(oapv_imgb_t *imgb); void oapv_block_copy(s16 *src, int src_stride, s16 *dst, int dst_stride, int log2_copy_w, int log2_copy_h); From 514bb9e30088eea0f748788c1740967cfaf8e336 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Thu, 9 Jul 2026 15:38:54 +0900 Subject: [PATCH 27/58] Rename imgb validator to oapv_imgb_is_valid --- src/oapv_util.c | 4 ++-- src/oapv_util.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/oapv_util.c b/src/oapv_util.c index e2b1f08..c49e299 100644 --- a/src/oapv_util.c +++ b/src/oapv_util.c @@ -259,7 +259,7 @@ static unsigned char uuid_frm_hash[16] = { }; // checks imgb self-consistency; returns OAPV_OK or a specific error code -int oapv_imgb_valid(oapv_imgb_t *imgb) +int oapv_imgb_is_valid(oapv_imgb_t *imgb) { if(imgb == NULL) { return OAPV_ERR_INVALID_ARGUMENT; @@ -286,7 +286,7 @@ int oapv_imgb_set_md5(oapv_imgb_t *imgb) oapv_md5_t md5[N_C]; int i, j, ret; - ret = oapv_imgb_valid(imgb); + ret = oapv_imgb_is_valid(imgb); if(OAPV_FAILED(ret)) { return ret; } diff --git a/src/oapv_util.h b/src/oapv_util.h index 0ca3c53..c33fa12 100644 --- a/src/oapv_util.h +++ b/src/oapv_util.h @@ -171,7 +171,7 @@ typedef struct } oapv_md5_t; /* MD5 Functions */ -int oapv_imgb_valid(oapv_imgb_t *imgb); +int oapv_imgb_is_valid(oapv_imgb_t *imgb); int oapv_imgb_set_md5(oapv_imgb_t *imgb); void oapv_imgb_clr_md5(oapv_imgb_t *imgb); From d8aaf2ab93aefc17971659578c981d6458b7448d Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Thu, 9 Jul 2026 15:38:54 +0900 Subject: [PATCH 28/58] Validate image buffers before copy --- app/oapv_app_util.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/oapv_app_util.h b/app/oapv_app_util.h index 5211b85..f0ebdcb 100644 --- a/app/oapv_app_util.h +++ b/app/oapv_app_util.h @@ -532,6 +532,35 @@ static int imgb_write(char *fname, oapv_imgb_t *imgb) return 0; } +// checks src/dst are compatible and both buffers hold the copy extent +static int imgb_cpy_is_valid(oapv_imgb_t *dst, oapv_imgb_t *src) +{ + if(src == NULL || dst == NULL) { + logerr("ERR: null image buffer in image copy\n"); + return 0; + } + if(src->np < 1 || src->np > OAPV_MAX_CC || dst->np != src->np) { + logerr("ERR: mismatched plane count in image copy\n"); + return 0; + } + int src_bytes = OAPV_CS_GET_BYTE_DEPTH(src->cs); + int dst_bytes = OAPV_CS_GET_BYTE_DEPTH(dst->cs); + for(int i = 0; i < src->np; i++) { + int w = src->w[i] > src->aw[i] ? src->w[i] : src->aw[i]; + int h = src->h[i] > src->ah[i] ? src->h[i] : src->ah[i]; + if(src->a[i] == NULL || dst->a[i] == NULL || w < 0 || h < 0) { + logerr("ERR: invalid plane in image copy\n"); + return 0; + } + if((long long)(h - 1) * src->s[i] + (long long)w * src_bytes > src->bsize[i] || + (long long)(h - 1) * dst->s[i] + (long long)w * dst_bytes > dst->bsize[i]) { + logerr("ERR: buffer too small in image copy\n"); + return 0; + } + } + return 1; +} + static void imgb_cpy_plane(oapv_imgb_t *dst, oapv_imgb_t *src) { int i, j; @@ -654,6 +683,9 @@ static void imgb_cpy_shift_right(oapv_imgb_t *dst, oapv_imgb_t *src, int shift) static void imgb_cpy(oapv_imgb_t *dst, oapv_imgb_t *src) { int i, bd_src, bd_dst; + + if(!imgb_cpy_is_valid(dst, src)) return; + bd_src = OAPV_CS_GET_BIT_DEPTH(src->cs); bd_dst = OAPV_CS_GET_BIT_DEPTH(dst->cs); From 6796e80598898c963e66834408ac45e4fc2e5e82 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Thu, 9 Jul 2026 18:04:35 +0900 Subject: [PATCH 29/58] Bound VLC bitstream reads against buffer end The reader flush now checks the buffer end, costing ~4% decoder speed, but is applied for stability against out-of-bounds reads on malformed bitstreams. --- src/oapv_vlc.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 0a6deac..82fa64c 100644 --- a/src/oapv_vlc.c +++ b/src/oapv_vlc.c @@ -631,9 +631,22 @@ int oapve_vlc_get_coef_rate(oapve_core_t* core, s16* coef, int c) // start of decoder code #if ENABLE_DECODER /////////////////////////////////////////////////////////////////////////////// -#define BSR_FLUSH_1BYTE(bs) { \ - (bs)->code = ((u64)(*((bs)->cur++))) << 56; \ - (bs)->leftbits = 8; \ +// Refill the bit buffer with one byte. On buffer end, do NOT read past it: +// set is_eob and fill 'code' with all 1-bits on purpose. The VLC exp-golomb +// loops terminate on a 1-bit, so 1-bits make them exit at once instead of +// spinning on 0-bits and reading further past the end. Callers see is_eob +// (BSR_IS_UNEXPECTED_EOB) and return OAPV_ERR_MALFORMED_BITSTREAM, so the +// garbage value produced here is discarded. +#define BSR_FLUSH_1BYTE(bs) { \ + if((bs)->cur < (bs)->end) { \ + (bs)->code = ((u64)(*((bs)->cur++))) << 56; \ + (bs)->leftbits = 8; \ + } \ + else { \ + (bs)->code = ~(u64)0; \ + (bs)->leftbits = 8; \ + (bs)->is_eob = 1; \ + } \ } #define BSR_READ_1BIT(bs, bit) { \ @@ -825,6 +838,8 @@ int oapvd_vlc_dc_coef(oapv_bs_t *bs, int *dc_diff, int *kparam_dc) int sign; abs_dc_diff = dec_vlc_read(bs, *kparam_dc); + if(abs_dc_diff < 0) // dec_vlc_read error sentinel + return OAPV_ERR_MALFORMED_BITSTREAM; if(abs_dc_diff) { if(bs->leftbits == 0) BSR_FLUSH_1BYTE(bs); BSR_READ_1BIT(bs, sign); From 257ce7a43218a87e38fbfae7c3ad264f7745406e Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Thu, 9 Jul 2026 18:22:15 +0900 Subject: [PATCH 30/58] Guard against failed image buffer allocation in app --- app/oapv_app_enc.c | 4 ++++ app/oapv_app_util.h | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c index 013906a..99387c7 100644 --- a/app/oapv_app_enc.c +++ b/app/oapv_app_enc.c @@ -1132,9 +1132,11 @@ int main(int argc, const char **argv) } else { imgb_r = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); + if(imgb_r == NULL) { ret = -1; goto ERR; } ifrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, codec_depth, 0)); } } + if(ifrms.frm[i].imgb == NULL) { ret = -1; goto ERR; } if(is_rec) { if(args_var->input_depth == codec_depth) { @@ -1147,9 +1149,11 @@ int main(int argc, const char **argv) else { imgb_w = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, args_var->input_depth, 0)); + if(imgb_w == NULL) { ret = -1; goto ERR; } rfrms.frm[i].imgb = imgb_create(param->w, param->h, OAPV_CS_SET(cfmt, codec_depth, 0)); } } + if(rfrms.frm[i].imgb == NULL) { ret = -1; goto ERR; } rfrms.num_frms++; } ifrms.num_frms++; diff --git a/app/oapv_app_util.h b/app/oapv_app_util.h index f0ebdcb..2d2192f 100644 --- a/app/oapv_app_util.h +++ b/app/oapv_app_util.h @@ -493,10 +493,16 @@ static int imgb_write(char *fname, oapv_imgb_t *imgb) { unsigned char *p8; int i, j, bd; + int chroma_format, bit_depth; FILE *fp; - int chroma_format = OAPV_CS_GET_FORMAT(imgb->cs); - int bit_depth = OAPV_CS_GET_BIT_DEPTH(imgb->cs); + if(imgb == NULL) { + logerr("ERR: null image buffer in image write\n"); + return -1; + } + + chroma_format = OAPV_CS_GET_FORMAT(imgb->cs); + bit_depth = OAPV_CS_GET_BIT_DEPTH(imgb->cs); fp = fopen(fname, "ab"); if(fp == NULL) { From 42c5fd81beccfbe29fcc86e4ceeab9ce5ff99d93 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 09:07:32 +0900 Subject: [PATCH 31/58] Initialize and validate Y4M header parameters --- app/oapv_app_enc.c | 2 +- app/oapv_app_y4m.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c index 99387c7..161763d 100644 --- a/app/oapv_app_enc.c +++ b/app/oapv_app_enc.c @@ -309,7 +309,7 @@ typedef struct args_var { char width[16]; char height[16]; - char fps[16]; + char fps[24]; char qp[16]; char qp_offset_c1[16]; diff --git a/app/oapv_app_y4m.h b/app/oapv_app_y4m.h index b85164a..07835b3 100644 --- a/app/oapv_app_y4m.h +++ b/app/oapv_app_y4m.h @@ -199,6 +199,7 @@ int y4m_header_parser(FILE *ip_y4m, y4m_params_t *y4m) int ret; int i; + memset(y4m, 0, sizeof(*y4m)); memset(buffer, 0, sizeof(char) * head_size); /*Read until newline, or 128 cols, whichever happens first.*/ @@ -227,6 +228,10 @@ int y4m_header_parser(FILE *ip_y4m, y4m_params_t *y4m) logerr("ERR: parsing YUV4MPEG2 header.\n"); return ret; } + if(y4m->w <= 0 || y4m->h <= 0 || y4m->fps_num <= 0 || y4m->fps_den <= 0) { + logerr("ERR: Y4M header missing required width/height/frame-rate.\n"); + return -1; + } return 0; } From e2421723ab682cb34905b69d2ca491e15342e52e Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 09:44:30 +0900 Subject: [PATCH 32/58] Fix bitstream read shift overflow and set decoder bitb size --- app/oapv_app_dec.c | 2 ++ src/oapv_bs.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/oapv_app_dec.c b/app/oapv_app_dec.c index 245090b..cd7836e 100644 --- a/app/oapv_app_dec.c +++ b/app/oapv_app_dec.c @@ -564,6 +564,7 @@ int dec_api_set_0(args_var_t *args_var, FILE *fp_bs, int is_y4m) /* main decoding block */ bitb.addr = bs_buf; + bitb.bsize = MAX_BS_BUF; bitb.ssize = bs_buf_size; memset(&stat, 0, sizeof(oapvd_stat_t)); @@ -953,6 +954,7 @@ int dec_api_set_1(args_var_t *args_var, FILE *fp_bs, int is_y4m) // start to decode a frame bitb.addr = pbu; + bitb.bsize = pbu_size; bitb.ssize = pbu_size; clk_beg = oapv_clk_get(); diff --git a/src/oapv_bs.c b/src/oapv_bs.c index 25545bf..ee9f42f 100644 --- a/src/oapv_bs.c +++ b/src/oapv_bs.c @@ -293,7 +293,7 @@ int oapv_bsr_read_direct(const void *addr, int len, u32 *out) return OAPV_ERR_INVALID_ARGUMENT; while(byte) { - code |= *(p) << shift; + code |= (u32)(*(p)) << shift; shift -= 8; byte--; p++; From fae622b62e95a6f3666b03683404fbfaa020b0d7 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 11:32:05 +0900 Subject: [PATCH 33/58] Reject decoder stream size larger than buffer size --- src/oapv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/oapv.c b/src/oapv.c index 8e3599c..3d1cb88 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1906,6 +1906,9 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid // read signature ('aPv1') oapv_assert_rv(bitb->ssize > 4, OAPV_ERR_MALFORMED_BITSTREAM); + if(bitb->bsize > 0) { + oapv_assert_rv(bitb->ssize <= bitb->bsize, OAPV_ERR_INVALID_ARGUMENT); + } ret = oapv_bsr_read_direct(bitb->addr, 32, &signature); oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); oapv_assert_rv(signature == 0x61507631, OAPV_ERR_MALFORMED_BITSTREAM); @@ -2191,6 +2194,9 @@ int oapvd_decode_frame(oapvd_t did, oapv_bitb_t *bitb, oapv_imgb_t *imgb, oapvd_ oapv_bs_t *bs; oapv_assert_gv((bitb->ssize >= 8), ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); + if(bitb->bsize > 0) { + oapv_assert_gv((bitb->ssize <= bitb->bsize), ret, OAPV_ERR_INVALID_ARGUMENT, ERR); + } oapv_bsr_init(&ctx->bs, (u8 *)bitb->addr, bitb->ssize, NULL); bs = &ctx->bs; @@ -2270,6 +2276,9 @@ int oapvd_decode_auinfo(oapvd_t did, oapv_bitb_t *bitb, oapv_au_info_t *aui) oapv_bs_t bs; oapv_aui_t ai; + if(bitb->bsize > 0) { + oapv_assert_rv(bitb->ssize <= bitb->bsize, OAPV_ERR_INVALID_ARGUMENT); + } oapv_bsr_init(&bs, bitb->addr, bitb->ssize, NULL); DUMP_SET(0); From 611f14660c3c93511ce76f3bc0cef5b4a2b36cbc Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 11:36:59 +0900 Subject: [PATCH 34/58] Bound tile-size read and reject undersized PBU --- src/oapv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oapv.c b/src/oapv.c index 3d1cb88..80c1683 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1690,6 +1690,7 @@ static int dec_thread_tile(void *arg) oapv_tpool_leave_cs(ctx->sync_obj); } /* read tile size */ + oapv_assert_gv(tile[tidx].bs_beg + OAPV_TILE_SIZE_LEN <= ctx->bs.end, ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); oapv_bsr_init(&bs, tile[tidx].bs_beg, OAPV_TILE_SIZE_LEN, NULL); ret = oapvd_vlc_tile_size(&bs, &tile[tidx].tile_size); oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); @@ -1926,7 +1927,7 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid ret = oapvd_vlc_pbu_size(bs, &pbu_size); // read pbu_size (4 byte) oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); remain -= 4; // size of pbu_size syntax - oapv_assert_gv(pbu_size <= remain, ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); + oapv_assert_gv(pbu_size >= 4 && pbu_size <= remain, ret, OAPV_ERR_MALFORMED_BITSTREAM, ERR); ret = oapvd_vlc_pbu_header(bs, &pbuh); oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); From 85dba81f051f8f6fc92f621a45b1f32626835b69 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 13:26:34 +0900 Subject: [PATCH 35/58] Reject NULL decode arguments --- src/oapv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oapv.c b/src/oapv.c index 80c1683..6fb3ab9 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1903,6 +1903,8 @@ int oapvd_decode(oapvd_t did, oapv_bitb_t *bitb, oapv_frms_t *ofrms, oapvm_t mid ctx = dec_id_to_ctx(did); oapv_assert_rv(ctx, OAPV_ERR_INVALID_ARGUMENT); + // required in/out pointers must be valid (mid is optional) + oapv_assert_rv(bitb && bitb->addr && ofrms && stat, OAPV_ERR_INVALID_ARGUMENT); oapv_mset(stat, 0, sizeof(oapvd_stat_t)); // read signature ('aPv1') From bf429c772268bcc45980c6bdb6ac0f4a0ba68c43 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 13:35:30 +0900 Subject: [PATCH 36/58] Validate reconstruction buffer size --- src/oapv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/oapv.c b/src/oapv.c index 6fb3ab9..c88f446 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -959,7 +959,13 @@ static int enc_frm_prepare(oapve_ctx_t *ctx, oapve_param_t *param, oapv_imgb_t * imgb_r->h[c] = imgb_i->h[c]; imgb_r->x[c] = imgb_i->x[c]; imgb_r->y[c] = imgb_i->y[c]; + // recon plane spans the aligned frame extents + imgb_r->aw[c] = ctx->w >> (is_planar2 ? 0 : ctx->c_sft[c][0]); + imgb_r->ah[c] = ctx->h >> (is_planar2 ? 0 : ctx->c_sft[c][1]); } + // reject recon buffers too small for the reconstruction picture + ret = oapv_imgb_is_valid(imgb_r); + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); ctx->imgb_r = imgb_r; imgb_addref(ctx->imgb_r); } From f0290f16aa531bd104c0dfadf85dca7d70d17b25 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 13:49:21 +0900 Subject: [PATCH 37/58] Reject output buffer too small for frame header --- src/oapv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/oapv.c b/src/oapv.c index c88f446..92178c7 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1001,6 +1001,8 @@ static int enc_frame(oapve_ctx_t *ctx, oapv_bs_t *bs) oapve_vlc_frame_header(bs, ctx, &ctx->fh); u8 *bs_tile_pos = oapv_bsw_sink(bs); + // sink returns NULL when the output buffer cannot hold the header + oapv_assert_gv(bs_tile_pos != NULL, ret, OAPV_ERR_OUT_OF_BS_BUF, ERR); /* rc init */ u64 cost_sum = 0; From a3376306e938433ae85ce5106eecb5f5a70c72ed Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 14:04:41 +0900 Subject: [PATCH 38/58] Reject out-of-range frame dimensions in param update --- src/oapv_param.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/oapv_param.c b/src/oapv_param.c index 1b3628a..a10d5fe 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -499,6 +499,10 @@ static int enc_update_param_bitrate(oapve_param_t* param) static int enc_update_param_tile(oapve_ctx_t* ctx, oapve_param_t* param) { + /* frame dimensions must fit the 24-bit frame_info fields */ + oapv_assert_rv(param->w > 0 && param->w <= ((1 << 24) - 1), OAPV_ERR_INVALID_WIDTH); + oapv_assert_rv(param->h > 0 && param->h <= ((1 << 24) - 1), OAPV_ERR_INVALID_HEIGHT); + /* set various value */ ctx->w = oapv_div_round_up(param->w, OAPV_MB_W) * OAPV_MB_W; ctx->h = oapv_div_round_up(param->h, OAPV_MB_H) * OAPV_MB_H; From 841a7e649fb36737439018e879117369c1f342e9 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 14:34:07 +0900 Subject: [PATCH 39/58] Bound exp-golomb k in 1-bit VLC read --- src/oapv_vlc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 82fa64c..553f6c6 100644 --- a/src/oapv_vlc.c +++ b/src/oapv_vlc.c @@ -714,6 +714,9 @@ static int dec_vlc_read_1bit_read(oapv_bs_t *bs) } } } + + oapv_assert_rv(k < 32, -1); /* prevent too large (impossible) k value */ + if(k > 0) { symbol += ((u32)0xFFFFFFFF) >> (32 - k); From cb518ce970797b3f2afa567df5cb1e672f4e8961 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 19:27:09 +0900 Subject: [PATCH 40/58] Validate param parse inputs and guard fps divisor --- src/oapv_param.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index a10d5fe..5ee828c 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -185,6 +185,10 @@ int oapve_param_parse(oapve_param_t *param, const char *name, const char *value int ti0; float tf0; + if(param == NULL || name == NULL || value == NULL) { + return OAPV_ERR_INVALID_ARGUMENT; + } + /* normalization of name and value ***************************************/ // pass '-- prefix' if exists if(name[0] == '-' && name[1] == '-') { name += 2; } @@ -251,18 +255,28 @@ int oapve_param_parse(oapve_param_t *param, const char *name, const char *value } NAME_CMP("fps") { if(strpbrk(value, "/") != NULL) { - if(sscanf(value, "%d/%d", ¶m->fps_num, ¶m->fps_den) != 2 || - param->fps_num <= 0 || param->fps_den <= 0) { + // parse into locals first so a bad value leaves param unchanged + int num, den; + if(sscanf(value, "%d/%d", &num, &den) != 2 || num <= 0 || den <= 0) { return OAPV_ERR_INVALID_ARGUMENT; } + param->fps_num = num; + param->fps_den = den; } else if(strpbrk(value, ".") != NULL) { GET_FLOAT_OR_ERR(value, tf0, OAPV_ERR_INVALID_ARGUMENT); - param->fps_num = tf0 * 10000; + // reject values that would overflow the fixed-point fps_num + if(!(tf0 > 0.0f) || tf0 > (float)INT_MAX / 10000.0f) { + return OAPV_ERR_INVALID_ARGUMENT; + } + param->fps_num = (int)(tf0 * 10000); param->fps_den = 10000; } else { GET_INTEGER_OR_ERR(value, ti0, OAPV_ERR_INVALID_ARGUMENT); + if(ti0 <= 0) { + return OAPV_ERR_INVALID_ARGUMENT; + } param->fps_num = ti0; param->fps_den = 1; } @@ -426,7 +440,8 @@ static int enc_update_param_level_band(oapve_param_t* param) { int w = oapv_div_round_up(param->w, OAPV_MB_W) * OAPV_MB_W; int h = oapv_div_round_up(param->h, OAPV_MB_H) * OAPV_MB_H; - double fps = (double)param->fps_num / param->fps_den; + // frame rate is optional (unused without rate control); guard the divisor + double fps = (param->fps_den > 0) ? (double)param->fps_num / param->fps_den : 0.0; u64 luma_sample_rate = (int)((double)w * h * fps); int min_level_idx = 0; for (int i = 0 ; i < MAX_LEVEL_NUM ; i++) { From c17a4b37e1da339f9623ba41e1b8c3d3ce3cd5a9 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 19:35:13 +0900 Subject: [PATCH 41/58] Require frame rate only when used and add fps error code --- inc/oapv.h | 1 + src/oapv_param.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/inc/oapv.h b/inc/oapv.h index 4b71222..5726b20 100644 --- a/inc/oapv.h +++ b/inc/oapv.h @@ -111,6 +111,7 @@ extern "C" { #define OAPV_ERR_INVALID_LEVEL (-401) #define OAPV_ERR_INVALID_WIDTH (-405) /* invalid width (like odd) */ #define OAPV_ERR_INVALID_HEIGHT (-406) +#define OAPV_ERR_INVALID_FPS (-407) /* invalid or missing frame rate */ #define OAPV_ERR_INVALID_QP (-410) #define OAPV_ERR_INVALID_FAMILY (-501) /* invalid family number */ #define OAPV_ERR_UNKNOWN (-32767) /* unknown error */ diff --git a/src/oapv_param.c b/src/oapv_param.c index 5ee828c..a6178ad 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -440,7 +440,12 @@ static int enc_update_param_level_band(oapve_param_t* param) { int w = oapv_div_round_up(param->w, OAPV_MB_W) * OAPV_MB_W; int h = oapv_div_round_up(param->h, OAPV_MB_H) * OAPV_MB_H; - // frame rate is optional (unused without rate control); guard the divisor + // fps is required for rate control (ABR) and for automatic level selection; + // it stays optional for fixed-QP with an explicit level + if(param->rc_type != OAPV_RC_CQP || param->level_idc == OAPVE_PARAM_LEVEL_IDC_AUTO) { + oapv_assert_rv(param->fps_num > 0 && param->fps_den > 0, OAPV_ERR_INVALID_FPS); + } + // otherwise fps is optional; guard the divisor when it is left unset double fps = (param->fps_den > 0) ? (double)param->fps_num / param->fps_den : 0.0; u64 luma_sample_rate = (int)((double)w * h * fps); int min_level_idx = 0; From a0d268b76700b18777bc92c0512161df29542fe0 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 19:48:20 +0900 Subject: [PATCH 42/58] Guard metadata write against full output buffer --- src/oapv.c | 8 ++++++-- src/oapv_vlc.c | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/oapv.c b/src/oapv.c index 92178c7..8e90d43 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1245,13 +1245,17 @@ int oapve_encode(oapve_t eid, oapv_frms_t *ifrms, oapvm_t mid, oapv_bitb_t *bitb for(i = 0; i < num_md; i++) { int group_id = md_list->md_arr[i].group_id; bs_pos_pbu_beg = oapv_bsw_sink(bs); /* store pbu pos to calculate size */ + oapv_assert_rv(bs_pos_pbu_beg != NULL, OAPV_ERR_OUT_OF_BS_BUF); DUMP_SAVE(0); oapv_bsw_write(bs, 0, 32); /* skip pbu_size syntax (later re-write) */ oapve_vlc_pbu_header(bs, OAPV_PBU_TYPE_METADATA, group_id); - oapve_vlc_metadata(&md_list->md_arr[i], bs); + ret = oapve_vlc_metadata(&md_list->md_arr[i], bs); + oapv_assert_rv(ret == OAPV_OK, ret); // rewrite pbu_size - int pbu_size = ((u8 *)oapv_bsw_sink(bs)) - bs_pos_pbu_beg - 4; + u8 *bs_pos_pbu_end = oapv_bsw_sink(bs); + oapv_assert_rv(bs_pos_pbu_end != NULL, OAPV_ERR_OUT_OF_BS_BUF); + int pbu_size = (bs_pos_pbu_end - bs_pos_pbu_beg) - 4; DUMP_SAVE(1); DUMP_LOAD(0); oapv_bsw_write_direct(bs_pos_pbu_beg, pbu_size, 32); diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 553f6c6..ce7937f 100644 --- a/src/oapv_vlc.c +++ b/src/oapv_vlc.c @@ -475,7 +475,10 @@ int oapve_vlc_pbu_header(oapv_bs_t *bs, int pbu_type, int group_id) int oapve_vlc_metadata(oapv_md_t *md, oapv_bs_t *bs) { u8 *bs_pos_md; + u8 *bs_pos_end; bs_pos_md = oapv_bsw_sink(bs); + // sink returns NULL when the output buffer has no room left + oapv_assert_rv(bs_pos_md != NULL, OAPV_ERR_OUT_OF_BS_BUF); oapv_bsw_write(bs, 0, 32); // raw bitstream byte size (skip) DUMP_SAVE(0); @@ -510,7 +513,9 @@ int oapve_vlc_metadata(oapv_md_t *md, oapv_bs_t *bs) mdp = mdp->next; } - u32 md_size = (u32)((u8 *)oapv_bsw_sink(bs) - bs_pos_md) - 4; + bs_pos_end = oapv_bsw_sink(bs); + oapv_assert_rv(bs_pos_end != NULL, OAPV_ERR_OUT_OF_BS_BUF); + u32 md_size = (u32)(bs_pos_end - bs_pos_md) - 4; oapv_bsw_write_direct(bs_pos_md, md_size, 32); DUMP_SAVE(1); DUMP_LOAD(0); From 79dc40d1dbb0d64591eaa2385118895ed19d3203 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 19:58:46 +0900 Subject: [PATCH 43/58] Use INVALID_FPS consistently for frame-rate errors --- src/oapv_param.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index a6178ad..ff6573e 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -258,24 +258,24 @@ int oapve_param_parse(oapve_param_t *param, const char *name, const char *value // parse into locals first so a bad value leaves param unchanged int num, den; if(sscanf(value, "%d/%d", &num, &den) != 2 || num <= 0 || den <= 0) { - return OAPV_ERR_INVALID_ARGUMENT; + return OAPV_ERR_INVALID_FPS; } param->fps_num = num; param->fps_den = den; } else if(strpbrk(value, ".") != NULL) { - GET_FLOAT_OR_ERR(value, tf0, OAPV_ERR_INVALID_ARGUMENT); + GET_FLOAT_OR_ERR(value, tf0, OAPV_ERR_INVALID_FPS); // reject values that would overflow the fixed-point fps_num if(!(tf0 > 0.0f) || tf0 > (float)INT_MAX / 10000.0f) { - return OAPV_ERR_INVALID_ARGUMENT; + return OAPV_ERR_INVALID_FPS; } param->fps_num = (int)(tf0 * 10000); param->fps_den = 10000; } else { - GET_INTEGER_OR_ERR(value, ti0, OAPV_ERR_INVALID_ARGUMENT); + GET_INTEGER_OR_ERR(value, ti0, OAPV_ERR_INVALID_FPS); if(ti0 <= 0) { - return OAPV_ERR_INVALID_ARGUMENT; + return OAPV_ERR_INVALID_FPS; } param->fps_num = ti0; param->fps_den = 1; @@ -627,6 +627,9 @@ int oapve_family_bitrate(int family, int w, int h, int fps_num, int fps_den, int { float key, ratio; + // frame rate is a divisor below; reject non-positive values + oapv_assert_rv(fps_num > 0 && fps_den > 0, OAPV_ERR_INVALID_FPS); + switch(family) { case OAPV_FAMILY_422_LQ: ratio = 1.f / (1.4f * 1.4f); From f13eba884e79322f1e9b98a1244273d5fa958f35 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 20:09:47 +0900 Subject: [PATCH 44/58] Prevent leftbits underflow on truncated bitstream --- src/oapv_bs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/oapv_bs.c b/src/oapv_bs.c index ee9f42f..5614e21 100644 --- a/src/oapv_bs.c +++ b/src/oapv_bs.c @@ -226,6 +226,11 @@ void oapv_bsr_skip(oapv_bs_t *bs, int size) // fn_flush set is_eob flag on error; caller must check it return; } + if(bs->leftbits < size) { + // truncated stream: fewer bits than requested; do not underflow + bs->is_eob = 1; + return; + } } bsr_skip_code(bs, size); } @@ -257,6 +262,11 @@ u32 oapv_bsr_read(oapv_bs_t *bs, int size) // fn_flush set is_eob flag on error; return (u32)(-1) with is_eob marker return (u32)(-1); } + if(bs->leftbits < size) { + // truncated stream: fewer bits than requested; do not underflow + bs->is_eob = 1; + return (u32)(-1); + } } code |= (u32)(bs->code >> (64 - size)); From 0270b131839e4fb5cfc9fe2e19b6c600dc737206 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 20:17:43 +0900 Subject: [PATCH 45/58] Bound frame count and null-check metadata payload APIs --- src/oapv.c | 5 ++++- src/oapv_metadata.c | 4 ++++ src/oapv_param.c | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/oapv.c b/src/oapv.c index 8e90d43..9fc9e82 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1182,7 +1182,10 @@ int oapve_encode(oapve_t eid, oapv_frms_t *ifrms, oapvm_t mid, oapv_bitb_t *bitb u8 *bs_pos_pbu_beg, *bs_pos_au_beg; ctx = enc_id_to_ctx(eid); - oapv_assert_rv(ctx != NULL && bitb->addr && bitb->bsize > 0, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(ctx != NULL && bitb != NULL && bitb->addr && bitb->bsize > 0, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(ifrms != NULL && stat != NULL, OAPV_ERR_INVALID_ARGUMENT); + // bound the frame count to the frm[]/param[] array sizes + oapv_assert_rv(ifrms->num_frms >= 1 && ifrms->num_frms <= OAPV_MAX_NUM_FRAMES, OAPV_ERR_INVALID_ARGUMENT); bs = &bsw; diff --git a/src/oapv_metadata.c b/src/oapv_metadata.c index 461e41f..b9aac91 100644 --- a/src/oapv_metadata.c +++ b/src/oapv_metadata.c @@ -185,6 +185,7 @@ int oapvm_write_mdcv(oapvm_payload_mdcv_t *mdcv, void *data, int *size) int i, t; u32 tu32; oapv_bs_t bs; + oapv_assert_rv(mdcv != NULL && data != NULL && size != NULL, OAPV_ERR_INVALID_ARGUMENT); oapv_bsw_init(&bs, data, 24, NULL); // MDCV payload has 24 bytes for(i = 0; i < 3; i++) { @@ -222,6 +223,7 @@ int oapvm_read_mdcv(void *data, int size, oapvm_payload_mdcv_t *mdcv) { int i; oapv_bs_t bs; + oapv_assert_rv(data != NULL && mdcv != NULL, OAPV_ERR_INVALID_ARGUMENT); oapv_assert_rv(size >= 24, OAPV_ERR_INVALID_ARGUMENT); oapv_bsr_init(&bs, data, size, NULL); // MDCV payload has 24 bytes @@ -243,6 +245,7 @@ int oapvm_write_cll(oapvm_payload_cll_t *cll, void *data, int *size) { int t; oapv_bs_t bs; + oapv_assert_rv(cll != NULL && data != NULL && size != NULL, OAPV_ERR_INVALID_ARGUMENT); oapv_bsw_init(&bs, data, 4, NULL); // CLL payload has 4 bytes t = cll->max_cll; @@ -262,6 +265,7 @@ int oapvm_write_cll(oapvm_payload_cll_t *cll, void *data, int *size) int oapvm_read_cll(void *data, int size, oapvm_payload_cll_t *cll) { oapv_bs_t bs; + oapv_assert_rv(data != NULL && cll != NULL, OAPV_ERR_INVALID_ARGUMENT); oapv_assert_rv(size >= 4, OAPV_ERR_INVALID_ARGUMENT); oapv_bsr_init(&bs, data, size, NULL); // CLL payload has 4 bytes diff --git a/src/oapv_param.c b/src/oapv_param.c index ff6573e..5c53a8b 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -559,6 +559,9 @@ int oapve_param_update(oapve_ctx_t* ctx) { int ret = OAPV_OK; int min_num_tiles = OAPV_MAX_TILES; + // bound the frame count to the param[] array size + oapv_assert_rv(ctx->cdesc.max_num_frms >= 1 && ctx->cdesc.max_num_frms <= OAPV_MAX_NUM_FRAMES, + OAPV_ERR_INVALID_ARGUMENT); for (int i = 0; i < ctx->cdesc.max_num_frms; i++) { ret = enc_update_param_tile(ctx, &ctx->cdesc.param[i]); oapv_assert_rv(ret == OAPV_OK, ret); From b3c9d945e2cf57266b665bd37bf2b2918438fc78 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 20:23:34 +0900 Subject: [PATCH 46/58] Bound rate-table indices in bitrate/level lookup --- src/oapv_param.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index 5c53a8b..09b44e2 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -505,6 +505,10 @@ static int enc_update_param_bitrate(oapve_param_t* param) { int level_idx = level_idc_to_level_idx(param->level_idc); + // bound the rate-table indices before use + oapv_assert_rv(level_idx >= 0 && level_idx < MAX_LEVEL_NUM, OAPV_ERR_INVALID_LEVEL); + oapv_assert_rv(param->band_idc >= 0 && param->band_idc < MAX_BAND_NUM, OAPV_ERR_INVALID_ARGUMENT); + if (param->bitrate == 0 && param->qp == OAPVE_PARAM_QP_AUTO) { param->bitrate = max_coded_data_rate[level_idx][param->band_idc]; } @@ -608,8 +612,15 @@ static float get_key_bitrate(int w, int h) if(wh < family_info[idx][0]) { wh_hi = family_info[idx][0]; // resolution of high-bound bit_hi = family_info[idx][1]; // Mbps for high-bound - wh_lo = family_info[idx-1][0]; // resolution of low-bound - bit_lo = family_info[idx-1][1]; // Mbps of low-bound + if(idx == 0) { + // below the smallest tabulated resolution: interpolate from origin + wh_lo = 0; + bit_lo = 0; + } + else { + wh_lo = family_info[idx-1][0]; // resolution of low-bound + bit_lo = family_info[idx-1][1]; // Mbps of low-bound + } float ratio = (float)(bit_hi - bit_lo) / (wh_hi - wh_lo); key = bit_lo + (ratio * (wh - wh_lo)); From cf20bb50bcf07c92065265062f93a7745e599b8b Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Fri, 10 Jul 2026 20:30:33 +0900 Subject: [PATCH 47/58] Avoid overflow in key bitrate resolution lookup --- src/oapv_param.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index 09b44e2..452f7c8 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -605,22 +605,16 @@ static int family_info[][2] = { static float get_key_bitrate(int w, int h) { int idx, wh_hi, wh_lo, bit_hi, bit_lo; - int wh = w * h; + // 64-bit to avoid overflow; family_info[0] is {0,..} so idx never underflows + s64 wh = (s64)w * h; float key = 0.f; for(idx = 0; idx < NUM_FAMILY_INFO; idx++) { if(wh < family_info[idx][0]) { wh_hi = family_info[idx][0]; // resolution of high-bound bit_hi = family_info[idx][1]; // Mbps for high-bound - if(idx == 0) { - // below the smallest tabulated resolution: interpolate from origin - wh_lo = 0; - bit_lo = 0; - } - else { - wh_lo = family_info[idx-1][0]; // resolution of low-bound - bit_lo = family_info[idx-1][1]; // Mbps of low-bound - } + wh_lo = family_info[idx-1][0]; // resolution of low-bound + bit_lo = family_info[idx-1][1]; // Mbps of low-bound float ratio = (float)(bit_hi - bit_lo) / (wh_hi - wh_lo); key = bit_lo + (ratio * (wh - wh_lo)); From d05f5ebc07a9d04a0373a0d6495b444a19fa7531 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Sat, 11 Jul 2026 10:17:55 +0900 Subject: [PATCH 48/58] Validate create/config/metadata inputs and add band error code --- inc/oapv.h | 5 +++-- src/oapv.c | 18 ++++++++++++++++++ src/oapv_metadata.c | 1 + src/oapv_param.c | 5 ++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/inc/oapv.h b/inc/oapv.h index 5726b20..33db283 100644 --- a/inc/oapv.h +++ b/inc/oapv.h @@ -107,8 +107,9 @@ extern "C" { #define OAPV_ERR_OUT_OF_BS_BUF (-203) /* too small bitstream buffer */ #define OAPV_ERR_NOT_FOUND (-204) #define OAPV_ERR_FAILED_SYSCALL (-301) /* failed system call */ -#define OAPV_ERR_INVALID_PROFILE (-400) -#define OAPV_ERR_INVALID_LEVEL (-401) +#define OAPV_ERR_INVALID_PROFILE (-400) /* invalid profile_idc */ +#define OAPV_ERR_INVALID_LEVEL (-401) /* invalid level_idc */ +#define OAPV_ERR_INVALID_BAND (-402) /* invalid band_idc */ #define OAPV_ERR_INVALID_WIDTH (-405) /* invalid width (like odd) */ #define OAPV_ERR_INVALID_HEIGHT (-406) #define OAPV_ERR_INVALID_FPS (-407) /* invalid or missing frame rate */ diff --git a/src/oapv.c b/src/oapv.c index 9fc9e82..e36b21d 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -836,6 +836,15 @@ static int enc_frm_prepare(oapve_ctx_t *ctx, oapve_param_t *param, oapv_imgb_t * oapv_assert_rv(param->h == imgb_i->h[0], OAPV_ERR_INVALID_HEIGHT); oapv_assert_rv((param->qp >= MIN_QUANT && param->qp <= MAX_QUANT(10)) || param->qp == OAPVE_PARAM_QP_AUTO, OAPV_ERR_INVALID_QP); + // q_matrix entries are divisors during quantization; reject zeros + if(param->use_q_matrix) { + for(int c = 0; c < OAPV_MAX_CC; c++) { + for(int i = 0; i < OAPV_BLK_D; i++) { + oapv_assert_rv(param->q_matrix[c][i] != 0, OAPV_ERR_INVALID_ARGUMENT); + } + } + } + // check width restriction for 422 if(OAPV_CS_GET_FORMAT(imgb_i->cs) == OAPV_CF_YCBCR422 && imgb_i->w[0] & 0x1) { return OAPV_ERR_INVALID_WIDTH; // odd width is spec-out in YCbCr422 @@ -1124,6 +1133,10 @@ oapve_t oapve_create(oapve_cdesc_t *cdesc, int *err) DUMP_CREATE(1); + if(cdesc == NULL) { + if(err) *err = OAPV_ERR_INVALID_ARGUMENT; + return NULL; + } if(!((cdesc->threads > 0 && cdesc->threads <= OAPV_MAX_THREADS) || cdesc->threads == OAPV_CDESC_THREADS_AUTO)) { if(err) *err = OAPV_ERR_INVALID_ARGUMENT; return NULL; @@ -1860,6 +1873,10 @@ oapvd_t oapvd_create(oapvd_cdesc_t *cdesc, int *err) DUMP_CREATE(0); ctx = NULL; + if(cdesc == NULL) { + if(err) *err = OAPV_ERR_INVALID_ARGUMENT; + return NULL; + } if(!((cdesc->threads > 0 && cdesc->threads <= OAPV_MAX_THREADS) || cdesc->threads == OAPV_CDESC_THREADS_AUTO)) { if(err) *err = OAPV_ERR_INVALID_ARGUMENT; return NULL; @@ -2035,6 +2052,7 @@ int oapvd_config(oapvd_t did, int cfg, void *buf, int *size) ctx = dec_id_to_ctx(did); oapv_assert_rv(ctx, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(buf != NULL, OAPV_ERR_INVALID_ARGUMENT); switch(cfg) { /* set config ************************************************************/ diff --git a/src/oapv_metadata.c b/src/oapv_metadata.c index b9aac91..91a7972 100644 --- a/src/oapv_metadata.c +++ b/src/oapv_metadata.c @@ -371,6 +371,7 @@ int oapvm_rem(oapvm_t mid, int group_id, int type, unsigned char *uuid) int oapvm_set_all(oapvm_t mid, oapvm_payload_t *pld, int num_plds) { int ret; + oapv_assert_rv(pld != NULL && num_plds >= 0, OAPV_ERR_INVALID_ARGUMENT); for(int i = 0; i < num_plds; i++) { ret = oapvm_set(mid, pld[i].group_id, pld[i].type, pld[i].data, pld[i].size); oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); diff --git a/src/oapv_param.c b/src/oapv_param.c index 452f7c8..b206e5e 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -445,6 +445,9 @@ static int enc_update_param_level_band(oapve_param_t* param) if(param->rc_type != OAPV_RC_CQP || param->level_idc == OAPVE_PARAM_LEVEL_IDC_AUTO) { oapv_assert_rv(param->fps_num > 0 && param->fps_den > 0, OAPV_ERR_INVALID_FPS); } + // band_idc indexes the rate table below; allow AUTO or a valid band + oapv_assert_rv(param->band_idc == OAPVE_PARAM_BAND_IDC_AUTO || + (param->band_idc >= 0 && param->band_idc < MAX_BAND_NUM), OAPV_ERR_INVALID_BAND); // otherwise fps is optional; guard the divisor when it is left unset double fps = (param->fps_den > 0) ? (double)param->fps_num / param->fps_den : 0.0; u64 luma_sample_rate = (int)((double)w * h * fps); @@ -507,7 +510,7 @@ static int enc_update_param_bitrate(oapve_param_t* param) // bound the rate-table indices before use oapv_assert_rv(level_idx >= 0 && level_idx < MAX_LEVEL_NUM, OAPV_ERR_INVALID_LEVEL); - oapv_assert_rv(param->band_idc >= 0 && param->band_idc < MAX_BAND_NUM, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(param->band_idc >= 0 && param->band_idc < MAX_BAND_NUM, OAPV_ERR_INVALID_BAND); if (param->bitrate == 0 && param->qp == OAPVE_PARAM_QP_AUTO) { param->bitrate = max_coded_data_rate[level_idx][param->band_idc]; From 83d07ab1f9681afc7165ccb34da18103ec81dd35 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Sat, 11 Jul 2026 10:39:28 +0900 Subject: [PATCH 49/58] Use reconstruction stride when writing recon picture --- src/oapv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oapv.c b/src/oapv.c index e36b21d..7e65346 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -723,7 +723,7 @@ static int enc_tile(oapve_ctx_t *ctx, oapve_core_t *core, oapve_tile_t *tile) if(ctx->imgb_r) { rec = ctx->imgb_r->a[tc]; rec += (c > 1) ? 1 : 0; - rec_s = ctx->imgb_i->s[tc]; + rec_s = ctx->imgb_r->s[tc]; // recon stride, not input stride } else { rec = NULL; @@ -735,7 +735,7 @@ static int enc_tile(oapve_ctx_t *ctx, oapve_core_t *core, oapve_tile_t *tile) org_s = ctx->imgb_i->s[c]; if(ctx->imgb_r) { rec = ctx->imgb_r->a[c]; - rec_s = ctx->imgb_i->s[c]; + rec_s = ctx->imgb_r->s[c]; // recon stride, not input stride } else { rec = NULL; From b240128b8d205362d9d4d1b6794feeadb397a199 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Sat, 11 Jul 2026 10:43:19 +0900 Subject: [PATCH 50/58] Join encoder worker threads before handling errors --- src/oapv.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/oapv.c b/src/oapv.c index 7e65346..dcb424d 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1038,7 +1038,7 @@ static int enc_frame(oapve_ctx_t *ctx, oapv_bs_t *bs) } oapv_tpool_t *tpool = ctx->tpool; - int res, tidx = 0, thread_num1 = 0; + int tidx = 0, thread_num1 = 0; int parallel_task = (ctx->threads > ctx->num_tiles) ? ctx->num_tiles : ctx->threads; /* encode tiles ************************************/ @@ -1047,13 +1047,19 @@ static int enc_frame(oapve_ctx_t *ctx, oapv_bs_t *bs) (void *)ctx->core[tidx]); } ret = enc_thread_tile((void *)ctx->core[tidx]); - oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + // always join spawned workers before handling any error, so no worker + // keeps reading shared state after this function returns for(thread_num1 = 0; thread_num1 < parallel_task - 1; thread_num1++) { - res = tpool->join(ctx->thread_id[thread_num1], &ret); - oapv_assert_gv(res == TPOOL_SUCCESS, ret, OAPV_ERR_FAILED_SYSCALL, ERR); - oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); + int thread_ret = OAPV_OK; + if(tpool->join(ctx->thread_id[thread_num1], &thread_ret) != TPOOL_SUCCESS) { + ret = OAPV_ERR_FAILED_SYSCALL; + } + else if(OAPV_FAILED(thread_ret)) { + ret = thread_ret; + } } + oapv_assert_g(OAPV_SUCCEEDED(ret), ERR); /****************************************************/ for(int i = 0; i < ctx->num_tiles; i++) { From 0b815129ff2d88bab8ebc6b61118eda93b03f5c1 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Sat, 11 Jul 2026 10:57:35 +0900 Subject: [PATCH 51/58] Reject bitrate strings that overflow int --- src/oapv_param.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index b206e5e..d721e5a 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -103,7 +103,11 @@ static int kbps_str_to_int(const char *str) if(endptr == tmp || *endptr != '\0' || errno == ERANGE) { return -1; } - kbps = (int)fval; + double v = (double)fval; // reject values that overflow int + if(!(v >= 0 && v <= (double)INT_MAX)) { + return -1; + } + kbps = (int)v; } else if(strchr(s, 'M') || strchr(s, 'm')) { char *tmp = strtok(s, "Mm "); @@ -115,7 +119,11 @@ static int kbps_str_to_int(const char *str) if(endptr == tmp || *endptr != '\0' || errno == ERANGE) { return -1; } - kbps = (int)(fval * 1000); + double v = (double)fval * 1000.0; // reject values that overflow int + if(!(v >= 0 && v <= (double)INT_MAX)) { + return -1; + } + kbps = (int)v; } else if(strchr(s, 'G') || strchr(s, 'g')) { char *tmp = strtok(s, "Gg "); @@ -127,7 +135,11 @@ static int kbps_str_to_int(const char *str) if(endptr == tmp || *endptr != '\0' || errno == ERANGE) { return -1; } - kbps = (int)(fval * 1000000); + double v = (double)fval * 1000000.0; // reject values that overflow int + if(!(v >= 0 && v <= (double)INT_MAX)) { + return -1; + } + kbps = (int)v; } else { char *endptr; From 6e553ff58a1eb143f2d080b410b79c15623bb641 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Sat, 11 Jul 2026 11:08:32 +0900 Subject: [PATCH 52/58] Null-check and bound metadata lookup APIs --- src/oapv_metadata.c | 9 ++++++++- src/oapv_tpool.c | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/oapv_metadata.c b/src/oapv_metadata.c index 91a7972..f2415f0 100644 --- a/src/oapv_metadata.c +++ b/src/oapv_metadata.c @@ -64,7 +64,8 @@ static oapv_mdp_t *meta_mdp_find_ud(oapv_md_t *md, unsigned char *uuid, oapv_mdp while(mdp != NULL) { if(mdp->pld_type == OAPV_METADATA_USER_DEFINED) { oapv_md_usd_t *usd = (oapv_md_usd_t *)mdp->pld_data; - if(oapv_mcmp(uuid, usd->uuid, 16) == 0) { + // uuid occupies the first 16 bytes of the payload + if(mdp->pld_size >= 16 && oapv_mcmp(uuid, usd->uuid, 16) == 0) { return mdp; } } @@ -342,6 +343,9 @@ int oapvm_get(oapvm_t mid, int group_id, int type, void **data, int *size, unsig { oapvm_ctx_t *ctx = meta_id_to_ctx(mid); oapv_assert_rv(ctx, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(data != NULL && size != NULL, OAPV_ERR_INVALID_ARGUMENT); + // user-defined lookup compares a 16-byte uuid; it must be provided + oapv_assert_rv(type != OAPV_METADATA_USER_DEFINED || uuid != NULL, OAPV_ERR_INVALID_ARGUMENT); oapv_md_t *md = meta_find_md(ctx, group_id); oapv_assert_g(md != NULL, ERR); oapv_mdp_t *mdp = meta_find_mdp(md, type, uuid); @@ -359,6 +363,8 @@ int oapvm_rem(oapvm_t mid, int group_id, int type, unsigned char *uuid) { oapvm_ctx_t *ctx = meta_id_to_ctx(mid); oapv_assert_rv(ctx, OAPV_ERR_INVALID_ARGUMENT); + // user-defined lookup compares a 16-byte uuid; it must be provided + oapv_assert_rv(type != OAPV_METADATA_USER_DEFINED || uuid != NULL, OAPV_ERR_INVALID_ARGUMENT); oapv_md_t *md = meta_find_md(ctx, group_id); oapv_assert_g(md != NULL, ERR); return meta_rm_mdp(md, type, uuid); @@ -387,6 +393,7 @@ int oapvm_get_all(oapvm_t mid, oapvm_payload_t *pld, int *num_plds) { oapvm_ctx_t *ctx = meta_id_to_ctx(mid); oapv_assert_rv(ctx, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(num_plds != NULL, OAPV_ERR_INVALID_ARGUMENT); if(pld == NULL) { int num_payload = 0; for(int i = 0; i < ctx->num; i++) { diff --git a/src/oapv_tpool.c b/src/oapv_tpool.c index 275a4aa..a3015cc 100644 --- a/src/oapv_tpool.c +++ b/src/oapv_tpool.c @@ -312,7 +312,7 @@ oapv_sync_obj_t oapv_tpool_sync_obj_create() tpool_result_t oapv_tpool_sync_obj_delete(oapv_sync_obj_t *sobj) { - + if(sobj == NULL) return TPOOL_SUCCESS; // nothing to delete thread_mutex_t *imutex = (thread_mutex_t *)(*sobj); if(imutex == NULL) return TPOOL_SUCCESS; // nothing to delete @@ -618,6 +618,7 @@ oapv_sync_obj_t oapv_tpool_sync_obj_create() tpool_result_t oapv_tpool_sync_obj_delete(oapv_sync_obj_t *sobj) { + if(sobj == NULL) return TPOOL_SUCCESS; // nothing to delete thread_mutex_t *imutex = (thread_mutex_t *)(*sobj); if(imutex == NULL) return TPOOL_SUCCESS; // nothing to delete #if WINDOWS_MUTEX_SYNC From 9772d51c35bbed87beff5b44905ba39e7cbf80bf Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Sat, 11 Jul 2026 11:29:43 +0900 Subject: [PATCH 53/58] Widen luma sample rate to avoid int overflow --- src/oapv_param.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index d721e5a..6af9ffe 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -462,7 +462,7 @@ static int enc_update_param_level_band(oapve_param_t* param) (param->band_idc >= 0 && param->band_idc < MAX_BAND_NUM), OAPV_ERR_INVALID_BAND); // otherwise fps is optional; guard the divisor when it is left unset double fps = (param->fps_den > 0) ? (double)param->fps_num / param->fps_den : 0.0; - u64 luma_sample_rate = (int)((double)w * h * fps); + u64 luma_sample_rate = (u64)((double)w * h * fps); // u64: product can exceed INT_MAX int min_level_idx = 0; for (int i = 0 ; i < MAX_LEVEL_NUM ; i++) { if (luma_sample_rate <= max_luma_sample_rate[i]) { From df4343e7163b9df4b0abba48b9d5476d63aa6b3a Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Mon, 13 Jul 2026 09:01:40 +0900 Subject: [PATCH 54/58] Add frame index to encoder config and null-check entry points --- app/oapv_app_enc.c | 2 +- inc/oapv.h | 5 +++++ src/oapv.c | 32 +++++++++++++++++++------------- src/oapv_metadata.c | 2 +- src/oapv_tpool.c | 1 + 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/app/oapv_app_enc.c b/app/oapv_app_enc.c index 161763d..710ccce 100644 --- a/app/oapv_app_enc.c +++ b/app/oapv_app_enc.c @@ -520,7 +520,7 @@ static int set_extra_config(oapve_t id, args_var_t *vars, oapve_param_t *param) if(vars->hash) { value = 1; size = 4; - ret = oapve_config(id, OAPV_CFG_SET_USE_FRM_HASH, &value, &size); + ret = oapve_config(id, OAPV_CFG_FRM(OAPV_CFG_SET_USE_FRM_HASH, 0), &value, &size); if(OAPV_FAILED(ret)) { logerr("ERR: failed to set config for using frame hash\n"); return -1; diff --git a/inc/oapv.h b/inc/oapv.h index 33db283..3cfb3a4 100644 --- a/inc/oapv.h +++ b/inc/oapv.h @@ -195,6 +195,11 @@ extern "C" { #define OAPV_CFG_GET_HEIGHT (702) #define OAPV_CFG_GET_AU_BS_FMT (802) +/* Target a specific frame's parameters in oapve_config(): the upper 16 bits of + * 'cfg' carry the frame index, the lower 16 bits the config id above. Legacy + * callers pass the plain id (index 0). */ +#define OAPV_CFG_FRM(cfg, frm_idx) ((((frm_idx) & 0xFFFF) << 16) | ((cfg) & 0xFFFF)) + /***************************************************************************** * config values *****************************************************************************/ diff --git a/src/oapv.c b/src/oapv.c index dcb424d..3869cb4 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -1299,38 +1299,44 @@ int oapve_encode(oapve_t eid, oapv_frms_t *ifrms, oapvm_t mid, oapv_bitb_t *bitb int oapve_config(oapve_t eid, int cfg, void *buf, int *size) { - oapve_ctx_t *ctx; - int t0; + oapve_ctx_t *ctx; + oapve_param_t *param; + int t0; + int frm_idx = (cfg >> 16) & 0xFFFF; // upper 16 bits: frame index + int cfg_id = cfg & 0xFFFF; // lower 16 bits: config id ctx = enc_id_to_ctx(eid); oapv_assert_rv(ctx, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(buf != NULL && size != NULL, OAPV_ERR_INVALID_ARGUMENT); + oapv_assert_rv(frm_idx < ctx->cdesc.max_num_frms, OAPV_ERR_INVALID_ARGUMENT); + param = &ctx->cdesc.param[frm_idx]; // persistent per-frame config - switch(cfg) { + switch(cfg_id) { /* set config **********************************************************/ case OAPV_CFG_SET_QP: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); t0 = *((int *)buf); oapv_assert_rv(t0 >= MIN_QUANT && t0 <= MAX_QUANT(10), OAPV_ERR_INVALID_ARGUMENT); - ctx->param->qp = t0; + param->qp = t0; break; case OAPV_CFG_SET_FPS_NUM: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); t0 = *((int *)buf); oapv_assert_rv(t0 > 0, OAPV_ERR_INVALID_ARGUMENT); - ctx->param->fps_num = t0; + param->fps_num = t0; break; case OAPV_CFG_SET_FPS_DEN: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); t0 = *((int *)buf); oapv_assert_rv(t0 > 0, OAPV_ERR_INVALID_ARGUMENT); - ctx->param->fps_den = t0; + param->fps_den = t0; break; case OAPV_CFG_SET_BPS: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); t0 = *((int *)buf); oapv_assert_rv(t0 > 0, OAPV_ERR_INVALID_ARGUMENT); - ctx->param->bitrate = t0; + param->bitrate = t0; break; case OAPV_CFG_SET_USE_FRM_HASH: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); @@ -1345,27 +1351,27 @@ int oapve_config(oapve_t eid, int cfg, void *buf, int *size) /* get config *******************************************************/ case OAPV_CFG_GET_QP: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); - *((int *)buf) = ctx->param->qp; + *((int *)buf) = param->qp; break; case OAPV_CFG_GET_WIDTH: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); - *((int *)buf) = ctx->param->w; + *((int *)buf) = param->w; break; case OAPV_CFG_GET_HEIGHT: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); - *((int *)buf) = ctx->param->h; + *((int *)buf) = param->h; break; case OAPV_CFG_GET_FPS_NUM: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); - *((int *)buf) = ctx->param->fps_num; + *((int *)buf) = param->fps_num; break; case OAPV_CFG_GET_FPS_DEN: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); - *((int *)buf) = ctx->param->fps_den; + *((int *)buf) = param->fps_den; break; case OAPV_CFG_GET_BPS: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); - *((int *)buf) = ctx->param->bitrate; + *((int *)buf) = param->bitrate; break; case OAPV_CFG_GET_AU_BS_FMT: oapv_assert_rv(*size == sizeof(int), OAPV_ERR_INVALID_ARGUMENT); diff --git a/src/oapv_metadata.c b/src/oapv_metadata.c index f2415f0..f3b3f19 100644 --- a/src/oapv_metadata.c +++ b/src/oapv_metadata.c @@ -443,7 +443,7 @@ oapvm_t oapvm_create(int *err) oapvm_ctx_t *ctx; ctx = oapv_malloc(sizeof(oapvm_ctx_t)); if(ctx == NULL) { - *err = OAPV_ERR_OUT_OF_MEMORY; + if(err) *err = OAPV_ERR_OUT_OF_MEMORY; return NULL; } oapv_mset(ctx, 0, sizeof(oapvm_ctx_t)); diff --git a/src/oapv_tpool.c b/src/oapv_tpool.c index a3015cc..8af2049 100644 --- a/src/oapv_tpool.c +++ b/src/oapv_tpool.c @@ -651,6 +651,7 @@ void oapv_tpool_leave_cs(oapv_sync_obj_t sobj) tpool_result_t oapv_tpool_init(oapv_tpool_t *tp, int maxtask) { + if(tp == NULL) return TPOOL_INVALID_ARG; // assign handles to threadcontroller object // handles for create, run, join and terminate will be given to controller object From 866900add593be825b4c72be5559615dc134d9e7 Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Mon, 13 Jul 2026 09:55:26 +0900 Subject: [PATCH 55/58] Clamp itrans step and guard thread-pool allocation --- src/avx/oapv_tq_avx.c | 2 ++ src/oapv.c | 2 ++ src/oapv_tq.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/avx/oapv_tq_avx.c b/src/avx/oapv_tq_avx.c index 5939986..c8788a3 100644 --- a/src/avx/oapv_tq_avx.c +++ b/src/avx/oapv_tq_avx.c @@ -533,6 +533,8 @@ const oapv_fn_dquant_t oapv_tbl_fn_dquant_avx[2] = void oapv_adjust_itrans_avx(int* src, int* dst, int itrans_diff_idx, int diff_step, int shift) { + // madd below reads diff_step as 16-bit; clamp so it is not truncated + diff_step = oapv_clip3(-32768, 32767, diff_step); __m256i v0 = _mm256_set1_epi32((1 << 16) | (diff_step & 0xffff)); __m256i v1 = _mm256_set1_epi16(1 << (shift - 1)); __m256i s0, s1, d, d0, d1; diff --git a/src/oapv.c b/src/oapv.c index 3869cb4..61bde19 100644 --- a/src/oapv.c +++ b/src/oapv.c @@ -586,6 +586,7 @@ static int enc_ready(oapve_ctx_t *ctx) if(ctx->threads >= 1) { ctx->tpool = oapv_malloc(sizeof(oapv_tpool_t)); + oapv_assert_gv(ctx->tpool != NULL, ret, OAPV_ERR_OUT_OF_MEMORY, ERR); oapv_tpool_init(ctx->tpool, ctx->threads); for(int i = 0; i < ctx->threads; i++) { ctx->thread_id[i] = ctx->tpool->create(ctx->tpool, i); @@ -1835,6 +1836,7 @@ static int dec_ready(oapvd_ctx_t *ctx) if(ctx->threads >= 2) { ctx->tpool = oapv_malloc(sizeof(oapv_tpool_t)); + oapv_assert_gv(ctx->tpool != NULL, ret, OAPV_ERR_OUT_OF_MEMORY, ERR); oapv_tpool_init(ctx->tpool, ctx->threads - 1); for(i = 0; i < ctx->threads - 1; i++) { ctx->thread_id[i] = ctx->tpool->create(ctx->tpool, i); diff --git a/src/oapv_tq.c b/src/oapv_tq.c index ee05948..b2a8539 100644 --- a/src/oapv_tq.c +++ b/src/oapv_tq.c @@ -423,6 +423,8 @@ const oapv_fn_dquant_t oapv_tbl_fn_dquant[2] = { void oapv_adjust_itrans(int *src, int *dst, int itrans_diff_idx, int diff_step, int shift) { int offset = 1 << (shift - 1); + // diff_step is used as a 16-bit value (see AVX madd); clamp to avoid overflow + diff_step = oapv_clip3(-32768, 32767, diff_step); short* itrans_diff = oapv_itrans_diff[itrans_diff_idx]; for(int k = 0; k < 4; k++) { dst[0] = src[0] + ((itrans_diff[0] * diff_step + offset) >> shift); From 5363fa0b659778e8839042b22c5fe25324c9091c Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Mon, 13 Jul 2026 10:00:16 +0900 Subject: [PATCH 56/58] Join rate-control worker threads before handling errors --- src/oapv_rc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/oapv_rc.c b/src/oapv_rc.c index 4e42579..25d6e3a 100644 --- a/src/oapv_rc.c +++ b/src/oapv_rc.c @@ -121,13 +121,19 @@ int oapve_rc_get_tile_cost_thread(oapve_ctx_t* ctx, u64* sum) } // use main thread int ret = get_tile_cost_thread((void*)ctx->core[tidx]); - oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); + // always join spawned workers before handling any error, so no worker + // keeps reading shared state after this function returns for (int thread_num1 = 0; thread_num1 < parallel_task - 1; thread_num1++) { - int res = tpool->join(ctx->thread_id[thread_num1], &ret); - oapv_assert_rv(res == TPOOL_SUCCESS, ret); - oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); + int thread_ret = OAPV_OK; + if(tpool->join(ctx->thread_id[thread_num1], &thread_ret) != TPOOL_SUCCESS) { + ret = OAPV_ERR_FAILED_SYSCALL; + } + else if(OAPV_FAILED(thread_ret)) { + ret = thread_ret; + } } + oapv_assert_rv(OAPV_SUCCEEDED(ret), ret); *sum = 0; for (int i = 0; i < ctx->num_tiles; i++) From 52c0269618e05ccda7d43ea9f95962742de4cb7c Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Mon, 13 Jul 2026 11:07:26 +0900 Subject: [PATCH 57/58] Clamp tile size to picture instead of rejecting --- src/oapv_param.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/oapv_param.c b/src/oapv_param.c index 6af9ffe..148eb99 100644 --- a/src/oapv_param.c +++ b/src/oapv_param.c @@ -550,7 +550,13 @@ static int enc_update_param_tile(oapve_ctx_t* ctx, oapve_param_t* param) int tile_w, tile_h; oapv_assert_rv(param->tile_w >= OAPV_MIN_TILE_W && param->tile_h >= OAPV_MIN_TILE_H, OAPV_ERR_INVALID_ARGUMENT); - oapv_assert_rv(param->tile_w <= ctx->w && param->tile_h <= ctx->h, OAPV_ERR_INVALID_ARGUMENT); + // a tile larger than the picture means a single tile; clamp to picture size + if(param->tile_w > ctx->w) { + param->tile_w = ctx->w; + } + if(param->tile_h > ctx->h) { + param->tile_h = ctx->h; + } oapv_assert_rv((param->tile_w & (OAPV_MB_W - 1)) == 0 && (param->tile_h & (OAPV_MB_H - 1)) == 0, OAPV_ERR_INVALID_ARGUMENT); if (oapv_div_round_up(ctx->w, param->tile_w) > OAPV_MAX_TILE_COLS) { From 1126c4ecbd144ebdd549d3351fcd7f6a8990758b Mon Sep 17 00:00:00 2001 From: "kp5.choi@samsung.com" Date: Mon, 13 Jul 2026 13:35:47 +0900 Subject: [PATCH 58/58] Bump minor version for new API additions --- inc/oapv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/oapv.h b/inc/oapv.h index 3cfb3a4..bc4c271 100644 --- a/inc/oapv.h +++ b/inc/oapv.h @@ -57,7 +57,7 @@ extern "C" { /* version numbers (should be changed in case of new release) */ #define OAPV_VER_APISET (1) #define OAPV_VER_MAJOR (0) -#define OAPV_VER_MINOR (0) +#define OAPV_VER_MINOR (1) #define OAPV_VER_PATCH (0) /* 4-bytes version number */