CVE-2018-20542: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: There is a heap-based buffer-overflow at generator_spgemm_csc_reader.c (function libxsmm_sparse_csc_reader) in LIBXSMM 1.10, a different vulnerability than CVE-2018-20541 (which is in a different part of the source code and is seen at a different address).
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: 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