pgsql-4793
Version:
8.4 beta1
Bug Link:
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.