Skip to content

Commit 6b89ce1

Browse files
committed
Don't reference out-of-bounds array elements in brin_minmax_multi.c
The primary fix here is to fix has_matching_range() so it does not reference ranges->values[-1] when nranges == 0. Similar problems existed in AssertCheckRanges() too. It does not look like any of these problems could lead to a crash as the array in question is at the end of the Ranges struct, and values[-1] is memory that belongs to other fields in the struct. However, let's get rid of these rather unsafe coding practices. In passing, I (David) adjusted some comments to try to make it more clear what some of the fields are for in the Ranges struct. I had to study the code to find out what nsorted was for as I couldn't tell from the comments. Author: Ranier Vilela Discussion: https://postgr.es/m/CAEudQAqJQzPitufX-jR=YUbJafpCDAKUnwgdbX_MzSc93wuvdw@mail.gmail.com Backpatch-through: 14, where multi-range brin was added.
1 parent 346990a commit 6b89ce1

File tree

1 file changed

+88
-84
lines changed

1 file changed

+88
-84
lines changed

src/backend/access/brin/brin_minmax_multi.c

Lines changed: 88 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -142,19 +142,23 @@ typedef struct MinMaxMultiOptions
142142
* The Ranges struct stores the boundary values in a single array, but we
143143
* treat regular and single-point ranges differently to save space. For
144144
* regular ranges (with different boundary values) we have to store both
145-
* values, while for "single-point ranges" we only need to save one value.
145+
* the lower and upper bound of the range, while for "single-point ranges"
146+
* we only need to store a single value.
146147
*
147148
* The 'values' array stores boundary values for regular ranges first (there
148149
* are 2*nranges values to store), and then the nvalues boundary values for
149150
* single-point ranges. That is, we have (2*nranges + nvalues) boundary
150151
* values in the array.
151152
*
152-
* +---------------------------------+-------------------------------+
153-
* | ranges (sorted pairs of values) | sorted values (single points) |
154-
* +---------------------------------+-------------------------------+
153+
* +-------------------------+----------------------------------+
154+
* | ranges (2 * nranges of) | single point values (nvalues of) |
155+
* +-------------------------+----------------------------------+
155156
*
156157
* This allows us to quickly add new values, and store outliers without
157-
* making the other ranges very wide.
158+
* having to widen any of the existing range values.
159+
*
160+
* 'nsorted' denotes how many of 'nvalues' in the values[] array are sorted.
161+
* When nsorted == nvalues, all single point values are sorted.
158162
*
159163
* We never store more than maxvalues values (as set by values_per_range
160164
* reloption). If needed we merge some of the ranges.
@@ -173,10 +177,10 @@ typedef struct Ranges
173177
FmgrInfo *cmp;
174178

175179
/* (2*nranges + nvalues) <= maxvalues */
176-
int nranges; /* number of ranges in the array (stored) */
177-
int nsorted; /* number of sorted values (ranges + points) */
178-
int nvalues; /* number of values in the data array (all) */
179-
int maxvalues; /* maximum number of values (reloption) */
180+
int nranges; /* number of ranges in the values[] array */
181+
int nsorted; /* number of nvalues which are sorted */
182+
int nvalues; /* number of point values in values[] array */
183+
int maxvalues; /* number of elements in the values[] array */
180184

