Skip to content

Commit

Permalink
Limit the golomb coding parameter to 16
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vbaderks committed Jan 3, 2021
1 parent ea5cbc6 commit 6a49aea
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 22 deletions.
2 changes: 2 additions & 0 deletions samples/convert.cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,10 @@ struct options final
options(const int argc, char** argv)
{
if (argc < 3)
{
throw std::runtime_error("Usage: <input_filename> <output_filename> [interleave-mode (none, line, or sample), "
"default = none] [near-lossless, default = 0 (lossless)]\n");
}

input_filename = argv[1];
output_filename = argv[2];
Expand Down
2 changes: 2 additions & 0 deletions src/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 11 additions & 18 deletions src/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,20 @@ struct jls_context final
ASSERT(N != 0);
}

FORCE_INLINE int32_t get_golomb_code() const noexcept
/// <summary>
/// <para>Computes the Golomb coding parameter using the algorithm as defined in ISO/IEC 14495-1, code segment A.10 </para>
/// <para>Original algorithm is: for (k = 0; (N[Q] << k) < A[Q]; k++) </para>
/// </summary>
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;
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/jpegls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ unique_ptr<Strategy> 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<golomb_code_table, 16> decoding_tables{
const array<golomb_code_table, max_k_value> 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),
Expand Down
7 changes: 4 additions & 3 deletions src/scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -21,7 +22,7 @@ namespace charls {
class decoder_strategy;
class encoder_strategy;

extern const std::array<golomb_code_table, 16> decoding_tables;
extern const std::array<golomb_code_table, max_k_value> decoding_tables;
extern const std::vector<int8_t> quantization_lut_lossless_8;
extern const std::vector<int8_t> quantization_lut_lossless_10;
extern const std::vector<int8_t> quantization_lut_lossless_12;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))};

Expand Down

0 comments on commit 6a49aea

Please sign in to comment.