From e8bdcd4dea62d025cedadc9b4fe35dbd1ea58879 Mon Sep 17 00:00:00 2001 From: Michael Platings Date: Fri, 2 Feb 2024 11:06:23 +0000 Subject: [PATCH] Add comprehensive null pointer checks --- intrinsiccv/include/utils.h | 11 +++ intrinsiccv/src/analysis/canny_neon.cpp | 2 + .../src/analysis/count_nonzeros_neon.cpp | 4 +- intrinsiccv/src/analysis/min_max_loc_neon.cpp | 2 + intrinsiccv/src/analysis/min_max_neon.cpp | 1 + intrinsiccv/src/arithmetics/absdiff_neon.cpp | 2 + intrinsiccv/src/arithmetics/absdiff_sme2.cpp | 2 + intrinsiccv/src/arithmetics/absdiff_sve2.cpp | 2 + .../add_abs_with_threshold_neon.cpp | 2 + .../arithmetics/add_abs_with_threshold_sc.h | 2 + intrinsiccv/src/arithmetics/add_neon.cpp | 2 + intrinsiccv/src/arithmetics/add_sme2.cpp | 2 + intrinsiccv/src/arithmetics/add_sve2.cpp | 2 + intrinsiccv/src/arithmetics/multiply_neon.cpp | 2 + intrinsiccv/src/arithmetics/multiply_sve2.cpp | 2 + intrinsiccv/src/arithmetics/scale_neon.cpp | 2 + intrinsiccv/src/arithmetics/sub_neon.cpp | 2 + intrinsiccv/src/arithmetics/sub_sme2.cpp | 2 + intrinsiccv/src/arithmetics/sub_sve2.cpp | 2 + .../src/arithmetics/threshold_neon.cpp | 2 + intrinsiccv/src/arithmetics/threshold_sc.h | 2 + .../src/arithmetics/transpose_neon.cpp | 2 + .../src/conversions/gray_to_rgb_neon.cpp | 2 + intrinsiccv/src/conversions/gray_to_rgb_sc.h | 2 + intrinsiccv/src/conversions/merge_neon.cpp | 8 ++ .../src/conversions/rgb_to_rgb_api.cpp | 2 + .../src/conversions/rgb_to_rgb_neon.cpp | 6 ++ intrinsiccv/src/conversions/rgb_to_rgb_sc.h | 6 ++ intrinsiccv/src/conversions/split_neon.cpp | 8 ++ .../src/conversions/yuv_to_rgb_neon.cpp | 1 + intrinsiccv/src/conversions/yuv_to_rgb_sc.h | 1 + intrinsiccv/src/filters/gaussian_blur_api.cpp | 6 +- .../src/filters/gaussian_blur_neon.cpp | 3 + intrinsiccv/src/filters/gaussian_blur_sc.h | 3 + intrinsiccv/src/filters/sobel_neon.cpp | 4 + intrinsiccv/src/filters/sobel_sc.h | 4 + intrinsiccv/src/morphology/morphology_api.cpp | 7 +- .../src/morphology/morphology_neon.cpp | 6 ++ intrinsiccv/src/morphology/morphology_sc.h | 10 +- intrinsiccv/src/resize/resize_neon.cpp | 2 + intrinsiccv/src/resize/resize_sc.h | 2 + test/api/test_canny.cpp | 26 +++++ test/api/test_count_nonzeros.cpp | 7 ++ test/api/test_gaussian_blur.cpp | 50 ++++++++++ test/api/test_merge.cpp | 28 +++++- test/api/test_min_max.cpp | 22 ++++- test/api/test_morphology.cpp | 99 +++++++++++++++++++ test/api/test_resize_to_quarter.cpp | 7 ++ test/api/test_rgb_and_gray.cpp | 6 ++ test/api/test_saturating_absdiff.cpp | 4 + test/api/test_saturating_add.cpp | 4 + test/api/test_saturating_multiply.cpp | 4 + test/api/test_saturating_sub.cpp | 4 + test/api/test_scale.cpp | 27 +++++ test/api/test_sobel.cpp | 10 ++ test/api/test_split.cpp | 30 +++++- test/api/test_threshold_binary.cpp | 7 ++ test/api/test_transpose.cpp | 6 ++ test/api/test_yuv_to_rgb.cpp | 5 + test/framework/utils.h | 54 ++++++++++ 60 files changed, 518 insertions(+), 19 deletions(-) create mode 100644 test/api/test_canny.cpp create mode 100644 test/api/test_gaussian_blur.cpp create mode 100644 test/api/test_morphology.cpp create mode 100644 test/api/test_scale.cpp diff --git a/intrinsiccv/include/utils.h b/intrinsiccv/include/utils.h index c50a0ced3..7eb308401 100644 --- a/intrinsiccv/include/utils.h +++ b/intrinsiccv/include/utils.h @@ -351,6 +351,17 @@ class LoopUnroll2 final { bool try_avoid_tail_loop_; }; // end of class LoopUnroll2 +// Check whether any of the arguments are null pointers. +template +bool any_null(Pointers... pointers) { + return (... || (pointers == nullptr)); +} + +#define CHECK_POINTERS(...) \ + if (any_null(__VA_ARGS__)) { \ + return INTRINSICCV_ERROR_NULL_POINTER; \ + } + } // namespace intrinsiccv #endif // INTRINSICCV_UTILS_H diff --git a/intrinsiccv/src/analysis/canny_neon.cpp b/intrinsiccv/src/analysis/canny_neon.cpp index 73aa15d07..733e7e08c 100644 --- a/intrinsiccv/src/analysis/canny_neon.cpp +++ b/intrinsiccv/src/analysis/canny_neon.cpp @@ -468,6 +468,8 @@ static void perform_hysteresis(StrongEdgeStack &strong_edge_pixels, extern "C" INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t intrinsiccv_canny_u8( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height, double low_threshold, double high_threshold) { + CHECK_POINTERS(src, dst); + Rectangle dst_rect{width, height}; // Allocate all temporary buffers in advance. diff --git a/intrinsiccv/src/analysis/count_nonzeros_neon.cpp b/intrinsiccv/src/analysis/count_nonzeros_neon.cpp index 86106a545..c453021b4 100644 --- a/intrinsiccv/src/analysis/count_nonzeros_neon.cpp +++ b/intrinsiccv/src/analysis/count_nonzeros_neon.cpp @@ -67,9 +67,7 @@ intrinsiccv_error_t intrinsiccv_count_nonzeros_u8(const uint8_t *src, size_t src_stride, size_t width, size_t height, size_t *count) { - if (count == nullptr) { - return INTRINSICCV_ERROR_NULL_POINTER; - } + CHECK_POINTERS(src, count); *count = count_nonzeros(src, src_stride, width, height); return INTRINSICCV_OK; } diff --git a/intrinsiccv/src/analysis/min_max_loc_neon.cpp b/intrinsiccv/src/analysis/min_max_loc_neon.cpp index 338a58653..814cedecb 100644 --- a/intrinsiccv/src/analysis/min_max_loc_neon.cpp +++ b/intrinsiccv/src/analysis/min_max_loc_neon.cpp @@ -310,6 +310,8 @@ template intrinsiccv_error_t min_max_loc(const ScalarType *src, size_t src_stride, size_t width, size_t height, size_t *min_offset, size_t *max_offset) { + CHECK_POINTERS(src); + Rectangle rect{width, height}; Rows src_rows{src, src_stride}; MinMaxLoc operation; diff --git a/intrinsiccv/src/analysis/min_max_neon.cpp b/intrinsiccv/src/analysis/min_max_neon.cpp index cd13c4b93..76e8e9ef1 100644 --- a/intrinsiccv/src/analysis/min_max_neon.cpp +++ b/intrinsiccv/src/analysis/min_max_neon.cpp @@ -48,6 +48,7 @@ template intrinsiccv_error_t min_max(const ScalarType *src, size_t src_stride, size_t width, size_t height, ScalarType *min_value, ScalarType *max_value) { + CHECK_POINTERS(src); Rectangle rect{width, height}; Rows src_rows{src, src_stride}; MinMax operation; diff --git a/intrinsiccv/src/arithmetics/absdiff_neon.cpp b/intrinsiccv/src/arithmetics/absdiff_neon.cpp index 7252fe77a..4d100c0f1 100644 --- a/intrinsiccv/src/arithmetics/absdiff_neon.cpp +++ b/intrinsiccv/src/arithmetics/absdiff_neon.cpp @@ -40,6 +40,8 @@ intrinsiccv_error_t saturating_absdiff(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingAbsDiff operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/absdiff_sme2.cpp b/intrinsiccv/src/arithmetics/absdiff_sme2.cpp index e00079225..654286ea7 100644 --- a/intrinsiccv/src/arithmetics/absdiff_sme2.cpp +++ b/intrinsiccv/src/arithmetics/absdiff_sme2.cpp @@ -40,6 +40,8 @@ template INTRINSICCV_LOCALLY_STREAMING intrinsiccv_error_t saturating_absdiff( const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingAbsDiff operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/absdiff_sve2.cpp b/intrinsiccv/src/arithmetics/absdiff_sve2.cpp index 3d146d361..56e5cddb6 100644 --- a/intrinsiccv/src/arithmetics/absdiff_sve2.cpp +++ b/intrinsiccv/src/arithmetics/absdiff_sve2.cpp @@ -38,6 +38,8 @@ intrinsiccv_error_t saturating_absdiff(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingAbsDiff operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/add_abs_with_threshold_neon.cpp b/intrinsiccv/src/arithmetics/add_abs_with_threshold_neon.cpp index 89464ac6c..6effc7dff 100644 --- a/intrinsiccv/src/arithmetics/add_abs_with_threshold_neon.cpp +++ b/intrinsiccv/src/arithmetics/add_abs_with_threshold_neon.cpp @@ -39,6 +39,8 @@ intrinsiccv_error_t add_abs_with_threshold(const T *src_a, size_t src_a_stride, T *dst, size_t dst_stride, size_t width, size_t height, T threshold) { + CHECK_POINTERS(src_a, src_b, dst); + AddAbsWithThreshold operation{threshold}; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/add_abs_with_threshold_sc.h b/intrinsiccv/src/arithmetics/add_abs_with_threshold_sc.h index 2faf46e3f..c2fa1643d 100644 --- a/intrinsiccv/src/arithmetics/add_abs_with_threshold_sc.h +++ b/intrinsiccv/src/arithmetics/add_abs_with_threshold_sc.h @@ -39,6 +39,8 @@ intrinsiccv_error_t add_abs_with_threshold_sc( const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height, T threshold) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src_a, src_b, dst); + AddAbsWithThreshold operation{threshold}; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/add_neon.cpp b/intrinsiccv/src/arithmetics/add_neon.cpp index c78348702..c3f3ed5db 100644 --- a/intrinsiccv/src/arithmetics/add_neon.cpp +++ b/intrinsiccv/src/arithmetics/add_neon.cpp @@ -39,6 +39,8 @@ intrinsiccv_error_t saturating_add(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingAdd operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/add_sme2.cpp b/intrinsiccv/src/arithmetics/add_sme2.cpp index 3fb6486f2..cd4dd5482 100644 --- a/intrinsiccv/src/arithmetics/add_sme2.cpp +++ b/intrinsiccv/src/arithmetics/add_sme2.cpp @@ -26,6 +26,8 @@ template INTRINSICCV_LOCALLY_STREAMING intrinsiccv_error_t saturating_add( const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingAdd operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/add_sve2.cpp b/intrinsiccv/src/arithmetics/add_sve2.cpp index 140dac6d4..8d44f99ee 100644 --- a/intrinsiccv/src/arithmetics/add_sve2.cpp +++ b/intrinsiccv/src/arithmetics/add_sve2.cpp @@ -26,6 +26,8 @@ intrinsiccv_error_t saturating_add(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingAdd operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/multiply_neon.cpp b/intrinsiccv/src/arithmetics/multiply_neon.cpp index 12e94ed7b..20563c787 100644 --- a/intrinsiccv/src/arithmetics/multiply_neon.cpp +++ b/intrinsiccv/src/arithmetics/multiply_neon.cpp @@ -67,6 +67,8 @@ intrinsiccv_error_t saturating_multiply(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height, double scale) { + CHECK_POINTERS(src_a, src_b, dst); + (void)scale; // TODO: figure out the way to process the scale. SaturatingMultiply operation; Rectangle rect{width, height}; diff --git a/intrinsiccv/src/arithmetics/multiply_sve2.cpp b/intrinsiccv/src/arithmetics/multiply_sve2.cpp index 59b54391c..7efca6a8f 100644 --- a/intrinsiccv/src/arithmetics/multiply_sve2.cpp +++ b/intrinsiccv/src/arithmetics/multiply_sve2.cpp @@ -45,6 +45,8 @@ intrinsiccv_error_t saturating_multiply(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height, double scale) { + CHECK_POINTERS(src_a, src_b, dst); + (void)scale; // TODO: figure out the way to process the scale. SaturatingMultiply operation; Rectangle rect{width, height}; diff --git a/intrinsiccv/src/arithmetics/scale_neon.cpp b/intrinsiccv/src/arithmetics/scale_neon.cpp index 52c5be3fe..4d81862c9 100644 --- a/intrinsiccv/src/arithmetics/scale_neon.cpp +++ b/intrinsiccv/src/arithmetics/scale_neon.cpp @@ -172,6 +172,8 @@ template intrinsiccv_error_t scale(const T *src, size_t src_stride, T *dst, size_t dst_stride, size_t width, size_t height, float scale, float shift) { + CHECK_POINTERS(src, dst); + Rectangle rect{width, height}; Rows src_rows{src, src_stride}; Rows dst_rows{dst, dst_stride}; diff --git a/intrinsiccv/src/arithmetics/sub_neon.cpp b/intrinsiccv/src/arithmetics/sub_neon.cpp index a01599866..318cd206a 100644 --- a/intrinsiccv/src/arithmetics/sub_neon.cpp +++ b/intrinsiccv/src/arithmetics/sub_neon.cpp @@ -39,6 +39,8 @@ intrinsiccv_error_t saturating_sub(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingSub operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/sub_sme2.cpp b/intrinsiccv/src/arithmetics/sub_sme2.cpp index 07ac367e8..b8d7b3926 100644 --- a/intrinsiccv/src/arithmetics/sub_sme2.cpp +++ b/intrinsiccv/src/arithmetics/sub_sme2.cpp @@ -26,6 +26,8 @@ template INTRINSICCV_LOCALLY_STREAMING intrinsiccv_error_t saturating_sub( const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingSub operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/sub_sve2.cpp b/intrinsiccv/src/arithmetics/sub_sve2.cpp index 376180384..0ab4901e9 100644 --- a/intrinsiccv/src/arithmetics/sub_sve2.cpp +++ b/intrinsiccv/src/arithmetics/sub_sve2.cpp @@ -26,6 +26,8 @@ intrinsiccv_error_t saturating_sub(const T *src_a, size_t src_a_stride, const T *src_b, size_t src_b_stride, T *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_a, src_b, dst); + SaturatingSub operation; Rectangle rect{width, height}; Rows src_a_rows{src_a, src_a_stride}; diff --git a/intrinsiccv/src/arithmetics/threshold_neon.cpp b/intrinsiccv/src/arithmetics/threshold_neon.cpp index 94ae83eb2..979114fd6 100644 --- a/intrinsiccv/src/arithmetics/threshold_neon.cpp +++ b/intrinsiccv/src/arithmetics/threshold_neon.cpp @@ -41,6 +41,8 @@ template intrinsiccv_error_t threshold_binary(const T *src, size_t src_stride, T *dst, size_t dst_stride, size_t width, size_t height, T threshold, T value) { + CHECK_POINTERS(src, dst); + Rectangle rect{width, height}; Rows src_rows{src, src_stride}; Rows dst_rows{dst, dst_stride}; diff --git a/intrinsiccv/src/arithmetics/threshold_sc.h b/intrinsiccv/src/arithmetics/threshold_sc.h index 5922a1777..784937993 100644 --- a/intrinsiccv/src/arithmetics/threshold_sc.h +++ b/intrinsiccv/src/arithmetics/threshold_sc.h @@ -37,6 +37,8 @@ template intrinsiccv_error_t threshold_binary_sc( const T *src, size_t src_stride, T *dst, size_t dst_stride, size_t width, size_t height, T threshold, T value) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); + Rectangle rect{width, height}; Rows src_rows{src, src_stride}; Rows dst_rows{dst, dst_stride}; diff --git a/intrinsiccv/src/arithmetics/transpose_neon.cpp b/intrinsiccv/src/arithmetics/transpose_neon.cpp index 55394649f..af7899db9 100644 --- a/intrinsiccv/src/arithmetics/transpose_neon.cpp +++ b/intrinsiccv/src/arithmetics/transpose_neon.cpp @@ -215,6 +215,8 @@ INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t transpose(const void *src, size_t src_stride, void *dst, size_t dst_stride, size_t src_width, size_t src_height, size_t element_size) { + CHECK_POINTERS(src, dst); + bool inplace = false; if (src == dst) { diff --git a/intrinsiccv/src/conversions/gray_to_rgb_neon.cpp b/intrinsiccv/src/conversions/gray_to_rgb_neon.cpp index 741e81e99..b0e01ea04 100644 --- a/intrinsiccv/src/conversions/gray_to_rgb_neon.cpp +++ b/intrinsiccv/src/conversions/gray_to_rgb_neon.cpp @@ -108,6 +108,7 @@ INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t gray_to_rgb_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride}; Rows dst_rows{dst, dst_stride, 3 /* RGB */}; @@ -120,6 +121,7 @@ INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t gray_to_rgba_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride}; Rows dst_rows{dst, dst_stride, 4 /* RGBA */}; diff --git a/intrinsiccv/src/conversions/gray_to_rgb_sc.h b/intrinsiccv/src/conversions/gray_to_rgb_sc.h index b68619bf9..69efea547 100644 --- a/intrinsiccv/src/conversions/gray_to_rgb_sc.h +++ b/intrinsiccv/src/conversions/gray_to_rgb_sc.h @@ -199,6 +199,7 @@ class GrayToRGBA final : INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t gray_to_rgb_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride}; Rows dst_rows{dst, dst_stride, 3 /* RGB */}; @@ -215,6 +216,7 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t gray_to_rgb_u8_sc( INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t gray_to_rgba_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride}; Rows dst_rows{dst, dst_stride, 4 /* RGBA */}; diff --git a/intrinsiccv/src/conversions/merge_neon.cpp b/intrinsiccv/src/conversions/merge_neon.cpp index 83bdaeffc..9017e3d15 100644 --- a/intrinsiccv/src/conversions/merge_neon.cpp +++ b/intrinsiccv/src/conversions/merge_neon.cpp @@ -371,6 +371,12 @@ template intrinsiccv_error_t merge(const void **srcs, const size_t *src_strides, void *dst, size_t dst_stride, size_t width, size_t height, size_t channels) { + if (channels < 2) { + return INTRINSICCV_ERROR_RANGE; + } + CHECK_POINTERS(srcs, src_strides, dst); + CHECK_POINTERS(srcs[0], srcs[1]); + Rectangle rect{width, height}; Rows src_a_rows{srcs[0], src_strides[0]}; Rows src_b_rows{srcs[1], src_strides[1]}; @@ -384,6 +390,7 @@ intrinsiccv_error_t merge(const void **srcs, const size_t *src_strides, } break; case 3: { + CHECK_POINTERS(srcs[2]); Merge3 operation; Rows src_c_rows{srcs[2], src_strides[2]}; apply_operation_by_rows(operation, rect, src_a_rows, src_b_rows, @@ -391,6 +398,7 @@ intrinsiccv_error_t merge(const void **srcs, const size_t *src_strides, } break; case 4: { + CHECK_POINTERS(srcs[2], srcs[3]); Merge4 operation; Rows src_c_rows{srcs[2], src_strides[2]}; Rows src_d_rows{srcs[3], src_strides[3]}; diff --git a/intrinsiccv/src/conversions/rgb_to_rgb_api.cpp b/intrinsiccv/src/conversions/rgb_to_rgb_api.cpp index a16fae24d..009424d3f 100644 --- a/intrinsiccv/src/conversions/rgb_to_rgb_api.cpp +++ b/intrinsiccv/src/conversions/rgb_to_rgb_api.cpp @@ -29,6 +29,7 @@ intrinsiccv_error_t intrinsiccv_rgb_to_rgb_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 3 /* BGR */}; @@ -40,6 +41,7 @@ intrinsiccv_error_t intrinsiccv_rgba_to_rgba_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 4 /* RGBA */}; Rows dst_rows{dst, dst_stride, 4 /* RGBA */}; diff --git a/intrinsiccv/src/conversions/rgb_to_rgb_neon.cpp b/intrinsiccv/src/conversions/rgb_to_rgb_neon.cpp index 34357d34a..d2f56d542 100644 --- a/intrinsiccv/src/conversions/rgb_to_rgb_neon.cpp +++ b/intrinsiccv/src/conversions/rgb_to_rgb_neon.cpp @@ -191,6 +191,7 @@ INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t rgb_to_bgr_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 3 /* BGR */}; @@ -203,6 +204,7 @@ INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t rgba_to_bgra_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 4 /* RGBA */}; Rows dst_rows{dst, dst_stride, 4 /* BGRA */}; @@ -214,6 +216,7 @@ intrinsiccv_error_t rgba_to_bgra_u8(const uint8_t *src, size_t src_stride, INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t rgb_to_bgra_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 4 /* BGRA */}; @@ -225,6 +228,7 @@ rgb_to_bgra_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t rgb_to_rgba_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 4 /* RGBA */}; @@ -237,6 +241,7 @@ INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t rgba_to_bgr_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 4 /* RGBA */}; Rows dst_rows{dst, dst_stride, 3 /* BGR */}; @@ -249,6 +254,7 @@ INTRINSICCV_TARGET_FN_ATTRS intrinsiccv_error_t rgba_to_rgb_u8(const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 4 /* RGBA */}; Rows dst_rows{dst, dst_stride, 3 /* RGB */}; diff --git a/intrinsiccv/src/conversions/rgb_to_rgb_sc.h b/intrinsiccv/src/conversions/rgb_to_rgb_sc.h index 2477d2846..df0c3817a 100644 --- a/intrinsiccv/src/conversions/rgb_to_rgb_sc.h +++ b/intrinsiccv/src/conversions/rgb_to_rgb_sc.h @@ -184,6 +184,7 @@ class RGBAToRGB final : public UnrollTwice { INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t rgb_to_bgr_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 3 /* BGR */}; @@ -201,6 +202,7 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t rgba_to_bgra_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 4 /* RGBA */}; Rows dst_rows{dst, dst_stride, 4 /* BGRA */}; @@ -213,6 +215,7 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t rgb_to_bgra_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 4 /* BGRA */}; @@ -225,6 +228,7 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t rgb_to_rgba_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 3 /* RGB */}; Rows dst_rows{dst, dst_stride, 4 /* RGBA */}; @@ -237,6 +241,7 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t rgba_to_bgr_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 4 /* RGBA */}; Rows dst_rows{dst, dst_stride, 3 /* BGR */}; @@ -249,6 +254,7 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t rgba_to_rgb_u8_sc( const uint8_t *src, size_t src_stride, uint8_t *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); Rectangle rect{width, height}; Rows src_rows{src, src_stride, 4 /* RGBA */}; Rows dst_rows{dst, dst_stride, 3 /* RGB */}; diff --git a/intrinsiccv/src/conversions/split_neon.cpp b/intrinsiccv/src/conversions/split_neon.cpp index b1ff1b827..4d6d5376c 100644 --- a/intrinsiccv/src/conversions/split_neon.cpp +++ b/intrinsiccv/src/conversions/split_neon.cpp @@ -256,6 +256,12 @@ template intrinsiccv_error_t split(const void *src_data, const size_t src_stride, void **dst_data, const size_t *dst_strides, size_t width, size_t height, size_t channels) { + if (channels < 2) { + return INTRINSICCV_ERROR_RANGE; + } + CHECK_POINTERS(src_data, dst_data, dst_strides); + CHECK_POINTERS(dst_data[0], dst_data[1]); + Rectangle rect{width, height}; ScalarType *dst0 = reinterpret_cast(dst_data[0]), *dst1 = reinterpret_cast(dst_data[1]); @@ -270,6 +276,7 @@ intrinsiccv_error_t split(const void *src_data, const size_t src_stride, apply_operation_by_rows(operation, rect, src_rows, dst_rows0, dst_rows1); } break; case 3: { + CHECK_POINTERS(dst_data[2]); ScalarType *dst2 = reinterpret_cast(dst_data[2]); Rows dst_rows2{dst2, dst_strides[2]}; Split3 operation; @@ -277,6 +284,7 @@ intrinsiccv_error_t split(const void *src_data, const size_t src_stride, dst_rows2); } break; case 4: { + CHECK_POINTERS(dst_data[2], dst_data[3]); ScalarType *dst2 = reinterpret_cast(dst_data[2]), *dst3 = reinterpret_cast(dst_data[3]); Rows dst_rows2{dst2, dst_strides[2]}; diff --git a/intrinsiccv/src/conversions/yuv_to_rgb_neon.cpp b/intrinsiccv/src/conversions/yuv_to_rgb_neon.cpp index 7ad86c3a2..3a7f9fa14 100644 --- a/intrinsiccv/src/conversions/yuv_to_rgb_neon.cpp +++ b/intrinsiccv/src/conversions/yuv_to_rgb_neon.cpp @@ -292,6 +292,7 @@ intrinsiccv_error_t yuv2rgbx_operation( OperationType &operation, const ScalarType *src_y, size_t src_y_stride, const ScalarType *src_uv, size_t src_uv_stride, ScalarType *dst, size_t dst_stride, size_t width, size_t height) { + CHECK_POINTERS(src_y, src_uv, dst); Rectangle rect{width, height}; ParallelRows y_rows{src_y, src_y_stride}; Rows uv_rows{src_uv, src_uv_stride}; diff --git a/intrinsiccv/src/conversions/yuv_to_rgb_sc.h b/intrinsiccv/src/conversions/yuv_to_rgb_sc.h index e0580b6ba..335fb03a8 100644 --- a/intrinsiccv/src/conversions/yuv_to_rgb_sc.h +++ b/intrinsiccv/src/conversions/yuv_to_rgb_sc.h @@ -193,6 +193,7 @@ intrinsiccv_error_t yuv2rgbx_operation( const ScalarType *src_uv, size_t src_uv_stride, ScalarType *dst, size_t dst_stride, size_t width, size_t height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src_y, src_uv, dst); Rectangle rect{width, height}; ParallelRows y_rows{src_y, src_y_stride}; Rows uv_rows{src_uv, src_uv_stride}; diff --git a/intrinsiccv/src/filters/gaussian_blur_api.cpp b/intrinsiccv/src/filters/gaussian_blur_api.cpp index c76796c70..ee9f96eba 100644 --- a/intrinsiccv/src/filters/gaussian_blur_api.cpp +++ b/intrinsiccv/src/filters/gaussian_blur_api.cpp @@ -13,6 +13,7 @@ extern "C" { intrinsiccv_error_t intrinsiccv_filter_create( intrinsiccv_filter_params_t *params, intrinsiccv_rectangle_t image) { + CHECK_POINTERS(params); auto workspace = SeparableFilterWorkspace::create( Rectangle{image}, params->channels, params->type_size); if (!workspace) { @@ -25,9 +26,8 @@ intrinsiccv_error_t intrinsiccv_filter_create( intrinsiccv_error_t intrinsiccv_filter_release( intrinsiccv_filter_params_t *params) { - if (!params->workspace) { - return INTRINSICCV_ERROR_NULL_POINTER; - } + CHECK_POINTERS(params); + CHECK_POINTERS(params->workspace); // Deliberately create and immediately destroy a unique_ptr to delete the // workspace. diff --git a/intrinsiccv/src/filters/gaussian_blur_neon.cpp b/intrinsiccv/src/filters/gaussian_blur_neon.cpp index 62038e762..59f947eae 100644 --- a/intrinsiccv/src/filters/gaussian_blur_neon.cpp +++ b/intrinsiccv/src/filters/gaussian_blur_neon.cpp @@ -150,6 +150,9 @@ intrinsiccv_error_t discrete_gaussian_blur( const intrinsiccv_filter_params_t *params) { using GaussianBlurFilterType = DiscreteGaussianBlur; + CHECK_POINTERS(src, dst, params); + CHECK_POINTERS(params->workspace); + Rectangle rect{width, height}; Rows src_rows{src, src_stride, channels}; Rows dst_rows{dst, dst_stride, channels}; diff --git a/intrinsiccv/src/filters/gaussian_blur_sc.h b/intrinsiccv/src/filters/gaussian_blur_sc.h index 738ab70c2..7ef65334e 100644 --- a/intrinsiccv/src/filters/gaussian_blur_sc.h +++ b/intrinsiccv/src/filters/gaussian_blur_sc.h @@ -91,6 +91,9 @@ intrinsiccv_error_t discrete_gaussian_blur( INTRINSICCV_STREAMING_COMPATIBLE { using GaussianBlurFilterType = DiscreteGaussianBlur; + CHECK_POINTERS(src, dst, params); + CHECK_POINTERS(params->workspace); + Rectangle rect{width, height}; Rows src_rows{src, src_stride, channels}; Rows dst_rows{dst, dst_stride, channels}; diff --git a/intrinsiccv/src/filters/sobel_neon.cpp b/intrinsiccv/src/filters/sobel_neon.cpp index 0dd403f4b..6bb047ff2 100644 --- a/intrinsiccv/src/filters/sobel_neon.cpp +++ b/intrinsiccv/src/filters/sobel_neon.cpp @@ -132,6 +132,8 @@ intrinsiccv_error_t sobel_3x3_horizontal_s16_u8(const uint8_t *src, size_t dst_stride, size_t width, size_t height, size_t channels) { + CHECK_POINTERS(src, dst); + Rectangle rect{width, height}; Rows src_rows{src, src_stride, channels}; Rows dst_rows{dst, dst_stride, channels}; @@ -154,6 +156,8 @@ intrinsiccv_error_t sobel_3x3_vertical_s16_u8(const uint8_t *src, size_t src_stride, int16_t *dst, size_t dst_stride, size_t width, size_t height, size_t channels) { + CHECK_POINTERS(src, dst); + Rectangle rect{width, height}; Rows src_rows{src, src_stride, channels}; Rows dst_rows{dst, dst_stride, channels}; diff --git a/intrinsiccv/src/filters/sobel_sc.h b/intrinsiccv/src/filters/sobel_sc.h index 47258922d..38e250fcb 100644 --- a/intrinsiccv/src/filters/sobel_sc.h +++ b/intrinsiccv/src/filters/sobel_sc.h @@ -122,6 +122,8 @@ static intrinsiccv_error_t sobel_3x3_horizontal_s16_u8_sc( const uint8_t *src, size_t src_stride, int16_t *dst, size_t dst_stride, size_t width, size_t height, size_t channels) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); + Rectangle rect{width, height}; Rows src_rows{src, src_stride, channels}; Rows dst_rows{dst, dst_stride, channels}; @@ -145,6 +147,8 @@ static intrinsiccv_error_t sobel_3x3_vertical_s16_u8_sc( const uint8_t *src, size_t src_stride, int16_t *dst, size_t dst_stride, size_t width, size_t height, size_t channels) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); + Rectangle rect{width, height}; Rows src_rows{src, src_stride, channels}; Rows dst_rows{dst, dst_stride, channels}; diff --git a/intrinsiccv/src/morphology/morphology_api.cpp b/intrinsiccv/src/morphology/morphology_api.cpp index d59f3ebfe..f715bb702 100644 --- a/intrinsiccv/src/morphology/morphology_api.cpp +++ b/intrinsiccv/src/morphology/morphology_api.cpp @@ -54,6 +54,8 @@ extern "C" { intrinsiccv_error_t intrinsiccv_morphology_create( intrinsiccv_morphology_params_t *params, intrinsiccv_rectangle_t image) { + CHECK_POINTERS(params); + auto workspace = MorphologyWorkspace::create(Rectangle{image}, Rectangle{params->kernel}, Margin{params->kernel, params->anchor}, @@ -68,9 +70,8 @@ intrinsiccv_error_t intrinsiccv_morphology_create( intrinsiccv_error_t intrinsiccv_morphology_release( intrinsiccv_morphology_params_t *params) { - if (!params->data) { - return INTRINSICCV_ERROR_NULL_POINTER; - } + CHECK_POINTERS(params); + CHECK_POINTERS(params->data); // Deliberately create and immediately destroy a unique_ptr to delete the // workspace. diff --git a/intrinsiccv/src/morphology/morphology_neon.cpp b/intrinsiccv/src/morphology/morphology_neon.cpp index a358c0ffc..d7886c2c9 100644 --- a/intrinsiccv/src/morphology/morphology_neon.cpp +++ b/intrinsiccv/src/morphology/morphology_neon.cpp @@ -490,6 +490,9 @@ template intrinsiccv_error_t dilate(const T *src, size_t src_stride, T *dst, size_t dst_stride, size_t width, size_t height, const intrinsiccv_morphology_params_t *params) { + CHECK_POINTERS(src, dst, params); + CHECK_POINTERS(params->impl, params->data); + Rectangle rect{width, height}; Rectangle kernel{params->kernel}; Rows src_rows{src, src_stride, params->channels}; @@ -541,6 +544,9 @@ template intrinsiccv_error_t erode(const T *src, size_t src_stride, T *dst, size_t dst_stride, size_t width, size_t height, const intrinsiccv_morphology_params_t *params) { + CHECK_POINTERS(src, dst, params); + CHECK_POINTERS(params->impl, params->data); + Rectangle rect{width, height}; Rectangle kernel{params->kernel}; Rows src_rows{src, src_stride, params->channels}; diff --git a/intrinsiccv/src/morphology/morphology_sc.h b/intrinsiccv/src/morphology/morphology_sc.h index a6ed84a9f..fdcadbfd9 100644 --- a/intrinsiccv/src/morphology/morphology_sc.h +++ b/intrinsiccv/src/morphology/morphology_sc.h @@ -448,10 +448,13 @@ class DilateOperation final { }; // end of class DilateOperation template -static inline intrinsiccv_error_t dilate_sc( +static intrinsiccv_error_t dilate_sc( const T *src, size_t src_stride, T *dst, size_t dst_stride, size_t width, size_t height, const intrinsiccv_morphology_params_t *params) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst, params); + CHECK_POINTERS(params->impl, params->data); + Rectangle rect{width, height}; Rectangle kernel{params->kernel}; Rows src_rows{src, src_stride, params->channels}; @@ -503,10 +506,13 @@ class ErodeOperation final { }; // end of class ErodeOperation template -static inline intrinsiccv_error_t erode_sc( +static intrinsiccv_error_t erode_sc( const T *src, size_t src_stride, T *dst, size_t dst_stride, size_t width, size_t height, const intrinsiccv_morphology_params_t *params) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst, params); + CHECK_POINTERS(params->impl, params->data); + Rectangle rect{width, height}; Rectangle kernel{params->kernel}; Rows src_rows{src, src_stride, params->channels}; diff --git a/intrinsiccv/src/resize/resize_neon.cpp b/intrinsiccv/src/resize/resize_neon.cpp index 58988f620..777211d94 100644 --- a/intrinsiccv/src/resize/resize_neon.cpp +++ b/intrinsiccv/src/resize/resize_neon.cpp @@ -13,6 +13,8 @@ intrinsiccv_error_t resize_to_quarter_u8(const uint8_t *src, size_t src_stride, size_t src_width, size_t src_height, uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height) { + CHECK_POINTERS(src, dst); + for (; src_height >= 2; src_height -= 2, src += (src_stride * 2), --dst_height, dst += dst_stride) { const uint8_t *src_l = src; diff --git a/intrinsiccv/src/resize/resize_sc.h b/intrinsiccv/src/resize/resize_sc.h index 42c9ed2fc..0a77b5ef0 100644 --- a/intrinsiccv/src/resize/resize_sc.h +++ b/intrinsiccv/src/resize/resize_sc.h @@ -143,6 +143,8 @@ INTRINSICCV_TARGET_FN_ATTRS static intrinsiccv_error_t resize_to_quarter_u8_sc( const uint8_t *src, size_t src_stride, size_t src_width, size_t src_height, uint8_t *dst, size_t dst_stride, size_t dst_width, size_t dst_height) INTRINSICCV_STREAMING_COMPATIBLE { + CHECK_POINTERS(src, dst); + Rows src_rows{src, src_stride, /* channels*/ 1}; Rows dst_rows{dst, dst_stride, /* channels*/ 1}; LoopUnroll2 loop{src_height, /* Process two rows */ 2}; diff --git a/test/api/test_canny.cpp b/test/api/test_canny.cpp new file mode 100644 index 000000000..9acc82b06 --- /dev/null +++ b/test/api/test_canny.cpp @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "framework/utils.h" + +#define INTRINSICCV_CANNY(type, suffix) \ + INTRINSICCV_API(canny, intrinsiccv_canny_##suffix, type) + +INTRINSICCV_CANNY(uint8_t, u8); + +using ElementTypes = ::testing::Types; + +template +class CannyTest : public testing::Test {}; + +TYPED_TEST_SUITE(CannyTest, ElementTypes); + +TYPED_TEST(CannyTest, NullPointer) { + TypeParam src[1], dst[1]; + test::test_null_args(canny(), src, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1, 0.0, 1.0); +} diff --git a/test/api/test_count_nonzeros.cpp b/test/api/test_count_nonzeros.cpp index 79c82ee49..3b3368c02 100644 --- a/test/api/test_count_nonzeros.cpp +++ b/test/api/test_count_nonzeros.cpp @@ -110,3 +110,10 @@ TYPED_TEST(CountNonZeros, AllNonZerosPadded) { CountNonZerosTest{}.test( test::Options::vector_length()); } + +TYPED_TEST(CountNonZeros, NullPointer) { + TypeParam src[1]; + size_t count = 0; + test::test_null_args(count_nonzeros(), src, sizeof(TypeParam), 1, + 1, &count); +} diff --git a/test/api/test_gaussian_blur.cpp b/test/api/test_gaussian_blur.cpp new file mode 100644 index 000000000..b880a112e --- /dev/null +++ b/test/api/test_gaussian_blur.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "framework/utils.h" + +#define INTRINSICCV_GAUSSIAN_BLUR(type, kernel_suffix, type_suffix) \ + INTRINSICCV_API(gaussian_blur_##kernel_suffix, \ + intrinsiccv_gaussian_blur_##kernel_suffix##_##type_suffix, \ + type) + +INTRINSICCV_GAUSSIAN_BLUR(uint8_t, 3x3, u8); +INTRINSICCV_GAUSSIAN_BLUR(uint8_t, 5x5, u8); + +using ElementTypes = ::testing::Types; + +template +class GaussianBlurTest : public testing::Test {}; + +TYPED_TEST_SUITE(GaussianBlurTest, ElementTypes); + +TYPED_TEST(GaussianBlurTest, NullPointer) { + intrinsiccv_filter_params_t params = { + .channels = 1, + .type_size = sizeof(TypeParam), + .workspace = nullptr, + }; + ASSERT_EQ(INTRINSICCV_OK, + intrinsiccv_filter_create(¶ms, intrinsiccv_rectangle_t{1, 1})); + TypeParam src[1] = {}, dst[1]; + test::test_null_args(gaussian_blur_3x3(), src, sizeof(TypeParam), + dst, sizeof(TypeParam), 1, 1, 1, + INTRINSICCV_BORDER_TYPE_REFLECT, ¶ms); + test::test_null_args(gaussian_blur_5x5(), src, sizeof(TypeParam), + dst, sizeof(TypeParam), 1, 1, 1, + INTRINSICCV_BORDER_TYPE_REFLECT, ¶ms); + EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_filter_release(¶ms)); + EXPECT_EQ(nullptr, params.workspace); + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + gaussian_blur_3x3()( + src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1, 1, + INTRINSICCV_BORDER_TYPE_REFLECT, ¶ms)); + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + gaussian_blur_5x5()( + src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1, 1, + INTRINSICCV_BORDER_TYPE_REFLECT, ¶ms)); +} diff --git a/test/api/test_merge.cpp b/test/api/test_merge.cpp index 4e2dd1e3c..a71cfa6f6 100644 --- a/test/api/test_merge.cpp +++ b/test/api/test_merge.cpp @@ -134,8 +134,8 @@ TYPED_TEST(Merge, FourChannels) { MergeTest().with_padding(test::Options::vector_length()).test(); } -TYPED_TEST(Merge, OneChannelNotImplemented) { - test_not_implemented(); +TYPED_TEST(Merge, OneChannelOutOfRange) { + test_not_implemented(INTRINSICCV_ERROR_RANGE); } TYPED_TEST(Merge, FiveChannelsNotImplemented) { @@ -143,3 +143,27 @@ TYPED_TEST(Merge, FiveChannelsNotImplemented) { } TEST(Merge128, NotImplemented) { test_not_implemented<__uint128_t, 2>(); } + +TYPED_TEST(Merge, NullPointer) { + const size_t kChannels = 4; + TypeParam src_arrays[kChannels]; + TypeParam dst[kChannels]; + size_t src_strides[kChannels] = {sizeof(TypeParam), sizeof(TypeParam), + sizeof(TypeParam), sizeof(TypeParam)}; + const void* srcs[kChannels] = {src_arrays, src_arrays + 1, src_arrays + 2, + src_arrays + 3}; + size_t dst_stride = kChannels * sizeof(TypeParam); + test::test_null_args(intrinsiccv_merge, srcs, src_strides, dst, dst_stride, 1, + 1, kChannels, sizeof(TypeParam)); + + for (int channels = 2; channels <= 4; ++channels) { + for (int null_src = 0; null_src < channels; ++null_src) { + for (int i = 0; i < channels; ++i) { + srcs[i] = (i == null_src) ? nullptr : src_arrays + i; + } + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + intrinsiccv_merge(srcs, src_strides, dst, dst_stride, 1, 1, + channels, sizeof(TypeParam))); + } + } +} diff --git a/test/api/test_min_max.cpp b/test/api/test_min_max.cpp index 71942bfb6..44cc54177 100644 --- a/test/api/test_min_max.cpp +++ b/test/api/test_min_max.cpp @@ -197,8 +197,9 @@ class MinMaxTest { if (p_max) { *p_max = std::numeric_limits::min(); } - min_max()(source.data(), source.stride(), width(), height(), - p_min, p_max); + EXPECT_EQ(INTRINSICCV_OK, + min_max()(source.data(), source.stride(), width(), + height(), p_min, p_max)); if (p_min) { EXPECT_EQ(*p_min, expected_min); } @@ -215,7 +216,13 @@ class MinMaxTest { setup(source, testData); - ElementType actual_min, actual_max; + ElementType actual_min = 2, actual_max = 1; + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + min_max()(nullptr, source.stride(), width(), + height(), &actual_min, &actual_max)); + EXPECT_EQ(2, actual_min); + EXPECT_EQ(1, actual_max); + one_test_call(source, nullptr, nullptr, 0, 0); one_test_call(source, &actual_min, nullptr, testData.expected_min, 0); one_test_call(source, nullptr, &actual_max, 0, testData.expected_max); @@ -261,7 +268,14 @@ class MinMaxLocTest : public MinMaxTest { setup(source, testData); - size_t min_offset = 0, max_offset = 0; + size_t min_offset = 2, max_offset = 1; + + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + min_max_loc()(nullptr, source.stride(), width(), + height(), &min_offset, &max_offset)); + EXPECT_EQ(2, min_offset); + EXPECT_EQ(1, max_offset); + one_test_call(source, nullptr, nullptr, 0, 0); one_test_call(source, &min_offset, nullptr, testData.expected_min_offset, 0); diff --git a/test/api/test_morphology.cpp b/test/api/test_morphology.cpp new file mode 100644 index 000000000..957df297b --- /dev/null +++ b/test/api/test_morphology.cpp @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "framework/operation.h" + +#define INTRINSICCV_DILATE(type, suffix) \ + INTRINSICCV_API(dilate, intrinsiccv_dilate_##suffix, type) + +#define INTRINSICCV_ERODE(type, suffix) \ + INTRINSICCV_API(erode, intrinsiccv_erode_##suffix, type) + +INTRINSICCV_DILATE(uint8_t, u8); +INTRINSICCV_ERODE(uint8_t, u8); + +template +class DilateTest : public testing::Test {}; + +template +class ErodeTest : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(DilateTest, ElementTypes); +TYPED_TEST_SUITE(ErodeTest, ElementTypes); + +static intrinsiccv_morphology_params_t make_minimal_params(size_t type_size) { + intrinsiccv_morphology_params_t params = { + .kernel = {1, 1}, + .anchor = {0, 0}, + .border_type = INTRINSICCV_BORDER_TYPE_REPLICATE, + .border_values = {0, 0, 1, 1}, + .channels = 1, + .iterations = 1, + .type_size = type_size, + .impl = nullptr, + .data = nullptr, + }; + return params; +} + +TYPED_TEST(DilateTest, NullPointer) { + intrinsiccv_morphology_params_t params = + make_minimal_params(sizeof(TypeParam)); + ASSERT_EQ(INTRINSICCV_OK, intrinsiccv_morphology_create( + ¶ms, intrinsiccv_rectangle_t{1, 1})); + TypeParam src[1] = {}, dst[1]; + test::test_null_args(dilate(), src, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1, ¶ms); + + { + intrinsiccv_morphology_params_t invalid_params = params; + invalid_params.impl = nullptr; + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + dilate()(src, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1, &invalid_params)); + } + + { + intrinsiccv_morphology_params_t invalid_params = params; + invalid_params.data = nullptr; + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + dilate()(src, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1, &invalid_params)); + } + + EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_morphology_release(¶ms)); +} + +TYPED_TEST(ErodeTest, NullPointer) { + intrinsiccv_morphology_params_t params = + make_minimal_params(sizeof(TypeParam)); + ASSERT_EQ(INTRINSICCV_OK, intrinsiccv_morphology_create( + ¶ms, intrinsiccv_rectangle_t{1, 1})); + + TypeParam src[1] = {}, dst[1]; + test::test_null_args(erode(), src, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1, ¶ms); + + { + intrinsiccv_morphology_params_t invalid_params = params; + invalid_params.impl = nullptr; + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + erode()(src, sizeof(TypeParam), dst, sizeof(TypeParam), + 1, 1, &invalid_params)); + } + + { + intrinsiccv_morphology_params_t invalid_params = params; + invalid_params.data = nullptr; + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + erode()(src, sizeof(TypeParam), dst, sizeof(TypeParam), + 1, 1, &invalid_params)); + } + + EXPECT_EQ(INTRINSICCV_OK, intrinsiccv_morphology_release(¶ms)); +} diff --git a/test/api/test_resize_to_quarter.cpp b/test/api/test_resize_to_quarter.cpp index b4cf33f53..128bf8d34 100644 --- a/test/api/test_resize_to_quarter.cpp +++ b/test/api/test_resize_to_quarter.cpp @@ -211,3 +211,10 @@ TEST(ResizeToQuarter, OddDimsTruncated) { size_t dst_height = src_height / 2; resize_test.execute_test(src_width, src_height, dst_width, dst_height); } + +TEST(ResizeToQuarter, NullPointer) { + const uint8_t src[4] = {}; + uint8_t dst[1]; + test::test_null_args(intrinsiccv_resize_to_quarter_u8, src, 2, 2, 2, dst, 1, + 1, 1); +} diff --git a/test/api/test_rgb_and_gray.cpp b/test/api/test_rgb_and_gray.cpp index b0fff439f..38426a2ef 100644 --- a/test/api/test_rgb_and_gray.cpp +++ b/test/api/test_rgb_and_gray.cpp @@ -33,6 +33,9 @@ class GrayTest final { ASSERT_EQ(INTRINSICCV_OK, err); EXPECT_EQ_ARRAY2D(actual, expected); + + test::test_null_args(impl, source.data(), source.stride(), actual.data(), + actual.stride(), logical_width, actual.height()); } private: @@ -85,6 +88,9 @@ class ColourTest final { ASSERT_EQ(INTRINSICCV_OK, err); EXPECT_EQ_ARRAY2D(actual, expected); + + test::test_null_args(impl, source.data(), source.stride(), actual.data(), + actual.stride(), logical_width, actual.height()); } private: diff --git a/test/api/test_saturating_absdiff.cpp b/test/api/test_saturating_absdiff.cpp index 0280de1c3..873c820d3 100644 --- a/test/api/test_saturating_absdiff.cpp +++ b/test/api/test_saturating_absdiff.cpp @@ -97,4 +97,8 @@ TYPED_TEST(SaturatingAbsDiff, API) { SaturatingAbsDiffTest{} .with_padding(test::Options::vector_length()) .test(); + + TypeParam src[1], dst[1]; + test::test_null_args(saturating_absdiff(), src, sizeof(TypeParam), + src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1); } diff --git a/test/api/test_saturating_add.cpp b/test/api/test_saturating_add.cpp index d202abcec..030b44056 100644 --- a/test/api/test_saturating_add.cpp +++ b/test/api/test_saturating_add.cpp @@ -89,4 +89,8 @@ TYPED_TEST(SaturatingAdd, API) { SaturatingAddTest{} .with_padding(test::Options::vector_length()) .test(); + + TypeParam src[1], dst[1]; + test::test_null_args(saturating_add(), src, sizeof(TypeParam), src, + sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1); } diff --git a/test/api/test_saturating_multiply.cpp b/test/api/test_saturating_multiply.cpp index 3ab896478..610128f73 100644 --- a/test/api/test_saturating_multiply.cpp +++ b/test/api/test_saturating_multiply.cpp @@ -97,4 +97,8 @@ TYPED_TEST(SaturatingMultiply, API) { SaturatingMultiplyTest{} .with_padding(test::Options::vector_length()) .test(); + + TypeParam src[1], dst[1]; + test::test_null_args(saturating_multiply(), src, sizeof(TypeParam), + src, sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1, 1); } diff --git a/test/api/test_saturating_sub.cpp b/test/api/test_saturating_sub.cpp index 4ab3c7bb1..1c78f4da7 100644 --- a/test/api/test_saturating_sub.cpp +++ b/test/api/test_saturating_sub.cpp @@ -91,4 +91,8 @@ TYPED_TEST(SaturatingSub, API) { SaturatingSubTest{} .with_padding(test::Options::vector_length()) .test(); + + TypeParam src[1], dst[1]; + test::test_null_args(saturating_sub(), src, sizeof(TypeParam), src, + sizeof(TypeParam), dst, sizeof(TypeParam), 1, 1); } diff --git a/test/api/test_scale.cpp b/test/api/test_scale.cpp new file mode 100644 index 000000000..f1caaf9b6 --- /dev/null +++ b/test/api/test_scale.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2024 Arm Limited and/or its affiliates +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include + +#include "framework/operation.h" + +#define INTRINSICCV_SCALE(type, suffix) \ + INTRINSICCV_API(scale, intrinsiccv_scale_##suffix, type) + +INTRINSICCV_SCALE(uint8_t, u8); + +template +class ScaleTest : public testing::Test {}; + +using ElementTypes = ::testing::Types; +TYPED_TEST_SUITE(ScaleTest, ElementTypes); + +TYPED_TEST(ScaleTest, NullPointer) { + TypeParam src[1] = {}, dst[1]; + test::test_null_args(scale(), src, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1, 2, 0); +} diff --git a/test/api/test_sobel.cpp b/test/api/test_sobel.cpp index b0370b26a..fdb8fa6d9 100644 --- a/test/api/test_sobel.cpp +++ b/test/api/test_sobel.cpp @@ -88,6 +88,11 @@ TYPED_TEST(Sobel, Horizontal3x3) { mask.set(1, 0, {-2, 0, 2}); mask.set(2, 0, {-1, 0, 1}); Sobel3x3Test{}.test(mask); + + typename KernelTestParams::InputType src[1] = {}; + typename KernelTestParams::OutputType dst[1]; + test::test_null_args(sobel_3x3_horizontal(), src, sizeof(src), dst, + sizeof(dst), 1, 1, 1); } // Tests sobel_3x3_vertical__ API. @@ -101,4 +106,9 @@ TYPED_TEST(Sobel, Vertical3x3) { mask.set(2, 0, { 1, 2, 1}); // clang-format on Sobel3x3Test{}.test(mask); + + typename KernelTestParams::InputType src[1] = {}; + typename KernelTestParams::OutputType dst[1]; + test::test_null_args(sobel_3x3_vertical(), src, sizeof(src), dst, + sizeof(dst), 1, 1, 1); } diff --git a/test/api/test_split.cpp b/test/api/test_split.cpp index 652bb3d65..91f36334b 100644 --- a/test/api/test_split.cpp +++ b/test/api/test_split.cpp @@ -133,8 +133,8 @@ TYPED_TEST(Split, FourChannels) { SplitTest().with_padding(test::Options::vector_length()).test(); } -TYPED_TEST(Split, OneChannelNotImplemented) { - test_not_implemented(); +TYPED_TEST(Split, OneChannelOutOfRange) { + test_not_implemented(INTRINSICCV_ERROR_RANGE); } TYPED_TEST(Split, FiveChannelsNotImplemented) { @@ -142,3 +142,29 @@ TYPED_TEST(Split, FiveChannelsNotImplemented) { } TEST(Split128, NotImplemented) { test_not_implemented<__uint128_t, 2>(); } + +TYPED_TEST(Split, NullPointer) { + const size_t kChannels = 4; + + TypeParam src_data[kChannels]; + size_t src_stride = kChannels * sizeof(TypeParam); + TypeParam dst_arrays[kChannels]; + void* dst_data[kChannels] = {dst_arrays, dst_arrays + 1, dst_arrays + 2, + dst_arrays + 3}; + size_t dst_strides[kChannels] = {sizeof(TypeParam), sizeof(TypeParam), + sizeof(TypeParam), sizeof(TypeParam)}; + + test::test_null_args(intrinsiccv_split, src_data, src_stride, dst_data, + dst_strides, 1, 1, kChannels, sizeof(TypeParam)); + + for (int channels = 2; channels <= 4; ++channels) { + for (int null_src = 0; null_src < channels; ++null_src) { + for (int i = 0; i < channels; ++i) { + dst_data[i] = (i == null_src) ? nullptr : dst_arrays + i; + } + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, + intrinsiccv_split(src_data, src_stride, dst_data, dst_strides, + 1, 1, channels, sizeof(TypeParam))); + } + } +} diff --git a/test/api/test_threshold_binary.cpp b/test/api/test_threshold_binary.cpp index 5ca3651d2..89c7366e6 100644 --- a/test/api/test_threshold_binary.cpp +++ b/test/api/test_threshold_binary.cpp @@ -119,3 +119,10 @@ TYPED_TEST(ThresholdBinary, TestZero) { TYPED_TEST(ThresholdBinary, TestMax) { ThresholdBinaryTestMax{}.test(); } + +TYPED_TEST(ThresholdBinary, NullPointer) { + const TypeParam src[1] = {}; + TypeParam dst[1]; + test::test_null_args(intrinsiccv_threshold_binary_u8, src, sizeof(TypeParam), + dst, sizeof(TypeParam), 1, 1, 1, 1); +} diff --git a/test/api/test_transpose.cpp b/test/api/test_transpose.cpp index bea30d121..94ec882ee 100644 --- a/test/api/test_transpose.cpp +++ b/test/api/test_transpose.cpp @@ -122,3 +122,9 @@ TYPED_TEST(Transpose, VectorInplaceWithPadding) { TestTranspose test(1); test.vector_test(); } + +TYPED_TEST(Transpose, NullPointer) { + TypeParam src[1] = {}, dst[1]; + test::test_null_args(intrinsiccv_transpose, src, sizeof(TypeParam), dst, + sizeof(TypeParam), 1, 1, sizeof(TypeParam)); +} diff --git a/test/api/test_yuv_to_rgb.cpp b/test/api/test_yuv_to_rgb.cpp index ac0c31f53..7f4dc641c 100644 --- a/test/api/test_yuv_to_rgb.cpp +++ b/test/api/test_yuv_to_rgb.cpp @@ -62,6 +62,11 @@ class YuvTest final { ASSERT_EQ(INTRINSICCV_OK, err); EXPECT_EQ_ARRAY2D(expected, actual); + + test::test_null_args(impl, input_y.data(), input_y.stride(), + input_uv.data(), input_uv.stride(), actual.data(), + actual.stride(), expected.width() / channel_number_, + expected.height(), is_nv21); } void calculate_expected(test::Array2D &y_arr, diff --git a/test/framework/utils.h b/test/framework/utils.h index a9ed38cb0..e266a3a8f 100644 --- a/test/framework/utils.h +++ b/test/framework/utils.h @@ -10,8 +10,10 @@ #include #include #include +#include #include +#include "ctypes.h" #include "framework/abstract.h" #include "framework/types.h" @@ -84,6 +86,58 @@ void dump(const TwoDimensional *elements); std::array default_array_layouts(size_t min_width, size_t min_height); +namespace internal { +template +class NullPointerTester { + // Set the given argument to null and test that the function diagnoses the + // error correctly. + template + static typename std::enable_if>::type + test_with_null_arg(Function f, Tuple t) { + std::get(t) = nullptr; + EXPECT_EQ(INTRINSICCV_ERROR_NULL_POINTER, std::apply(f, t)); + } + + // Skip arguments that aren't pointers. + template + static typename std::enable_if>::type + test_with_null_arg(Function, const Tuple &) {} + + public: + template + static void test(Function f, const Tuple &t) { + // Recurse to test earlier arguments first + test(f, t); + using ArgType = typename std::tuple_element_t; + test_with_null_arg(f, t); + } + + // Terminate recursion + template <> + static void test<-1>(Function, const Tuple &) {} +}; + +template +class ParamsExtractor; +template +class ParamsExtractor { + public: + using Tuple = std::tuple; +}; +} // namespace internal + +// Tests that the function returns INTRINSICCV_ERROR_NULL_POINTER if any of its +// pointer arguments are null. +template +void test_null_args(Function f, Args... args) { + // Ensure that the function parameter types are used otherwise arguments may + // not be recognised as pointers. + using Tuple = typename internal::ParamsExtractor::Tuple; + constexpr int LastArgIndex = std::tuple_size_v - 1; + using Tester = internal::NullPointerTester; + Tester::template test(f, Tuple(args...)); +} + } // namespace test #endif // INTRINSICCV_TEST_FRAMEWORK_UTILS_H_ -- GitLab