181185
/*
182186
* We simply add the values into a large buffer, without any expensive
@@ -318,102 +322,99 @@ AssertCheckRanges(Ranges *ranges, FmgrInfo *cmpFn, Oid colloid)
318322
* Check that none of the values are not covered by ranges (both sorted
319323
* and unsorted)
320324
*/
321-
for (i = 0; i < ranges->nvalues; i++)
325+
if (ranges->nranges > 0)
322326
{
323-
Datum compar;
324-
int start,
325-
end;
326-
Datum minvalue,
327-
maxvalue;
328-
329-
Datum value = ranges->values[2 * ranges->nranges + i];
330-
331-
if (ranges->nranges == 0)
332-
break;
333-
334-
minvalue = ranges->values[0];
335-
maxvalue = ranges->values[2 * ranges->nranges - 1];
336-
337-
/*
338-
* Is the value smaller than the minval? If yes, we'll recurse to the
339-
* left side of range array.
340-
*/
341-
compar = FunctionCall2Coll(cmpFn, colloid, value, minvalue);
342-
343-
/* smaller than the smallest value in the first range */
344-
if (DatumGetBool(compar))
345-
continue;
346-
347-
/*
348-
* Is the value greater than the maxval? If yes, we'll recurse to the
349-
* right side of range array.
350-
*/
351-
compar = FunctionCall2Coll(cmpFn, colloid, maxvalue, value);
352-
353-
/* larger than the largest value in the last range */
354-
if (DatumGetBool(compar))
355-
continue;
356-
357-
start = 0; /* first range */
358-
end = ranges->nranges - 1; /* last range */
359-
while (true)
327+
for (i = 0; i < ranges->nvalues; i++)
360328
{
361-
int midpoint = (start + end) / 2;
362-
363-
/* this means we ran out of ranges in the last step */
364-
if (start > end)
365-
break;
329+
Datum compar;
330+
int start,
331+
end;
332+
Datum minvalue = ranges->values[0];
333+
Datum maxvalue = ranges->values[2 * ranges->nranges - 1];
334+
Datum value = ranges->values[2 * ranges->nranges + i];
366335

367-
/* copy the min/max values from the ranges */
368-
minvalue = ranges->values[2 * midpoint];
369-
maxvalue = ranges->values[2 * midpoint + 1];
336+
compar = FunctionCall2Coll(cmpFn, colloid, value, minvalue);
370337

371338
/*
372-
* Is the value smaller than the minval? If yes, we'll recurse to
373-
* the left side of range array.
339+
* If the value is smaller than the lower bound in the first range
340+
* then it cannot possibly be in any of the ranges.
374341
*/
375-
compar = FunctionCall2Coll(cmpFn, colloid, value, minvalue);
376-
377-
/* smaller than the smallest value in this range */
378342
if (DatumGetBool(compar))
379-
{
380-
end = (midpoint - 1);
381343
continue;
382-
}
383344

384-
/*
385-
* Is the value greater than the minval? If yes, we'll recurse to
386-
* the right side of range array.
387-
*/
388345
compar = FunctionCall2Coll(cmpFn, colloid, maxvalue, value);
389346

390-
/* larger than the largest value in this range */
347+
/*
348+
* Likewise, if the value is larger than the upper bound of the
349+
* final range, then it cannot possibly be inside any of the
350+
* ranges.
351+
*/
391352
if (DatumGetBool(compar))
392-
{
393-
start = (midpoint + 1);
394353
continue;
395-
}
396354

397-
/* hey, we found a matching range */
398-
Assert(false);
355+
/* bsearch the ranges to see if 'value' fits within any of them */
356+
start = 0; /* first range */
357+
end = ranges->nranges - 1; /* last range */
358+
while (true)
359+
{
360+
int midpoint = (start + end) / 2;
361+
362+
/* this means we ran out of ranges in the last step */
363+
if (start > end)
364+
break;
365+
366+
/* copy the min/max values from the ranges */
367+
minvalue = ranges->values[2 * midpoint];
368+
maxvalue = ranges->values[2 * midpoint + 1];
369+
370+
/*
371+
* Is the value smaller than the minval? If yes, we'll recurse
372+
* to the left side of range array.
373+
*/
374+
compar = FunctionCall2Coll(cmpFn, colloid, value, minvalue);
375+
376+
/* smaller than the smallest value in this range */
377+
if (DatumGetBool(compar))
378+
{
379+
end = (midpoint - 1);
380+
continue;
381+
}
382+
383+
/*
384+
* Is the value greater than the minval? If yes, we'll recurse
385+
* to the right side of range array.
386+
*/
387+
compar = FunctionCall2Coll(cmpFn, colloid, maxvalue, value);
388+
389+
/* larger than the largest value in this range */
390+
if (DatumGetBool(compar))
391+
{
392+
start = (midpoint + 1);
393+
continue;
394+
}
395+
396+
/* hey, we found a matching range */
397+
Assert(false);
398+
}
399399
}
400400
}
401401

402-
/* and values in the unsorted part must not be in sorted part */
403-
for (i = ranges->nsorted; i < ranges->nvalues; i++)
402+
/* and values in the unsorted part must not be in the sorted part */
403+
if (ranges->nsorted > 0)
404404
{
405405
compare_context cxt;
406-
Datum value = ranges->values[2 * ranges->nranges + i];
407-
408-
if (ranges->nsorted == 0)
409-
break;
410406

411407
cxt.colloid = ranges->colloid;
412408
cxt.cmpFn = ranges->cmp;
413409

414-
Assert(bsearch_arg(&value, &ranges->values[2 * ranges->nranges],
415-
ranges->nsorted, sizeof(Datum),
416-
compare_values, (void *) &cxt) == NULL);
410+
for (i = ranges->nsorted; i < ranges->nvalues; i++)
411+
{
412+
Datum value = ranges->values[2 * ranges->nranges + i];
413+
414+
Assert(bsearch_arg(&value, &ranges->values[2 * ranges->nranges],
415+
ranges->nsorted, sizeof(Datum),
416+
compare_values, (void *) &cxt) == NULL);
417+
}
417418
}
418419
#endif
419420
}
@@ -923,8 +924,8 @@ has_matching_range(BrinDesc *bdesc, Oid colloid, Ranges *ranges,
923924
{
924925
Datum compar;
925926

926-
Datum minvalue = ranges->values[0];
927-
Datum maxvalue = ranges->values[2 * ranges->nranges - 1];
927+
Datum minvalue;
928+
Datum maxvalue;
928929

929930
FmgrInfo *cmpLessFn;
930931
FmgrInfo *cmpGreaterFn;
@@ -936,6 +937,9 @@ has_matching_range(BrinDesc *bdesc, Oid colloid, Ranges *ranges,
936937
if (ranges->nranges == 0)
937938
return false;
938939

940+
minvalue = ranges->values[0];
941+
maxvalue = ranges->values[2 * ranges->nranges - 1];
942+
939943
/*
940944
* Otherwise, need to compare the new value with boundaries of all the
941945
* ranges. First check if it's less than the absolute minimum, which is

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy