diff --git a/CMakeLists.txt b/CMakeLists.txt index f040c923..6700bca1 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_args.h b/app/oapv_app_args.h index d464e510..48ff8d83 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; } diff --git a/app/oapv_app_dec.c b/app/oapv_app_dec.c index 2a409044..cd7836ef 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)) @@ -238,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) { @@ -250,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; @@ -261,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; @@ -281,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; } } @@ -290,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; } } @@ -565,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)); @@ -599,8 +599,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; } @@ -706,6 +706,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; } @@ -740,7 +743,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; @@ -951,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/app/oapv_app_enc.c b/app/oapv_app_enc.c index 9ef77ef8..710ccce3 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]; @@ -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; @@ -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; \ } \ } @@ -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); @@ -782,7 +787,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 @@ -1127,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) { @@ -1142,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 9e190579..2d2192fd 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; } @@ -493,14 +493,20 @@ 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) { - 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 +520,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; } @@ -532,6 +538,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,9 +689,17 @@ 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); + if(bd_src < 8 || bd_src > 16 || bd_dst < 8 || bd_dst > 16) { + logerr("ERR: unsupported bit depth in image copy\n"); + return; + } + if(src->cs == dst->cs) { imgb_cpy_plane(dst, src); } @@ -708,7 +751,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]; @@ -734,7 +777,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]); @@ -752,7 +795,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); @@ -765,7 +808,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 02aa7c2d..07835b36 100644 --- a/app/oapv_app_y4m.h +++ b/app/oapv_app_y4m.h @@ -84,19 +84,31 @@ 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("ERR: 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("ERR: 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; + if(fps_n <= 0 || fps_d <= 0) { + logerr("ERR: invalid y4m frame rate %d:%d\n", fps_n, fps_d); + return -1; + } y4m->fps_num = fps_n; y4m->fps_den = fps_d; break; @@ -107,12 +119,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; @@ -123,8 +135,8 @@ 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; + logerr("ERR: Mandatory width and height values were not found in y4m header"); + return -1; } if(!found_cf) { @@ -177,7 +189,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) @@ -187,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.*/ @@ -199,22 +212,26 @@ 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; } + 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; } @@ -272,7 +289,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; } @@ -282,7 +299,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)) { @@ -298,7 +315,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)) { @@ -321,9 +338,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; diff --git a/inc/oapv.h b/inc/oapv.h index 5388a02f..bc4c2719 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 */ @@ -107,10 +107,12 @@ 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 */ #define OAPV_ERR_INVALID_QP (-410) #define OAPV_ERR_INVALID_FAMILY (-501) /* invalid family number */ #define OAPV_ERR_UNKNOWN (-32767) /* unknown error */ @@ -193,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 *****************************************************************************/ @@ -376,6 +383,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/avx/oapv_tq_avx.c b/src/avx/oapv_tq_avx.c index 59399862..c8788a3d 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 b15ca9e7..61bde19a 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; @@ -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); @@ -723,7 +724,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 +736,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; @@ -836,6 +837,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 @@ -919,6 +929,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); @@ -945,7 +969,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); } @@ -981,6 +1011,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; @@ -1007,7 +1039,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 ************************************/ @@ -1016,16 +1048,23 @@ 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++) { + 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; @@ -1100,6 +1139,16 @@ oapve_t oapve_create(oapve_cdesc_t *cdesc, int *err) int ret; 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; + } + /* memory allocation for ctx and core structure */ ctx = (oapve_ctx_t *)enc_ctx_alloc(); if(ctx != NULL) { @@ -1153,7 +1202,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; @@ -1216,13 +1268,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); @@ -1244,38 +1300,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); @@ -1290,27 +1352,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); @@ -1407,7 +1469,9 @@ 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; + + 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 @@ -1490,9 +1554,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 +1570,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 { @@ -1581,7 +1648,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++) { @@ -1664,6 +1731,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); @@ -1726,7 +1794,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]); @@ -1766,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); @@ -1816,8 +1887,14 @@ 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 == 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; + } /* memory allocation for ctx and core structure */ ctx = (oapvd_ctx_t *)dec_ctx_alloc(); @@ -1866,16 +1943,23 @@ 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; 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') oapv_assert_rv(bitb->ssize > 4, OAPV_ERR_MALFORMED_BITSTREAM); - u32 signature = oapv_bsr_read_direct(bitb->addr, 32); + 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); cur_read_size += 4; stat->read += 4; @@ -1891,7 +1975,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); @@ -1934,7 +2018,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); @@ -1982,6 +2066,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 ************************************************************/ @@ -2001,6 +2086,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 +2094,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 +2198,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++) { @@ -2154,6 +2244,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; @@ -2200,7 +2293,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); @@ -2233,6 +2326,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); diff --git a/src/oapv_bs.c b/src/oapv_bs.c index 48d29843..5614e218 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,15 @@ 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; + } + if(bs->leftbits < size) { + // truncated stream: fewer bits than requested; do not underflow + bs->is_eob = 1; return; } } @@ -250,13 +255,16 @@ 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); + } + if(bs->leftbits < size) { + // truncated stream: fewer bits than requested; do not underflow + bs->is_eob = 1; return (u32)(-1); } } @@ -272,7 +280,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,22 +292,24 @@ 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; + code |= (u32)(*(p)) << shift; shift -= 8; 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 40f1a901..c8193b5b 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 659d904f..f3b3f194 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; } } @@ -185,6 +186,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 +224,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 +246,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 +266,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 @@ -299,7 +304,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; @@ -338,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); @@ -355,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); @@ -367,6 +377,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); @@ -382,10 +393,13 @@ 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++) { 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 +410,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; @@ -428,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_param.c b/src/oapv_param.c index 81b50221..148eb990 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; @@ -102,10 +103,15 @@ 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 "); + if(tmp == NULL) return -1; char *endptr; float fval; errno = 0; @@ -113,10 +119,15 @@ 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 "); + if(tmp == NULL) return -1; char *endptr; float fval; errno = 0; @@ -124,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; @@ -182,6 +197,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; } @@ -248,15 +267,28 @@ 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); + // 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_FPS; + } + 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; + 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_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_FPS; + } param->fps_num = ti0; param->fps_den = 1; } @@ -420,8 +452,17 @@ 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; - u64 luma_sample_rate = (int)((double)w * h * fps); + // 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); + } + // 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 = (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]) { @@ -479,6 +520,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_BAND); + if (param->bitrate == 0 && param->qp == OAPVE_PARAM_QP_AUTO) { param->bitrate = max_coded_data_rate[level_idx][param->band_idc]; } @@ -493,6 +538,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; @@ -501,6 +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); + // 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) { @@ -528,6 +584,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); @@ -567,7 +626,8 @@ 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++) { @@ -596,6 +656,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); diff --git a/src/oapv_rc.c b/src/oapv_rc.c index fed8b046..25d6e3a3 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; @@ -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++) @@ -189,6 +195,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); @@ -214,11 +223,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; + + alpha_new = oapv_clip3(0.05, 500, alpha_new); + beta_new = oapv_clip3(0.1, 3, beta_new); - 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); + // 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; } diff --git a/src/oapv_tpool.c b/src/oapv_tpool.c index 8be07401..8af20496 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) @@ -306,8 +312,9 @@ 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 // delete the mutex pthread_mutex_destroy(&imutex->lmutex); @@ -611,7 +618,9 @@ 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 // release the mutex CloseHandle(imutex->lmutex); @@ -642,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 diff --git a/src/oapv_tq.c b/src/oapv_tq.c index ee059481..b2a85393 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); diff --git a/src/oapv_util.c b/src/oapv_util.c index 708f7b5e..c49e299a 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_is_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_is_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 9226c5fa..c33fa12e 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 @@ -142,7 +171,8 @@ typedef struct } oapv_md5_t; /* MD5 Functions */ -void oapv_imgb_set_md5(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); void oapv_block_copy(s16 *src, int src_stride, s16 *dst, int dst_stride, int log2_copy_w, int log2_copy_h); diff --git a/src/oapv_vlc.c b/src/oapv_vlc.c index 1e452fb5..ce7937f8 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; \ } @@ -467,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); @@ -502,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); @@ -623,9 +636,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) { \ @@ -693,6 +719,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); @@ -774,6 +803,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 +824,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); @@ -815,6 +846,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); @@ -825,6 +858,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; } @@ -866,7 +902,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. @@ -909,6 +945,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 +956,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 +967,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 +989,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 +1048,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 +1074,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 +1125,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,11 +1149,12 @@ 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; } -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; @@ -1121,19 +1171,24 @@ 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++) { 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); 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 +1196,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 +1218,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 +1231,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 +1275,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; diff --git a/src/oapv_vlc.h b/src/oapv_vlc.h index 435515b2..b04d12c0 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);