CVE-2017-14151: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: An off-by-one error was discovered in opj_tcd_code_block_enc_allocate_data in lib/openjp2/tcd.c in OpenJPEG 2.2.0. The vulnerability causes an out-of-bounds write, which may lead to remote denial of service (heap-based buffer overflow affecting opj_mqc_flush in lib/openjp2/mqc.c and opj_t1_encode_cblk in lib/openjp2/t1.c) or possibly remote code execution.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 8.8
Published: 2017-09-05T16:29Z
Repository: https://github.com/uclouvain/openjpeg
Language: C
Files changed: src/lib/openjp2/tcd.c, tests/nonregression/test_suite.ctest.in
Commit date: 2017-08-14 17:20:37 +0200
Commit message: Encoder: grow buffer size in opj_tcd_code_block_enc_allocate_data() to avoid write heap buffer overflow in opj_mqc_flush (#982)

Diff: diff --git a/src/lib/openjp2/tcd.c b/src/lib/openjp2/tcd.c
index 301c7213e..53cdcf64d 100644
--- a/src/lib/openjp2/tcd.c
+++ b/src/lib/openjp2/tcd.c
@@ -1185,12 +1185,15 @@ static OPJ_BOOL opj_tcd_code_block_enc_allocate(opj_tcd_cblk_enc_t *
 static OPJ_BOOL opj_tcd_code_block_enc_allocate_data(opj_tcd_cblk_enc_t *
         p_code_block)
 {
     OPJ_UINT32 l_data_size;
 
-    /* The +1 is needed for https://github.com/uclouvain/openjpeg/issues/835 */
-    l_data_size = 1 + (OPJ_UINT32)((p_code_block->x1 - p_code_block->x0) *
+    /* +1 is needed for https://github.com/uclouvain/openjpeg/issues/835 */
+    /* and actually +2 required for https://github.com/uclouvain/openjpeg/issues/982 */
+    /* TODO: is there a theoretical upper-bound for the compressed code */
+    /* block size ? */
+    l_data_size = 2 + (OPJ_UINT32)((p_code_block->x1 - p_code_block->x0) *
                                    (p_code_block->y1 - p_code_block->y0) * (OPJ_INT32)sizeof(OPJ_UINT32));
 
     if (l_data_size > p_code_block->data_size) {
         if (p_code_block->data) {
             /* We refer to data - 1 since below we incremented it */
diff --git a/tests/nonregression/test_suite.ctest.in b/tests/nonregression/test_suite.ctest.in
index aaf40d7d0..ffd964c2a 100644
--- a/tests/nonregression/test_suite.ctest.in
+++ b/tests/nonregression/test_suite.ctest.in
@@ -167,10 +167,12 @@ opj_compress -i @INPUT_NR_PATH@/Bretagne2.ppm -o @TEMP_PATH@/Bretagne2_bypass_vs
 # Test fix for #891/#892 (tiles smaller than decomposition levels)
 opj_compress -i @INPUT_NR_PATH@/Bretagne2.ppm -o @TEMP_PATH@/Bretagne2_empty_band.j2k -t 2591,1943 -n 2
 # Same rate as Bretagne2_4.j2k
 opj_compress -i @INPUT_NR_PATH@/Bretagne2.ppm -o @TEMP_PATH@/Bretagne2_empty_band_r800.j2k -t 2591,1943 -n 2 -r 800
 
+opj_compress -i @INPUT_NR_PATH@/issue982.bmp -o @TEMP_PATH@/issue982.j2k -n 1
+
 # DECODER TEST SUITE
 opj_decompress -i  @INPUT_NR_PATH@/Bretagne2.j2k -o @TEMP_PATH@/Bretagne2.j2k.pgx
 opj_decompress -i  @INPUT_NR_PATH@/_00042.j2k -o @TEMP_PATH@/_00042.j2k.pgx
 opj_decompress -i  @INPUT_NR_PATH@/123.j2c -o @TEMP_PATH@/123.j2c.pgx
 # The 4 following tests should failed (kakadu indicates that they are corrupted)

Vulnerable code:     /* The +1 is needed for https://github.com/uclouvain/openjpeg/issues/835 */
    l_data_size = 1 + (OPJ_UINT32)((p_code_block->x1 - p_code_block->x0) *
Fixed code:     /* +1 is needed for https://github.com/uclouvain/openjpeg/issues/835 */
    /* and actually +2 required for https://github.com/uclouvain/openjpeg/issues/982 */
    /* TODO: is there a theoretical upper-bound for the compressed code */
    /* block size ? */
    l_data_size = 2 + (OPJ_UINT32)((p_code_block->x1 - p_code_block->x0) *
opj_compress -i @INPUT_NR_PATH@/issue982.bmp -o @TEMP_PATH@/issue982.j2k -n 1