pgsql-4793

Version:

8.4 beta1

Bug Link:

http://postgresql.1045698.n5.nabble.com/BUG-4793-Segmentation-fault-when-doing-vacuum-analyze-td2129379.html

Patch Link:

http://archives.postgresql.org/message-id/20090505180211.B2A59754069@cvs.postgresql.org

Symptom:

Segmentation fault when doing vacuum analyze.

How it is diagnosed:

Discussion and source code analysis.

Root Cause:

Brief: .There’s an overflow of integer in computing an array’s index, accessing a negative index results in segment fault.

Detail:

/*compute column statistics */

static void

compute_scalar_stats(...)

{

                ...

                     for (i = 0; i < num_hist; i++)

                        {

                                int                     pos;

                                pos = (i * (nvals - 1)) / (num_hist - 1);

                                hist_values[i] = datumCopy(values[pos].value,   <-- Crashed here

                                       stats->attr->attbyval,

                                       stats->attr->attlen);

                        }

                ...

}

(gdb) print i

$17 = 1458

(gdb) print nvals

$18 = 1473527

(gdb) print num_hist

$19 = 5001

(gdb) print pos

$20 = -429313           // should be 429680 !

Since there is an overflow when computing i*(nvals-1), if you try it in a snippet of C, it’s -2146566388.

So the patch is just to add  (nvals-1) / (num_hist - 1) fraction i times to avoid the overflow.

+
+                         /*
+                          * The object of this loop is to copy the first and last values[]
+                          * entries along with evenly-spaced values in between.  So the
+                          * i'th value is values[(i * (nvals - 1)) / (num_hist - 1)].  But
+                          * computing that subscript directly risks integer overflow when
+                          * the stats target is more than a couple thousand.  Instead we
+                          * add (nvals - 1) / (num_hist - 1) to pos at each step, tracking
+                          * the integral and fractional parts of the sum separately.
+                          */
+                         delta = (nvals - 1) / (num_hist - 1);
+                         deltafrac = (nvals - 1) % (num_hist - 1);
+                         pos = posfrac = 0;
+
                         for (i = 0; i < num_hist; i++)
                         {

 -                                int                     pos;

 -                                pos = (i * (nvals - 1)) / (num_hist - 1);
                                 hist_values[i] = datumCopy(values[pos].value,
                                                                                    stats->attr->attbyval,
                                                                                    stats->attr->attlen);
+                                 pos += delta;
+                                 posfrac += deltafrac;
+                                 if (posfrac >= (num_hist - 1))
+                                 {
+                                         /* fractional part exceeds 1, carry to integer part */
+                                         pos++;
+                                         posfrac -= (num_hist - 1);
+                                 }
                         }
+

Is there any log message?

Yes.