From 6a49aeabb02cfe9594ea89498c1ba64632a85dc0 Mon Sep 17 00:00:00 2001 From: Victor Derks Date: Sun, 3 Jan 2021 12:26:33 +0100 Subject: [PATCH] Limit the golomb coding parameter to 16 The CharLS implementation has only 16 Golomb tables. During fuzzy testing a jpeg-ls stream was found that could create a k value of 16 and thus an out of bound condition. Ensure that this condition will generate an invalid data exception. Note 1: The compiler (verified with compiler explorer) will auto optimize the algorithm. There is no need to perform manual auto tuning. Note 2: The JPEG-LS reference implementation uses a max k of 24. More analysis is needed if the number of predefined tables need to be extended. --- samples/convert.cpp/main.cpp | 2 ++ src/constants.h | 2 ++ src/context.h | 29 +++++++++++------------------ src/jpegls.cpp | 2 +- src/scan.h | 7 ++++--- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/samples/convert.cpp/main.cpp b/samples/convert.cpp/main.cpp index 1db0ea5c..082d9a7d 100644 --- a/samples/convert.cpp/main.cpp +++ b/samples/convert.cpp/main.cpp @@ -147,8 +147,10 @@ struct options final options(const int argc, char** argv) { if (argc < 3) + { throw std::runtime_error("Usage: [interleave-mode (none, line, or sample), " "default = none] [near-lossless, default = 0 (lossless)]\n"); + } input_filename = argv[1]; output_filename = argv[2]; diff --git a/src/constants.h b/src/constants.h index 7c0f6c1d..c9194165 100644 --- a/src/constants.h +++ b/src/constants.h @@ -24,6 +24,8 @@ constexpr int minimum_bits_per_sample{2}; constexpr int maximum_bits_per_sample{16}; constexpr int maximum_near_lossless{255}; +constexpr int max_k_value{16}; // This is an implementation limit (theoretical limit is 32) + constexpr int compute_maximum_near_lossless(const int maximum_sample_value) noexcept { return std::min(255, maximum_sample_value / 2); // As defined by ISO/IEC 14495-1, C.2.3 diff --git a/src/context.h b/src/context.h index ba057ad2..eb46b393 100644 --- a/src/context.h +++ b/src/context.h @@ -80,27 +80,20 @@ struct jls_context final ASSERT(N != 0); } - FORCE_INLINE int32_t get_golomb_code() const noexcept + /// + /// Computes the Golomb coding parameter using the algorithm as defined in ISO/IEC 14495-1, code segment A.10 + /// Original algorithm is: for (k = 0; (N[Q] << k) < A[Q]; k++) + /// + FORCE_INLINE int32_t get_golomb_coding_parameter() const { - const int32_t n_test{N}; - const int32_t a_test{A}; - - if (n_test >= a_test) - return 0; - if (n_test << 1 >= a_test) - return 1; - if (n_test << 2 >= a_test) - return 2; - if (n_test << 3 >= a_test) - return 3; - if (n_test << 4 >= a_test) - return 4; - - int32_t k{5}; - for (; n_test << k < a_test; ++k) + int32_t k{0}; + for (; N << k < A && k < max_k_value; ++k) { - ASSERT(k <= 32); } + + if (k == max_k_value) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); + return k; } }; diff --git a/src/jpegls.cpp b/src/jpegls.cpp index d588aa6e..c90ea7b7 100644 --- a/src/jpegls.cpp +++ b/src/jpegls.cpp @@ -74,7 +74,7 @@ unique_ptr make_codec(const Traits& traits, const frame_info& frame_in // Lookup table: decode symbols that are smaller or equal to 8 bit (16 tables for each value of k) // NOLINTNEXTLINE(clang-diagnostic-global-constructors) -const array decoding_tables{ +const array decoding_tables{ initialize_table(0), initialize_table(1), initialize_table(2), initialize_table(3), initialize_table(4), initialize_table(5), initialize_table(6), initialize_table(7), initialize_table(8), initialize_table(9), initialize_table(10), initialize_table(11), diff --git a/src/scan.h b/src/scan.h index 320d7167..157f7167 100644 --- a/src/scan.h +++ b/src/scan.h @@ -5,6 +5,7 @@ #include "coding_parameters.h" #include "color_transform.h" +#include "constants.h" #include "context.h" #include "context_run_mode.h" #include "lookup_table.h" @@ -21,7 +22,7 @@ namespace charls { class decoder_strategy; class encoder_strategy; -extern const std::array decoding_tables; +extern const std::array decoding_tables; extern const std::vector quantization_lut_lossless_8; extern const std::vector quantization_lut_lossless_10; extern const std::vector quantization_lut_lossless_12; @@ -323,7 +324,7 @@ class jls_codec final : public Strategy { const int32_t sign = bit_wise_sign(qs); jls_context& context = contexts_[apply_sign(qs, sign)]; - const int32_t k = context.get_golomb_code(); + const int32_t k = context.get_golomb_coding_parameter(); const int32_t predicted_value = traits_.correct_prediction(predicted + apply_sign(context.C, sign)); int32_t error_value; @@ -353,7 +354,7 @@ class jls_codec final : public Strategy { const int32_t sign{bit_wise_sign(qs)}; jls_context& context{contexts_[apply_sign(qs, sign)]}; - const int32_t k{context.get_golomb_code()}; + const int32_t k{context.get_golomb_coding_parameter()}; const int32_t predicted_value{traits_.correct_prediction(predicted + apply_sign(context.C, sign))}; const int32_t error_value{traits_.compute_error_value(apply_sign(x - predicted_value, sign))};