Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 1 | /****************************************************************************** |
| 2 | * |
| 3 | * Copyright (C) 2019 The Android Open Source Project |
| 4 | * |
| 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at: |
| 8 | * |
| 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | * |
| 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
| 16 | * |
| 17 | ***************************************************************************** |
| 18 | * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore |
| 19 | */ |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 20 | |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 21 | #include <stddef.h> |
| 22 | #include <stdint.h> |
| 23 | #include <stdio.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
| 26 | |
| 27 | #include <algorithm> |
| 28 | #include <memory> |
| 29 | |
| 30 | #include "ih264_typedefs.h" |
| 31 | #include "ih264d.h" |
| 32 | #include "iv.h" |
| 33 | #include "ivd.h" |
| 34 | |
| 35 | #define NELEMENTS(x) (sizeof(x) / sizeof(x[0])) |
| 36 | #define ivd_api_function ih264d_api_function |
| 37 | const IV_COLOR_FORMAT_T supportedColorFormats[] = { |
| 38 | IV_YUV_420P, IV_YUV_420SP_UV, IV_YUV_420SP_VU, |
| 39 | IV_YUV_422ILE, IV_RGB_565, IV_RGBA_8888}; |
| 40 | |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 41 | /* Decoder ignores invalid arch, i.e. for arm build, if SSSE3 is requested, |
| 42 | * decoder defaults to a supported configuration. So same set of supported |
| 43 | * architectures can be used in arm/arm64/x86 builds */ |
| 44 | const IVD_ARCH_T supportedArchitectures[] = { |
| 45 | ARCH_ARM_NONEON, ARCH_ARM_A9Q, ARCH_ARM_NEONINTR, ARCH_ARMV8_GENERIC, |
| 46 | ARCH_X86_GENERIC, ARCH_X86_SSSE3, ARCH_X86_SSE42}; |
| 47 | |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 48 | enum { |
| 49 | OFFSET_COLOR_FORMAT = 6, |
| 50 | OFFSET_NUM_CORES, |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 51 | OFFSET_ARCH, |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 52 | /* Should be the last entry */ |
| 53 | OFFSET_MAX, |
| 54 | }; |
| 55 | |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 56 | const static int kMaxNumDecodeCalls = 100; |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 57 | const static int kSupportedColorFormats = NELEMENTS(supportedColorFormats); |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 58 | const static int kSupportedArchitectures = NELEMENTS(supportedArchitectures); |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 59 | const static int kMaxCores = 4; |
| 60 | void *iv_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) { |
Harish Mahendrakar | 9aefe92 | 2019-09-09 16:14:50 -0700 | [diff] [blame] | 61 | void *buf = NULL; |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 62 | (void)ctxt; |
Harish Mahendrakar | 9aefe92 | 2019-09-09 16:14:50 -0700 | [diff] [blame] | 63 | if (0 != posix_memalign(&buf, alignment, size)) { |
| 64 | return NULL; |
| 65 | } |
| 66 | return buf; |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | void iv_aligned_free(void *ctxt, void *buf) { |
| 70 | (void)ctxt; |
| 71 | free(buf); |
| 72 | } |
| 73 | |
| 74 | class Codec { |
| 75 | public: |
| 76 | Codec(IV_COLOR_FORMAT_T colorFormat, size_t numCores); |
| 77 | ~Codec(); |
| 78 | |
| 79 | void createCodec(); |
| 80 | void deleteCodec(); |
| 81 | void resetCodec(); |
| 82 | void setCores(); |
| 83 | void allocFrame(); |
| 84 | void freeFrame(); |
| 85 | void decodeHeader(const uint8_t *data, size_t size); |
| 86 | IV_API_CALL_STATUS_T decodeFrame(const uint8_t *data, size_t size, |
| 87 | size_t *bytesConsumed); |
| 88 | void setParams(IVD_VIDEO_DECODE_MODE_T mode); |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 89 | void setArchitecture(IVD_ARCH_T arch); |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 90 | |
| 91 | private: |
| 92 | IV_COLOR_FORMAT_T mColorFormat; |
| 93 | size_t mNumCores; |
| 94 | iv_obj_t *mCodec; |
| 95 | ivd_out_bufdesc_t mOutBufHandle; |
| 96 | uint32_t mWidth; |
| 97 | uint32_t mHeight; |
| 98 | }; |
| 99 | |
| 100 | Codec::Codec(IV_COLOR_FORMAT_T colorFormat, size_t numCores) { |
| 101 | mColorFormat = colorFormat; |
| 102 | mNumCores = numCores; |
| 103 | mCodec = nullptr; |
| 104 | mWidth = 0; |
| 105 | mHeight = 0; |
| 106 | |
| 107 | memset(&mOutBufHandle, 0, sizeof(mOutBufHandle)); |
| 108 | } |
| 109 | Codec::~Codec() {} |
| 110 | void Codec::createCodec() { |
| 111 | IV_API_CALL_STATUS_T ret; |
| 112 | ih264d_create_ip_t create_ip; |
| 113 | ih264d_create_op_t create_op; |
| 114 | void *fxns = (void *)&ivd_api_function; |
| 115 | |
| 116 | create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE; |
| 117 | create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0; |
| 118 | create_ip.s_ivd_create_ip_t.e_output_format = mColorFormat; |
| 119 | create_ip.s_ivd_create_ip_t.pf_aligned_alloc = iv_aligned_malloc; |
| 120 | create_ip.s_ivd_create_ip_t.pf_aligned_free = iv_aligned_free; |
| 121 | create_ip.s_ivd_create_ip_t.pv_mem_ctxt = NULL; |
| 122 | create_ip.s_ivd_create_ip_t.u4_size = sizeof(ih264d_create_ip_t); |
| 123 | create_op.s_ivd_create_op_t.u4_size = sizeof(ih264d_create_op_t); |
| 124 | |
| 125 | ret = ivd_api_function(NULL, (void *)&create_ip, (void *)&create_op); |
| 126 | if (ret != IV_SUCCESS) { |
| 127 | return; |
| 128 | } |
| 129 | mCodec = (iv_obj_t *)create_op.s_ivd_create_op_t.pv_handle; |
| 130 | mCodec->pv_fxns = fxns; |
| 131 | mCodec->u4_size = sizeof(iv_obj_t); |
| 132 | } |
| 133 | |
| 134 | void Codec::deleteCodec() { |
| 135 | ivd_delete_ip_t delete_ip; |
| 136 | ivd_delete_op_t delete_op; |
| 137 | |
| 138 | delete_ip.e_cmd = IVD_CMD_DELETE; |
| 139 | delete_ip.u4_size = sizeof(ivd_delete_ip_t); |
| 140 | delete_op.u4_size = sizeof(ivd_delete_op_t); |
| 141 | |
| 142 | ivd_api_function(mCodec, (void *)&delete_ip, (void *)&delete_op); |
| 143 | } |
| 144 | void Codec::resetCodec() { |
| 145 | ivd_ctl_reset_ip_t s_ctl_ip; |
| 146 | ivd_ctl_reset_op_t s_ctl_op; |
| 147 | |
| 148 | s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; |
| 149 | s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET; |
| 150 | s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t); |
| 151 | s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t); |
| 152 | |
| 153 | ivd_api_function(mCodec, (void *)&s_ctl_ip, (void *)&s_ctl_op); |
| 154 | } |
| 155 | |
| 156 | void Codec::setCores() { |
| 157 | ih264d_ctl_set_num_cores_ip_t s_ctl_ip; |
| 158 | ih264d_ctl_set_num_cores_op_t s_ctl_op; |
| 159 | |
| 160 | s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; |
| 161 | s_ctl_ip.e_sub_cmd = |
| 162 | (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES; |
| 163 | s_ctl_ip.u4_num_cores = mNumCores; |
| 164 | s_ctl_ip.u4_size = sizeof(ih264d_ctl_set_num_cores_ip_t); |
| 165 | s_ctl_op.u4_size = sizeof(ih264d_ctl_set_num_cores_op_t); |
| 166 | |
| 167 | ivd_api_function(mCodec, (void *)&s_ctl_ip, (void *)&s_ctl_op); |
| 168 | } |
| 169 | |
| 170 | void Codec::setParams(IVD_VIDEO_DECODE_MODE_T mode) { |
| 171 | ivd_ctl_set_config_ip_t s_ctl_ip; |
| 172 | ivd_ctl_set_config_op_t s_ctl_op; |
| 173 | |
| 174 | s_ctl_ip.u4_disp_wd = 0; |
| 175 | s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE; |
| 176 | s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT; |
| 177 | s_ctl_ip.e_vid_dec_mode = mode; |
| 178 | s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; |
| 179 | s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS; |
| 180 | s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t); |
| 181 | s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t); |
| 182 | |
| 183 | ivd_api_function(mCodec, (void *)&s_ctl_ip, (void *)&s_ctl_op); |
| 184 | } |
| 185 | |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 186 | void Codec::setArchitecture(IVD_ARCH_T arch) { |
| 187 | ih264d_ctl_set_processor_ip_t s_ctl_ip; |
| 188 | ih264d_ctl_set_processor_op_t s_ctl_op; |
| 189 | |
| 190 | s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL; |
| 191 | s_ctl_ip.e_sub_cmd = |
| 192 | (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_PROCESSOR; |
| 193 | s_ctl_ip.u4_arch = arch; |
| 194 | s_ctl_ip.u4_soc = SOC_GENERIC; |
| 195 | s_ctl_ip.u4_size = sizeof(ih264d_ctl_set_processor_ip_t); |
| 196 | s_ctl_op.u4_size = sizeof(ih264d_ctl_set_processor_op_t); |
| 197 | |
| 198 | ivd_api_function(mCodec, (void *)&s_ctl_ip, (void *)&s_ctl_op); |
| 199 | } |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 200 | void Codec::freeFrame() { |
| 201 | for (int i = 0; i < mOutBufHandle.u4_num_bufs; i++) { |
| 202 | if (mOutBufHandle.pu1_bufs[i]) { |
| 203 | free(mOutBufHandle.pu1_bufs[i]); |
| 204 | mOutBufHandle.pu1_bufs[i] = nullptr; |
| 205 | } |
| 206 | } |
| 207 | } |
| 208 | void Codec::allocFrame() { |
| 209 | size_t sizes[4] = {0}; |
| 210 | size_t num_bufs = 0; |
| 211 | |
| 212 | freeFrame(); |
| 213 | |
| 214 | memset(&mOutBufHandle, 0, sizeof(mOutBufHandle)); |
| 215 | |
| 216 | switch (mColorFormat) { |
| 217 | case IV_YUV_420SP_UV: |
| 218 | [[fallthrough]]; |
| 219 | case IV_YUV_420SP_VU: |
| 220 | sizes[0] = mWidth * mHeight; |
| 221 | sizes[1] = mWidth * mHeight >> 1; |
| 222 | num_bufs = 2; |
| 223 | break; |
| 224 | case IV_YUV_422ILE: |
| 225 | sizes[0] = mWidth * mHeight * 2; |
| 226 | num_bufs = 1; |
| 227 | break; |
| 228 | case IV_RGB_565: |
| 229 | sizes[0] = mWidth * mHeight * 2; |
| 230 | num_bufs = 1; |
| 231 | break; |
| 232 | case IV_RGBA_8888: |
| 233 | sizes[0] = mWidth * mHeight * 4; |
| 234 | num_bufs = 1; |
| 235 | break; |
| 236 | case IV_YUV_420P: |
| 237 | [[fallthrough]]; |
| 238 | default: |
| 239 | sizes[0] = mWidth * mHeight; |
| 240 | sizes[1] = mWidth * mHeight >> 2; |
| 241 | sizes[2] = mWidth * mHeight >> 2; |
| 242 | num_bufs = 3; |
| 243 | break; |
| 244 | } |
| 245 | mOutBufHandle.u4_num_bufs = num_bufs; |
| 246 | for (int i = 0; i < num_bufs; i++) { |
| 247 | mOutBufHandle.u4_min_out_buf_size[i] = sizes[i]; |
Harish Mahendrakar | 9aefe92 | 2019-09-09 16:14:50 -0700 | [diff] [blame] | 248 | mOutBufHandle.pu1_bufs[i] = (UWORD8 *)iv_aligned_malloc(NULL, 16, sizes[i]); |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 249 | } |
| 250 | } |
| 251 | void Codec::decodeHeader(const uint8_t *data, size_t size) { |
| 252 | setParams(IVD_DECODE_HEADER); |
| 253 | |
| 254 | while (size > 0) { |
| 255 | IV_API_CALL_STATUS_T ret; |
| 256 | ivd_video_decode_ip_t dec_ip; |
| 257 | ivd_video_decode_op_t dec_op; |
| 258 | size_t bytes_consumed; |
| 259 | |
| 260 | memset(&dec_ip, 0, sizeof(dec_ip)); |
| 261 | memset(&dec_op, 0, sizeof(dec_op)); |
| 262 | |
| 263 | dec_ip.e_cmd = IVD_CMD_VIDEO_DECODE; |
| 264 | dec_ip.u4_ts = 0; |
| 265 | dec_ip.pv_stream_buffer = (void *)data; |
| 266 | dec_ip.u4_num_Bytes = size; |
| 267 | dec_ip.u4_size = sizeof(ivd_video_decode_ip_t); |
| 268 | dec_op.u4_size = sizeof(ivd_video_decode_op_t); |
| 269 | |
| 270 | ret = ivd_api_function(mCodec, (void *)&dec_ip, (void *)&dec_op); |
| 271 | |
| 272 | bytes_consumed = dec_op.u4_num_bytes_consumed; |
| 273 | /* If no bytes are consumed, then consume 4 bytes to ensure fuzzer proceeds |
| 274 | * to feed next data */ |
| 275 | if (!bytes_consumed) bytes_consumed = 4; |
| 276 | |
| 277 | bytes_consumed = std::min(size, bytes_consumed); |
| 278 | |
| 279 | data += bytes_consumed; |
| 280 | size -= bytes_consumed; |
| 281 | |
| 282 | mWidth = std::min(dec_op.u4_pic_wd, (UWORD32)10240); |
| 283 | mHeight = std::min(dec_op.u4_pic_ht, (UWORD32)10240); |
| 284 | |
| 285 | /* Break after successful header decode */ |
| 286 | if (mWidth && mHeight) { |
| 287 | break; |
| 288 | } |
| 289 | } |
| 290 | /* if width / height are invalid, set them to defaults */ |
| 291 | if (!mWidth) mWidth = 1920; |
| 292 | if (!mHeight) mHeight = 1088; |
| 293 | } |
| 294 | |
| 295 | IV_API_CALL_STATUS_T Codec::decodeFrame(const uint8_t *data, size_t size, |
| 296 | size_t *bytesConsumed) { |
| 297 | IV_API_CALL_STATUS_T ret; |
| 298 | ivd_video_decode_ip_t dec_ip; |
| 299 | ivd_video_decode_op_t dec_op; |
| 300 | |
| 301 | memset(&dec_ip, 0, sizeof(dec_ip)); |
| 302 | memset(&dec_op, 0, sizeof(dec_op)); |
| 303 | |
| 304 | dec_ip.e_cmd = IVD_CMD_VIDEO_DECODE; |
| 305 | dec_ip.u4_ts = 0; |
| 306 | dec_ip.pv_stream_buffer = (void *)data; |
| 307 | dec_ip.u4_num_Bytes = size; |
| 308 | dec_ip.u4_size = sizeof(ivd_video_decode_ip_t); |
| 309 | dec_ip.s_out_buffer = mOutBufHandle; |
| 310 | |
| 311 | dec_op.u4_size = sizeof(ivd_video_decode_op_t); |
| 312 | |
| 313 | ret = ivd_api_function(mCodec, (void *)&dec_ip, (void *)&dec_op); |
| 314 | |
| 315 | /* In case of change in resolution, reset codec and feed the same data again |
| 316 | */ |
| 317 | if (IVD_RES_CHANGED == (dec_op.u4_error_code & 0xFF)) { |
| 318 | resetCodec(); |
| 319 | ret = ivd_api_function(mCodec, (void *)&dec_ip, (void *)&dec_op); |
| 320 | } |
| 321 | *bytesConsumed = dec_op.u4_num_bytes_consumed; |
| 322 | |
| 323 | /* If no bytes are consumed, then consume 4 bytes to ensure fuzzer proceeds |
| 324 | * to feed next data */ |
| 325 | if (!*bytesConsumed) *bytesConsumed = 4; |
| 326 | |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 327 | if (dec_op.u4_pic_wd && dec_op.u4_pic_ht && |
| 328 | (mWidth != dec_op.u4_pic_wd || mHeight != dec_op.u4_pic_ht)) { |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 329 | mWidth = std::min(dec_op.u4_pic_wd, (UWORD32)10240); |
| 330 | mHeight = std::min(dec_op.u4_pic_ht, (UWORD32)10240); |
| 331 | allocFrame(); |
| 332 | } |
| 333 | |
| 334 | return ret; |
| 335 | } |
| 336 | |
| 337 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
| 338 | if (size < 1) { |
| 339 | return 0; |
| 340 | } |
| 341 | size_t colorFormatOfst = std::min((size_t)OFFSET_COLOR_FORMAT, size - 1); |
| 342 | size_t numCoresOfst = std::min((size_t)OFFSET_NUM_CORES, size - 1); |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 343 | size_t architectureOfst = std::min((size_t)OFFSET_ARCH, size - 1); |
| 344 | size_t architectureIdx = data[architectureOfst] % kSupportedArchitectures; |
| 345 | IVD_ARCH_T arch = (IVD_ARCH_T)supportedArchitectures[architectureIdx]; |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 346 | size_t colorFormatIdx = data[colorFormatOfst] % kSupportedColorFormats; |
| 347 | IV_COLOR_FORMAT_T colorFormat = |
| 348 | (IV_COLOR_FORMAT_T)(supportedColorFormats[colorFormatIdx]); |
| 349 | uint32_t numCores = (data[numCoresOfst] % kMaxCores) + 1; |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 350 | size_t numDecodeCalls = 0; |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 351 | Codec *codec = new Codec(colorFormat, numCores); |
| 352 | codec->createCodec(); |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 353 | codec->setArchitecture(arch); |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 354 | codec->setCores(); |
| 355 | codec->decodeHeader(data, size); |
| 356 | codec->setParams(IVD_DECODE_FRAME); |
| 357 | codec->allocFrame(); |
| 358 | |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 359 | while (size > 0 && numDecodeCalls < kMaxNumDecodeCalls) { |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 360 | IV_API_CALL_STATUS_T ret; |
| 361 | size_t bytesConsumed; |
| 362 | ret = codec->decodeFrame(data, size, &bytesConsumed); |
| 363 | |
| 364 | bytesConsumed = std::min(size, bytesConsumed); |
| 365 | data += bytesConsumed; |
| 366 | size -= bytesConsumed; |
Harish Mahendrakar | 82b7f93 | 2019-08-26 16:03:03 -0700 | [diff] [blame] | 367 | numDecodeCalls++; |
Harish Mahendrakar | 1cf2178 | 2019-06-20 13:57:20 -0700 | [diff] [blame] | 368 | } |
| 369 | |
| 370 | codec->freeFrame(); |
| 371 | codec->deleteCodec(); |
| 372 | delete codec; |
| 373 | return 0; |
| 374 | } |