CVE-2018-20541: Out-of-bounds Write (CWE-787)
value: There is a heap-based buffer overflow in libxsmm_sparse_csc_reader at generator_spgemm_csc_reader.c in LIBXSMM 1.10, a different vulnerability than CVE-2018-20542 (which is in a different part of the source code and is seen at different addresses).
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 8.8
Published: 2018-12-28T16:29Z
Repository: https://github.com/hfp/libxsmm
Language: C
Files changed: samples/edge/common_edge_proxy.h, samples/edge/edge_proxy_common.c, samples/pyfr/pyfr_driver_asp_reg.c, src/generator_spgemm_csc_reader.c, src/generator_spgemm_csr_reader.c, version.txt
Commit date: 2018-11-26 15:28:06 +0100
Commit message: Issue #287: made CSR/CSC readers more robust against invalid input (case #1).

Diff: diff --git a/samples/edge/common_edge_proxy.h b/samples/edge/common_edge_proxy.h
index 13d525b62..22b95e3bb 100644
--- a/samples/edge/common_edge_proxy.h
+++ b/samples/edge/common_edge_proxy.h
@@ -76,11 +76,13 @@ static void libxsmm_sparse_csr_reader( const char*    i_csr_file_in,
     if ( l_line[0] == '%' ) {
       continue;
     } else {
       /* if we are the first line after comment header, we allocate our data structures */
       if ( l_header_read == 0 ) {
-        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
+        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
+            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
+        {
           /* allocate CSC datastructure matching mtx file */
           *o_column_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_element_count));
           *o_row_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_row_count + 1));
           *o_values = (REALTYPE*) malloc(sizeof(double) * (*o_element_count));
           l_row_idx_id = (unsigned int*) malloc(sizeof(unsigned int) * (*o_row_count));
diff --git a/samples/edge/edge_proxy_common.c b/samples/edge/edge_proxy_common.c
index 0583f53d8..24a2ed5a3 100644
--- a/samples/edge/edge_proxy_common.c
+++ b/samples/edge/edge_proxy_common.c
@@ -60,11 +60,13 @@ void edge_sparse_csr_reader_double( const char*           i_csr_file_in,
     if ( l_line[0] == '%' ) {
       continue;
     } else {
       /* if we are the first line after comment header, we allocate our data structures */
       if ( l_header_read == 0 ) {
-        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
+        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
+            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
+        {
           /* allocate CSC datastructure matching mtx file */
           *o_column_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_element_count));
           *o_row_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_row_count + 1));
           *o_values = (double*) malloc(sizeof(double) * (*o_element_count));
           l_row_idx_id = (unsigned int*) malloc(sizeof(unsigned int) * (*o_row_count));
diff --git a/samples/pyfr/pyfr_driver_asp_reg.c b/samples/pyfr/pyfr_driver_asp_reg.c
index ffef8e71d..10e196f5f 100644
--- a/samples/pyfr/pyfr_driver_asp_reg.c
+++ b/samples/pyfr/pyfr_driver_asp_reg.c
@@ -73,11 +73,13 @@ int my_csr_reader( const char*           i_csr_file_in,
     if ( l_line[0] == '%' ) {
       continue;
     } else {
       /* if we are the first line after comment header, we allocate our data structures */
       if ( l_header_read == 0 ) {
-        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
+        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
+            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
+        {
           /* allocate CSC datastructure matching mtx file */
           *o_column_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_element_count));
           *o_row_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_row_count + 1));
           *o_values = (REALTYPE*) malloc(sizeof(double) * (*o_element_count));
           l_row_idx_id = (unsigned int*) malloc(sizeof(unsigned int) * (*o_row_count));
