changeset 21419:13415264b9f8

Fix out-of-bounds memory access in ichol, ilu (bug #46449). Clean up code, add more comments. * __ichol__.cc: Test for an empty column (j1 == j2) before trying to find the row index (ridx[j1]). Add more comments. Rename variable param_list to arg_list. Capitalize Hermitian in error message. * __ilu__.cc: Test for an empty column (j1 == j2) and raise an error about a 0 on the diagonal. Add more comments. Rename variable param_list to arg_list. Re-arrange declaration of variables to be clearer.
author Rik <rik@octave.org>
date Wed, 09 Mar 2016 09:55:32 -0800
parents ca8450b9ef5b
children 29e8d4a922b5
files libinterp/corefcn/__ichol__.cc libinterp/corefcn/__ilu__.cc
diffstat 2 files changed, 123 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/__ichol__.cc	Tue Mar 08 13:52:03 2016 -0800
+++ b/libinterp/corefcn/__ichol__.cc	Wed Mar 09 09:55:32 2016 -0800
@@ -52,7 +52,7 @@
 bool ichol_checkpivot_complex (Complex pivot)
 {
   if (pivot.imag () != 0)
-    error ("ichol: non-real pivot encountered.  The matrix must be hermitian.");
+    error ("ichol: non-real pivot encountered.  The matrix must be Hermitian.");
   else if (pivot.real () < 0)
     error ("ichol: negative pivot encountered");
 
@@ -71,10 +71,10 @@
           bool (*ichol_checkpivot) (T)>
 void ichol_0 (octave_matrix_t& sm, const std::string michol = "off")
 {
-
   const octave_idx_type n = sm.cols ();
   octave_idx_type j1, jend, j2, jrow, jjrow, j, jw, i, k, jj, r;
   T tl;
+
   char opt;
   enum {OFF, ON};
   if (michol == "on")
@@ -102,7 +102,7 @@
       dropsums[i] = 0;
     }
 
-  // Main loop
+  // Loop over all columns
   for (k = 0; k < n; k++)
     {
       j1 = cidx[k];
@@ -110,7 +110,7 @@
       for (j = j1; j < j2; j++)
         iw[ridx[j]] = j;
 
-      jrow = Llist [k];
+      jrow = Llist[k];
       // Iterate over each non-zero element in the actual row.
       while (jrow != -1)
         {
@@ -148,7 +148,8 @@
       if (opt == ON)
         data[j1] += dropsums[k];
 
-      if (ridx[j1] != k)
+      // Test for j1 == j2 must be first to avoid invalid ridx[j1] access
+      if (j1 == j2 || ridx[j1] != k)
         error ("ichol: encountered a pivot equal to 0");
 
       if (! ichol_checkpivot (data[j1]))
@@ -192,12 +193,12 @@
   // In ICHOL0 algorithm the zero-pattern of the input matrix is preserved
   // so it's structure does not change during the algorithm.  The same input
   // matrix is used to build the output matrix due to that fact.
-  octave_value_list param_list;
+  octave_value_list arg_list;
   if (! args(0).is_complex_type ())
     {
       SparseMatrix sm = args(0).sparse_matrix_value ();
-      param_list.append (sm);
-      sm = feval ("tril", param_list)(0).sparse_matrix_value ();
+      arg_list.append (sm);
+      sm = feval ("tril", arg_list)(0).sparse_matrix_value ();
       ichol_0 <SparseMatrix, double, ichol_mult_real,
                ichol_checkpivot_real> (sm, michol);
 
@@ -206,8 +207,8 @@
   else
     {
       SparseComplexMatrix sm = args(0).sparse_complex_matrix_value ();
-      param_list.append (sm);
-      sm = feval ("tril", param_list)(0).sparse_complex_matrix_value ();
+      arg_list.append (sm);
+      sm = feval ("tril", arg_list)(0).sparse_complex_matrix_value ();
       ichol_0 <SparseComplexMatrix, Complex, ichol_mult_complex,
                ichol_checkpivot_complex> (sm, michol);
 
@@ -433,19 +434,19 @@
   if (nargin == 3)
     michol = args(2).string_value ();
 
-  octave_value_list param_list;
+  octave_value_list arg_list;
   if (! args(0).is_complex_type ())
     {
       Array <double> cols_norm;
       SparseMatrix L;
-      param_list.append (args(0).sparse_matrix_value ());
+      arg_list.append (args(0).sparse_matrix_value ());
       SparseMatrix sm_l =
-        feval ("tril", param_list)(0).sparse_matrix_value ();
-      param_list(0) = sm_l;
-      param_list(1) = 1;
-      param_list(2) = "cols";
-      cols_norm = feval ("norm", param_list)(0).vector_value ();
-      param_list.clear ();
+        feval ("tril", arg_list)(0).sparse_matrix_value ();
+      arg_list(0) = sm_l;
+      arg_list(1) = 1;
+      arg_list(2) = "cols";
+      cols_norm = feval ("norm", arg_list)(0).vector_value ();
+      arg_list.clear ();
       ichol_t <SparseMatrix,
                double, ichol_mult_real, ichol_checkpivot_real>
                (sm_l, L, cols_norm.fortran_vec (), droptol, michol);
@@ -456,14 +457,14 @@
     {
       Array <Complex> cols_norm;
       SparseComplexMatrix L;
-      param_list.append (args(0).sparse_complex_matrix_value ());
+      arg_list.append (args(0).sparse_complex_matrix_value ());
       SparseComplexMatrix sm_l =
-        feval ("tril", param_list)(0).sparse_complex_matrix_value ();
-      param_list(0) = sm_l;
-      param_list(1) = 1;
-      param_list(2) = "cols";
-      cols_norm = feval ("norm", param_list)(0).complex_vector_value ();
-      param_list.clear ();
+        feval ("tril", arg_list)(0).sparse_complex_matrix_value ();
+      arg_list(0) = sm_l;
+      arg_list(1) = 1;
+      arg_list(2) = "cols";
+      cols_norm = feval ("norm", arg_list)(0).complex_vector_value ();
+      arg_list.clear ();
       ichol_t <SparseComplexMatrix,
                Complex, ichol_mult_complex, ichol_checkpivot_complex>
                (sm_l, L, cols_norm.fortran_vec (),
--- a/libinterp/corefcn/__ilu__.cc	Tue Mar 08 13:52:03 2016 -0800
+++ b/libinterp/corefcn/__ilu__.cc	Wed Mar 09 09:55:32 2016 -0800
@@ -31,7 +31,7 @@
 #include "error.h"
 #include "parse.h"
 
-// That function implements the IKJ and JKI variants of Gaussian elimination to
+// This function implements the IKJ and JKI variants of Gaussian elimination to
 // perform the ILUTP decomposition.  The behavior is controlled by milu
 // parameter.  If milu = ['off'|'col'] the JKI version is performed taking
 // advantage of CCS format of the input matrix.  If milu = 'row' the input
@@ -40,11 +40,8 @@
 template <typename octave_matrix_t, typename T>
 void ilu_0 (octave_matrix_t& sm, const std::string milu = "off")
 {
-
   const octave_idx_type n = sm.cols ();
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, iw, n);
-  OCTAVE_LOCAL_BUFFER (octave_idx_type, uptr, n);
-  octave_idx_type j1, j2, jrow, jw, i, k, jj;
+  octave_idx_type j1, j2, jrow, jw, i, j, k, jj;
   T tl, r;
 
   enum {OFF, ROW, COL};
@@ -59,24 +56,35 @@
   else
     opt = OFF;
 
+  // Input matrix pointers
   octave_idx_type* cidx = sm.cidx ();
   octave_idx_type* ridx = sm.ridx ();
   T* data = sm.data ();
+
+  // Working arrays
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, iw, n);
+  OCTAVE_LOCAL_BUFFER (octave_idx_type, uptr, n);
+
+  // Initialize working arrays
   for (i = 0; i < n; i++)
     iw[i] = -1;
+
+  // Loop over all columns
   for (k = 0; k < n; k++)
     {
       j1 = cidx[k];
-      j2 = cidx[k+1] - 1;
-      octave_idx_type j;
-      for (j = j1; j <= j2; j++)
-        {
-          iw[ridx[j]] = j;
-        }
+      j2 = cidx[k+1];
+
+      if (j1 == j2)
+        error ("ilu: A has a zero on the diagonal");
+
+      for (j = j1; j < j2; j++)
+        iw[ridx[j]] = j;
+
       r = 0;
       j = j1;
-      jrow = ridx[j];
-      while ((jrow < k) && (j <= j2))
+      jrow = ridx[j1];
+      while ((jrow < k) && (j < j2))
         {
           if (opt == ROW)
             {
@@ -116,9 +124,10 @@
       if (data[j] == T(0))
         error ("ilu: encountered a pivot equal to 0");
 
-      for (i = j1; i <= j2; i++)
+      for (i = j1; i < j2; i++)
         iw[ridx[i]] = -1;
     }
+
   if (opt == ROW)
     sm = sm.transpose ();
 }
@@ -141,35 +150,34 @@
   std::string milu;
 
   // In ILU0 algorithm the zero-pattern of the input matrix is preserved so
-  // it's structure does not change during the algorithm.  The same input
+  // its structure does not change during the algorithm.  The same input
   // matrix is used to build the output matrix due to that fact.
-  octave_value_list param_list;
+  octave_value_list arg_list;
   if (! args(0).is_complex_type ())
     {
       SparseMatrix sm = args(0).sparse_matrix_value ();
       ilu_0 <SparseMatrix, double> (sm, milu);
 
-      param_list.append (sm);
-      retval(1) = feval ("triu", param_list)(0).sparse_matrix_value ();
+      arg_list.append (sm);
+      retval(1) = feval ("triu", arg_list)(0).sparse_matrix_value ();
       SparseMatrix eye =
-        feval ("speye", octave_value_list (octave_value (sm.cols ())))(0).sparse_matrix_value ();
-      param_list.append (-1);
+        feval ("speye", ovl (sm.cols ()))(0).sparse_matrix_value ();
+      arg_list.append (-1);
       retval(0) = eye +
-        feval ("tril", param_list)(0).sparse_matrix_value ();
+                  feval ("tril", arg_list)(0).sparse_matrix_value ();
     }
   else
     {
       SparseComplexMatrix sm = args(0).sparse_complex_matrix_value ();
       ilu_0 <SparseComplexMatrix, Complex> (sm, milu);
 
-      param_list.append (sm);
-      retval(1) =
-        feval ("triu", param_list)(0).sparse_complex_matrix_value ();
+      arg_list.append (sm);
+      retval(1) = feval ("triu", arg_list)(0).sparse_complex_matrix_value ();
       SparseComplexMatrix eye =
-        feval ("speye", octave_value_list (octave_value (sm.cols ())))(0).sparse_complex_matrix_value ();
-      param_list.append (-1);
-      retval(0) =
-        eye + feval ("tril", param_list)(0).sparse_complex_matrix_value ();
+        feval ("speye", ovl (sm.cols ()))(0).sparse_complex_matrix_value ();
+      arg_list.append (-1);
+      retval(0) = eye +
+                  feval ("tril", arg_list)(0).sparse_complex_matrix_value ();
     }
 
   return retval;
@@ -181,7 +189,6 @@
                 T* rows_norm, const T droptol = 0,
                 const std::string milu = "off")
 {
-
   // Map the strings into chars for faster comparing inside loops
   char opt;
   enum {OFF, ROW, COL};
@@ -202,6 +209,7 @@
   max_len_u += (0.1 * max_len_u) > n ? 0.1 * max_len_u : n;
   max_len_l = sm_l.nnz ();
   max_len_l += (0.1 * max_len_l) > n ? 0.1 * max_len_l : n;
+
   // Extract pointers to the arrays for faster access inside loops
   octave_idx_type* cidx_in_u = sm_u.cidx ();
   octave_idx_type* ridx_in_u = sm_u.ridx ();
@@ -235,6 +243,7 @@
 
   T zero = T (0);
 
+  // Initialize working arrays 
   cidx_u[0] = cidx_in_u[0];
   cidx_l[0] = cidx_in_l[0];
   for (i = 0; i < n; i++)
@@ -249,6 +258,7 @@
   cols_list_len = 0;
   rows_list_len = 0;
 
+  // Loop over all columns
   for (k = 0; k < n; k++)
     {
       // Load the working column and working row
@@ -434,6 +444,7 @@
   L = octave_matrix_t (n, n, total_len_l);
   U = octave_matrix_t (n, n, total_len_u);
 
+  // FIXME: Can these loops be replaced by std::copy?
   for (i = 0; i <= n; i++)
     L.cidx (i) = cidx_l[i];
 
@@ -478,56 +489,55 @@
   if (nargin == 3)
     milu = args(2).string_value ();
 
-  octave_value_list param_list;
+  octave_value_list arg_list;
   if (! args(0).is_complex_type ())
     {
       Array<double> cols_norm, rows_norm;
-      param_list.append (args(0).sparse_matrix_value ());
-      SparseMatrix sm_u = feval ("triu", param_list)(0).sparse_matrix_value ();
-      param_list.append (-1);
-      SparseMatrix sm_l = feval ("tril", param_list)(0).sparse_matrix_value ();
-      param_list(1) = "rows";
-      rows_norm = feval ("norm", param_list)(0).vector_value ();
-      param_list(1) = "cols";
-      cols_norm = feval ("norm", param_list)(0).vector_value ();
-      param_list.clear ();
+      arg_list.append (args(0).sparse_matrix_value ());
+      SparseMatrix sm_u = feval ("triu", arg_list)(0).sparse_matrix_value ();
+      arg_list.append (-1);
+      SparseMatrix sm_l = feval ("tril", arg_list)(0).sparse_matrix_value ();
+      arg_list(1) = "rows";
+      rows_norm = feval ("norm", arg_list)(0).vector_value ();
+      arg_list(1) = "cols";
+      cols_norm = feval ("norm", arg_list)(0).vector_value ();
+      arg_list.clear ();
       SparseMatrix U, L;
       ilu_crout <SparseMatrix, double> (sm_l, sm_u, L, U,
                                         cols_norm.fortran_vec (),
                                         rows_norm.fortran_vec (),
                                         droptol, milu);
-      param_list.append (octave_value (L.cols ()));
-      SparseMatrix eye =
-        feval ("speye", param_list)(0).sparse_matrix_value ();
+      arg_list.append (octave_value (L.cols ()));
+      SparseMatrix eye = feval ("speye", arg_list)(0).sparse_matrix_value ();
       return ovl (L + eye, U);
     }
   else
     {
       Array<Complex> cols_norm, rows_norm;
-      param_list.append (args(0).sparse_complex_matrix_value ());
+      arg_list.append (args(0).sparse_complex_matrix_value ());
       SparseComplexMatrix sm_u =
-        feval ("triu", param_list)(0).sparse_complex_matrix_value ();
-      param_list.append (-1);
+        feval ("triu", arg_list)(0).sparse_complex_matrix_value ();
+      arg_list.append (-1);
       SparseComplexMatrix sm_l =
-        feval ("tril", param_list)(0).sparse_complex_matrix_value ();
-      param_list(1) = "rows";
-      rows_norm = feval ("norm", param_list)(0).complex_vector_value ();
-      param_list(1) = "cols";
-      cols_norm = feval ("norm", param_list)(0).complex_vector_value ();
-      param_list.clear ();
+        feval ("tril", arg_list)(0).sparse_complex_matrix_value ();
+      arg_list(1) = "rows";
+      rows_norm = feval ("norm", arg_list)(0).complex_vector_value ();
+      arg_list(1) = "cols";
+      cols_norm = feval ("norm", arg_list)(0).complex_vector_value ();
+      arg_list.clear ();
       SparseComplexMatrix U, L;
       ilu_crout < SparseComplexMatrix, Complex >
                 (sm_l, sm_u, L, U, cols_norm.fortran_vec () ,
                  rows_norm.fortran_vec (), Complex (droptol), milu);
 
-      param_list.append (octave_value (L.cols ()));
+      arg_list.append (octave_value (L.cols ()));
       SparseComplexMatrix eye =
-        feval ("speye", param_list)(0).sparse_complex_matrix_value ();
+        feval ("speye", arg_list)(0).sparse_complex_matrix_value ();
       return ovl (L + eye, U);
     }
 }
 
-// That function implements the IKJ and JKI variants of gaussian elimination
+// This function implements the IKJ and JKI variants of gaussian elimination
 // to perform the ILUTP decomposition.  The behavior is controlled by milu
 // parameter.  If milu = ['off'|'col'] the JKI version is performed taking
 // advantage of CCS format of the input matrix.  Row pivoting is performed.
@@ -553,7 +563,7 @@
 
   const octave_idx_type n = sm.cols ();
 
-  // That is necessary for the JKI (milu = "row") variant.
+  // This is necessary for the JKI (milu = "row") variant.
   if (opt == ROW)
     sm = sm.transpose ();
 
@@ -572,12 +582,14 @@
   max_len_l = nnz_l;
   max_len_l += (0.1 * max_len_l) > n ? 0.1 * max_len_l : n;
 
+  // Extract pointers to the arrays for faster access inside loops
   Array <octave_idx_type> cidx_out_l (dim_vector (n + 1, 1));
   octave_idx_type* cidx_l = cidx_out_l.fortran_vec ();
   Array <octave_idx_type> ridx_out_l (dim_vector (max_len_l, 1));
   octave_idx_type* ridx_l = ridx_out_l.fortran_vec ();
   Array <T> data_out_l (dim_vector (max_len_l ,1));
   T* data_l = data_out_l.fortran_vec ();
+
   // Data for U
   Array <octave_idx_type> cidx_out_u (dim_vector (n + 1, 1));
   octave_idx_type* cidx_u = cidx_out_u.fortran_vec ();
@@ -597,7 +609,7 @@
   octave_idx_type* perm = perm_vec.fortran_vec ();
   OCTAVE_LOCAL_BUFFER (octave_idx_type, uptr, n);
 
-
+  // Initialize working and permutation arrays 
   cidx_l[0] = cidx_in[0];
   cidx_u[0] = cidx_in[0];
   for (i = 0; i < n; i++)
@@ -609,9 +621,9 @@
   total_len_u = 0;
   total_len_l = 0;
 
+  // Loop over all columns
   for (k = 0; k < n; k++)
     {
-
       for (j = cidx_in[k]; j < cidx_in[k+1]; j++)
         {
           p_perm = iperm[ridx_in[j]];
@@ -662,8 +674,8 @@
                     }
                 }
 
-              // Drop element from the U part in IKJ and L part in JKI
-              // variant (milu = [col|off])
+              // Drop element from the U part in IKJ
+              // and L part in JKI variant (milu = [col|off])
               if ((std::abs (w_data[jrow]) < (droptol * cols_norm[k]))
                   && (w_data[jrow] != zero))
                 {
@@ -703,7 +715,7 @@
                   it2 = it;
                 }
             }
-          // If the pivot is not the diagonal element update all.
+          // If the pivot is not the diagonal element update all
           p_perm = iperm[max_ind];
           if (max_ind != perm[k])
             {
@@ -725,8 +737,8 @@
 
         }
 
-      // Drop elements in the L part in the IKJ and from the U part in the JKI
-      // version.
+      // Drop elements in the L part in the IKJ version,
+      // and from the U part in the JKI version.
       it = iw_l.begin ();
       while (it != iw_l.end ())
         {
@@ -755,8 +767,6 @@
           w_data[k] += total_sum;
         }
 
-
-
       // Check if the pivot is zero and if udiag is active.
       // NOTE: If the pivot == 0 and udiag is active, then if milu = [col|row]
       //       will not preserve the row sum for that column/row.
@@ -777,7 +787,6 @@
             w_data[p_perm] = w_data[p_perm] / w_data[k];
           }
 
-
       if ((max_len_u - total_len_u) < n)
         {
           max_len_u += (0.1 * max_len_u) > n ? 0.1 * max_len_u : n;
@@ -808,6 +817,7 @@
             }
           w_data[*it] = 0;
         }
+
       // Expand working vector into L.
       w_len_l = 0;
       for (it = iw_l.begin (); it != iw_l.end (); ++it)
@@ -823,6 +833,7 @@
         }
       total_len_u += w_len_u;
       total_len_l += w_len_l;
+
       // Check if there are too many elements to be indexed with
       // octave_idx_type type due to fill-in during the process.
       if (total_len_l < 0 || total_len_u < 0)
@@ -948,30 +959,30 @@
   if (nargin == 5)
     udiag = args(4).double_value ();
 
-  octave_value_list param_list;
+  octave_value_list arg_list;
   octave_idx_type nnz_u, nnz_l;
   if (! args(0).is_complex_type ())
     {
       Array <double> rc_norm;
       SparseMatrix sm = args(0).sparse_matrix_value ();
-      param_list.append (sm);
-      nnz_u =  (feval ("triu", param_list)(0).sparse_matrix_value ()).nnz ();
-      param_list.append (-1);
-      nnz_l =  (feval ("tril", param_list)(0).sparse_matrix_value ()).nnz ();
+      arg_list.append (sm);
+      nnz_u =  (feval ("triu", arg_list)(0).sparse_matrix_value ()).nnz ();
+      arg_list.append (-1);
+      nnz_l =  (feval ("tril", arg_list)(0).sparse_matrix_value ()).nnz ();
       if (milu == "row")
-        param_list (1) = "rows";
+        arg_list (1) = "rows";
       else
-        param_list (1) = "cols";
-      rc_norm = feval ("norm", param_list)(0).vector_value ();
-      param_list.clear ();
+        arg_list (1) = "cols";
+      rc_norm = feval ("norm", arg_list)(0).vector_value ();
+      arg_list.clear ();
       Array <octave_idx_type> perm (dim_vector (sm.cols (), 1));
       SparseMatrix U, L;
       ilu_tp <SparseMatrix, double> (sm, L, U, nnz_u, nnz_l,
                                      rc_norm.fortran_vec (),
                                      perm, droptol, thresh, milu, udiag);
-      param_list.append (octave_value (L.cols ()));
+      arg_list.append (octave_value (L.cols ()));
       SparseMatrix eye =
-        feval ("speye", param_list)(0).sparse_matrix_value ();
+        feval ("speye", arg_list)(0).sparse_matrix_value ();
       if (milu == "row")
         {
           if (nargout == 3)
@@ -1002,27 +1013,27 @@
     {
       Array <Complex> rc_norm;
       SparseComplexMatrix sm = args(0).sparse_complex_matrix_value ();
-      param_list.append (sm);
+      arg_list.append (sm);
       nnz_u =
-        feval ("triu", param_list)(0).sparse_complex_matrix_value ().nnz ();
-      param_list.append (-1);
+        feval ("triu", arg_list)(0).sparse_complex_matrix_value ().nnz ();
+      arg_list.append (-1);
       nnz_l =
-        feval ("tril", param_list)(0).sparse_complex_matrix_value ().nnz ();
+        feval ("tril", arg_list)(0).sparse_complex_matrix_value ().nnz ();
       if (milu == "row")
-        param_list(1) = "rows";
+        arg_list(1) = "rows";
       else
-        param_list(1) = "cols";
-      rc_norm = feval ("norm", param_list)(0).complex_vector_value ();
+        arg_list(1) = "cols";
+      rc_norm = feval ("norm", arg_list)(0).complex_vector_value ();
+      arg_list.clear ();
       Array <octave_idx_type> perm (dim_vector (sm.cols (), 1));
-      param_list.clear ();
       SparseComplexMatrix U, L;
       ilu_tp < SparseComplexMatrix, Complex>
               (sm, L, U, nnz_u, nnz_l, rc_norm.fortran_vec (), perm,
                Complex (droptol), Complex (thresh), milu, udiag);
 
-      param_list.append (octave_value (L.cols ()));
+      arg_list.append (octave_value (L.cols ()));
       SparseComplexMatrix eye =
-        feval ("speye", param_list)(0).sparse_complex_matrix_value ();
+        feval ("speye", arg_list)(0).sparse_complex_matrix_value ();
       if (milu == "row")
         {
           if (nargout == 3)