CVE-2017-14164: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: A size-validation issue was discovered in opj_j2k_write_sot in lib/openjp2/j2k.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_write_bytes_LE in lib/openjp2/cio.c) or possibly remote code execution. NOTE: this vulnerability exists because of an incomplete fix for CVE-2017-14152.
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-06T18:29Z
Repository: https://github.com/uclouvain/openjpeg
Language: C
Files changed: src/lib/openjp2/j2k.c
Commit date: 2017-08-16 17:09:10 +0200
Commit message: opj_j2k_write_sot(): fix potential write heap buffer overflow (#991)

Diff: diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c
index 54b490a8c..16915452e 100644
--- a/src/lib/openjp2/j2k.c
+++ b/src/lib/openjp2/j2k.c
@@ -830,17 +830,19 @@ static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
 
 /**
  * Writes the SOT marker (Start of tile-part)
  *
  * @param       p_j2k            J2K codec.
- * @param       p_data           FIXME DOC
- * @param       p_data_written   FIXME DOC
+ * @param       p_data           Output buffer
+ * @param       p_total_data_size Output buffer size
+ * @param       p_data_written   Number of bytes written into stream
  * @param       p_stream         the stream to write data to.
  * @param       p_manager        the user event manager.
 */
 static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
                                   OPJ_BYTE * p_data,
+                                  OPJ_UINT32 p_total_data_size,
                                   OPJ_UINT32 * p_data_written,
                                   const opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager);
 
 /**
@@ -4199,10 +4201,11 @@ static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
     return OPJ_TRUE;
 }
 
 static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
                                   OPJ_BYTE * p_data,
+                                  OPJ_UINT32 p_total_data_size,
                                   OPJ_UINT32 * p_data_written,
                                   const opj_stream_private_t *p_stream,
                                   opj_event_mgr_t * p_manager
                                  )
 {
@@ -4212,10 +4215,16 @@ static OPJ_BOOL opj_j2k_write_sot(opj_j2k_t *p_j2k,
     assert(p_stream != 00);
 
     OPJ_UNUSED(p_stream);
     OPJ_UNUSED(p_manager);
 
+    if (p_total_data_size < 12) {
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "Not enough bytes in output buffer to write SOT marker\n");
+        return OPJ_FALSE;
+    }
+
     opj_write_bytes(p_data, J2K_MS_SOT,
                     2);                                 /* SOT */
     p_data += 2;
 
     opj_write_bytes(p_data, 10,
@@ -11478,11 +11487,12 @@ static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
     /* INDEX >> */
     /* << INDEX */
 
     l_current_nb_bytes_written = 0;
     l_begin_data = p_data;
-    if (! opj_j2k_write_sot(p_j2k, p_data, &l_current_nb_bytes_written, p_stream,
+    if (! opj_j2k_write_sot(p_j2k, p_data, p_total_data_size,
+                            &l_current_nb_bytes_written, p_stream,
                             p_manager)) {
         return OPJ_FALSE;
     }
 
     l_nb_bytes_written += l_current_nb_bytes_written;
@@ -11570,11 +11580,14 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
         p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = tilepartno;
         l_current_nb_bytes_written = 0;
         l_part_tile_size = 0;
         l_begin_data = p_data;
 
-        if (! opj_j2k_write_sot(p_j2k, p_data, &l_current_nb_bytes_written, p_stream,
+        if (! opj_j2k_write_sot(p_j2k, p_data,
+                                p_total_data_size,
+                                &l_current_nb_bytes_written,
+                                p_stream,
                                 p_manager)) {
             return OPJ_FALSE;
         }
 
         l_nb_bytes_written += l_current_nb_bytes_written;
@@ -11613,11 +11626,13 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
             p_j2k->m_specific_param.m_encoder.m_current_poc_tile_part_number = tilepartno;
             l_current_nb_bytes_written = 0;
             l_part_tile_size = 0;
             l_begin_data = p_data;
 
-            if (! opj_j2k_write_sot(p_j2k, p_data, &l_current_nb_bytes_written, p_stream,
+            if (! opj_j2k_write_sot(p_j2k, p_data,
+                                    p_total_data_size,
+                                    &l_current_nb_bytes_written, p_stream,
                                     p_manager)) {
                 return OPJ_FALSE;
             }
 
             l_nb_bytes_written += l_current_nb_bytes_written;

Vulnerable code:  * @param       p_data           FIXME DOC
 * @param       p_data_written   FIXME DOC
    if (! opj_j2k_write_sot(p_j2k, p_data, &l_current_nb_bytes_written, p_stream,
        if (! opj_j2k_write_sot(p_j2k, p_data, &l_current_nb_bytes_written, p_stream,
            if (! opj_j2k_write_sot(p_j2k, p_data, &l_current_nb_bytes_written, p_stream,
Fixed code:  * @param       p_data           Output buffer
 * @param       p_total_data_size Output buffer size
 * @param       p_data_written   Number of bytes written into stream
                                  OPJ_UINT32 p_total_data_size,
                                  OPJ_UINT32 p_total_data_size,
    if (p_total_data_size < 12) {
        opj_event_msg(p_manager, EVT_ERROR,
                      "Not enough bytes in output buffer to write SOT marker\n");
        return OPJ_FALSE;
    }

    if (! opj_j2k_write_sot(p_j2k, p_data, p_total_data_size,
                            &l_current_nb_bytes_written, p_stream,
        if (! opj_j2k_write_sot(p_j2k, p_data,
                                p_total_data_size,
                                &l_current_nb_bytes_written,
                                p_stream,
            if (! opj_j2k_write_sot(p_j2k, p_data,
                                    p_total_data_size,
                                    &l_current_nb_bytes_written, p_stream,