diff --git a/src/generator_spgemm_csc_reader.c b/src/generator_spgemm_csc_reader.c
index b6c94e5ba..113cbbe8f 100644
--- a/src/generator_spgemm_csc_reader.c
+++ b/src/generator_spgemm_csc_reader.c
@@ -115,11 +115,13 @@ void libxsmm_sparse_csc_reader( libxsmm_generated_code* io_generated_code,
     if ( l_line[0] == '%' ) {
       continue;
     } else {
       /* if we are the first line after comment header, we allocate our data structures */
       if ( l_header_read == 0 ) {
-        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
+        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
+            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
+        {
           /* allocate CSC data structure matching mtx file */
           *o_row_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_element_count));
           *o_column_idx = (unsigned int*) malloc(sizeof(unsigned int) * ((size_t)(*o_column_count) + 1));
           *o_values = (double*) malloc(sizeof(double) * (*o_element_count));
           l_column_idx_id = (unsigned int*) malloc(sizeof(unsigned int) * (*o_column_count));
@@ -166,12 +168,12 @@ void libxsmm_sparse_csc_reader( libxsmm_generated_code* io_generated_code,
           fclose(l_csc_file_handle); /* close mtx file */
           LIBXSMM_HANDLE_ERROR( io_generated_code, LIBXSMM_ERR_CSC_READ_ELEMS );
           return;
         }
         /* adjust numbers to zero termination */
-        l_row--;
-        l_column--;
+        LIBXSMM_ASSERT(0 != l_row && 0 != l_column);
+        l_row--; l_column--;
         /* add these values to row and value structure */
         (*o_row_idx)[l_i] = l_row;
         (*o_values)[l_i] = l_value;
         l_i++;
         /* handle columns, set id to own for this column, yeah we need to handle empty columns */
@@ -203,6 +205,5 @@ void libxsmm_sparse_csc_reader( libxsmm_generated_code* io_generated_code,
     /* free helper data structure */
     free( l_column_idx_id );
   }
 }
 
-
diff --git a/src/generator_spgemm_csr_reader.c b/src/generator_spgemm_csr_reader.c
index 471f34c70..aaeda2eb2 100644
--- a/src/generator_spgemm_csr_reader.c
+++ b/src/generator_spgemm_csr_reader.c
@@ -76,11 +76,13 @@ void libxsmm_sparse_csr_reader( libxsmm_generated_code* io_generated_code,
     if ( l_line[0] == '%' ) {
       continue;
     } else {
       /* if we are the first line after comment header, we allocate our data structures */
       if ( l_header_read == 0 ) {
-        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
+        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
+            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
+        {
           /* allocate CSC data-structure matching mtx file */
           *o_column_idx = (unsigned int*) malloc(sizeof(unsigned int) * (*o_element_count));
           *o_row_idx = (unsigned int*) malloc(sizeof(unsigned int) * ((size_t)(*o_row_count) + 1));
           *o_values = (double*) malloc(sizeof(double) * (*o_element_count));
           l_row_idx_id = (unsigned int*) malloc(sizeof(unsigned int) * (*o_row_count));
@@ -127,12 +129,12 @@ void libxsmm_sparse_csr_reader( libxsmm_generated_code* io_generated_code,
           fclose(l_csr_file_handle); /* close mtx file */
           LIBXSMM_HANDLE_ERROR( io_generated_code, LIBXSMM_ERR_CSR_READ_ELEMS );
           return;
         }
         /* adjust numbers to zero termination */
-        l_row--;
-        l_column--;
+        LIBXSMM_ASSERT(0 != l_row && 0 != l_column);
+        l_row--; l_column--;
         /* add these values to row and value structure */
         (*o_column_idx)[l_i] = l_column;
         (*o_values)[l_i] = l_value;
         l_i++;
         /* handle columns, set id to own for this column, yeah we need to handle empty columns */
diff --git a/version.txt b/version.txt
index 3acbfb0b3..88b5f2717 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-master-1.10-47
+master-1.10-48

Vulnerable code:         if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
        l_row--;
        l_column--;

        if ( sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) == 3 ) {
        l_row--;
        l_column--;
master-1.10-47
Fixed code:         if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
        {
        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
        {
        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
        {
        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
        {
        LIBXSMM_ASSERT(0 != l_row && 0 != l_column);
        l_row--; l_column--;
        if (3 == sscanf(l_line, "%u %u %u", o_row_count, o_column_count, o_element_count) &&
            0 != *o_row_count && 0 != *o_column_count && 0 != *o_element_count)
        {
        LIBXSMM_ASSERT(0 != l_row && 0 != l_column);
        l_row--; l_column--;
master-1.10-48