Skip to content

Commit d059963

Browse files
committed
Be more wary of corrupt data in pageinspect's heap_page_items().
The original intent in heap_page_items() was to return nulls, not throw an error or crash, if an item was sufficiently corrupt that we couldn't safely extract data from it. However, commit d6061f8 utterly missed that memo, and not only put in an un-length-checked copy of the tuple's data section, but also managed to break the check on sane nulls-bitmap length. Either mistake could possibly lead to a SIGSEGV crash if the tuple is corrupt. Bug: #18896 Reported-by: Dmitry Kovalenko <d.kovalenko@postgrespro.ru> Author: Dmitry Kovalenko <d.kovalenko@postgrespro.ru> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/18896-add267b8e06663e3@postgresql.org Backpatch-through: 13
1 parent 88e9471 commit d059963

File tree

1 file changed

+27
-18
lines changed

1 file changed

+27
-18
lines changed

contrib/pageinspect/heapfuncs.c

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,8 @@ heap_page_items(PG_FUNCTION_ARGS)
212212
lp_offset + lp_len <= raw_page_size)
213213
{
214214
HeapTupleHeader tuphdr;
215-
bytea *tuple_data_bytea;
216-
int tuple_data_len;
217215

218216
/* Extract information from the tuple header */
219-
220217
tuphdr = (HeapTupleHeader) PageGetItem(page, id);
221218

222219
values[4] = UInt32GetDatum(HeapTupleHeaderGetRawXmin(tuphdr));
@@ -228,31 +225,32 @@ heap_page_items(PG_FUNCTION_ARGS)
228225
values[9] = UInt32GetDatum(tuphdr->t_infomask);
229226
values[10] = UInt8GetDatum(tuphdr->t_hoff);
230227

231-
/* Copy raw tuple data into bytea attribute */
232-
tuple_data_len = lp_len - tuphdr->t_hoff;
233-
tuple_data_bytea = (bytea *) palloc(tuple_data_len + VARHDRSZ);
234-
SET_VARSIZE(tuple_data_bytea, tuple_data_len + VARHDRSZ);
235-
memcpy(VARDATA(tuple_data_bytea), (char *) tuphdr + tuphdr->t_hoff,
236-
tuple_data_len);
237-
values[13] = PointerGetDatum(tuple_data_bytea);
238-
239228
/*
240229
* We already checked that the item is completely within the raw
241230
* page passed to us, with the length given in the line pointer.
242-
* Let's check that t_hoff doesn't point over lp_len, before using
243-
* it to access t_bits and oid.
231+
* But t_hoff could be out of range, so check it before relying on
232+
* it to fetch additional info.
244233
*/
245234
if (tuphdr->t_hoff >= SizeofHeapTupleHeader &&
246235
tuphdr->t_hoff <= lp_len &&
247236
tuphdr->t_hoff == MAXALIGN(tuphdr->t_hoff))
248237
{
238+
int tuple_data_len;
239+
bytea *tuple_data_bytea;
240+
241+
/* Copy null bitmask and OID, if present */
249242
if (tuphdr->t_infomask & HEAP_HASNULL)
250243
{
251-
int bits_len;
252-
253-
bits_len =
254-
BITMAPLEN(HeapTupleHeaderGetNatts(tuphdr)) * BITS_PER_BYTE;
255-
values[11] = CStringGetTextDatum(bits_to_text(tuphdr->t_bits, bits_len));
244+
int bitmaplen;
245+
246+
bitmaplen = BITMAPLEN(HeapTupleHeaderGetNatts(tuphdr));
247+
/* better range-check the attribute count, too */
248+
if (bitmaplen <= tuphdr->t_hoff - SizeofHeapTupleHeader)
249+
values[11] =
250+
CStringGetTextDatum(bits_to_text(tuphdr->t_bits,
251+
bitmaplen * BITS_PER_BYTE));
252+
else
253+
nulls[11] = true;
256254
}
257255
else
258256
nulls[11] = true;
@@ -261,11 +259,22 @@ heap_page_items(PG_FUNCTION_ARGS)
261259
values[12] = HeapTupleHeaderGetOidOld(tuphdr);
262260
else
263261
nulls[12] = true;
262+
263+
/* Copy raw tuple data into bytea attribute */
264+
tuple_data_len = lp_len - tuphdr->t_hoff;
265+
tuple_data_bytea = (bytea *) palloc(tuple_data_len + VARHDRSZ);
266+
SET_VARSIZE(tuple_data_bytea, tuple_data_len + VARHDRSZ);
267+
if (tuple_data_len > 0)
268+
memcpy(VARDATA(tuple_data_bytea),
269+
(char *) tuphdr + tuphdr->t_hoff,
270+
tuple_data_len);
271+
values[13] = PointerGetDatum(tuple_data_bytea);
264272
}
265273
else
266274
{
267275
nulls[11] = true;
268276
nulls[12] = true;
277+
nulls[13] = true;
269278
}
270279
}
271280
else

0 commit comments

Comments
 (0)