Skip to content

Commit c9f4618

Browse files
committed
Add defenses against running with a wrong selection of LOBLKSIZE.
It's critical that the backend's idea of LOBLKSIZE match the way data has actually been divided up in pg_largeobject. While we don't provide any direct way to adjust that value, doing so is a one-line source code change and various people have expressed interest recently in changing it. So, just as with TOAST_MAX_CHUNK_SIZE, it seems prudent to record the value in pg_control and cross-check that the backend's compiled-in setting matches the on-disk data. Also tweak the code in inv_api.c so that fetches from pg_largeobject explicitly verify that the length of the data field is not more than LOBLKSIZE. Formerly we just had Asserts() for that, which is no protection at all in production builds. In some of the call sites an overlength data value would translate directly to a security-relevant stack clobber, so it seems worth one extra runtime comparison to be sure. In the back branches, we can't change the contents of pg_control; but we can still make the extra checks in inv_api.c, which will offer some amount of protection against running with the wrong value of LOBLKSIZE.
1 parent 037c6fb commit c9f4618

File tree

1 file changed

+39
-45
lines changed

1 file changed

+39
-45
lines changed

src/backend/storage/large_object/inv_api.c

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,38 @@ myLargeObjectExists(Oid loid, Snapshot snapshot)
174174
}
175175

176176

177-
static int32
178-
getbytealen(bytea *data)
177+
/*
178+
* Extract data field from a pg_largeobject tuple, detoasting if needed
179+
* and verifying that the length is sane. Returns data pointer (a bytea *),
180+
* data length, and an indication of whether to pfree the data pointer.
181+
*/
182+
static void
183+
getdatafield(Form_pg_largeobject tuple,
184+
bytea **pdatafield,
185+
int *plen,
186+
bool *pfreeit)
179187
{
180-
Assert(!VARATT_IS_EXTENDED(data));
181-
if (VARSIZE(data) < VARHDRSZ)
182-
elog(ERROR, "invalid VARSIZE(data)");
183-
return (VARSIZE(data) - VARHDRSZ);
188+
bytea *datafield;
189+
int len;
190+
bool freeit;
191+
192+
datafield = &(tuple->data); /* see note at top of file */
193+
freeit = false;
194+
if (VARATT_IS_EXTENDED(datafield))
195+
{
196+
datafield = (bytea *)
197+
heap_tuple_untoast_attr((struct varlena *) datafield);
198+
freeit = true;
199+
}
200+
len = VARSIZE(datafield) - VARHDRSZ;
201+
if (len < 0 || len > LOBLKSIZE)
202+
ereport(ERROR,
203+
(errcode(ERRCODE_DATA_CORRUPTED),
204+
errmsg("pg_largeobject entry for OID %u, page %d has invalid data field size %d",
205+
tuple->loid, tuple->pageno, len)));
206+
*pdatafield = datafield;
207+
*plen = len;
208+
*pfreeit = freeit;
184209
}
185210

186211

@@ -362,20 +387,14 @@ inv_getsize(LargeObjectDesc *obj_desc)
362387
{
363388
Form_pg_largeobject data;
364389
bytea *datafield;
390+
int len;
365391
bool pfreeit;
366392

367393
if (HeapTupleHasNulls(tuple)) /* paranoia */
368394
elog(ERROR, "null field found in pg_largeobject");
369395
data = (Form_pg_largeobject) GETSTRUCT(tuple);
370-
datafield = &(data->data); /* see note at top of file */
371-
pfreeit = false;
372-
if (VARATT_IS_EXTENDED(datafield))
373-
{
374-
datafield = (bytea *)
375-
heap_tuple_untoast_attr((struct varlena *) datafield);
376-
pfreeit = true;
377-
}
378-
lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
396+
getdatafield(data, &datafield, &len, &pfreeit);
397+
lastbyte = data->pageno * LOBLKSIZE + len;
379398
if (pfreeit)
380399
pfree(datafield);
381400
}
@@ -490,15 +509,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
490509
off = (int) (obj_desc->offset - pageoff);
491510
Assert(off >= 0 && off < LOBLKSIZE);
492511

493-
datafield = &(data->data); /* see note at top of file */
494-
pfreeit = false;
495-
if (VARATT_IS_EXTENDED(datafield))
496-
{
497-
datafield = (bytea *)
498-
heap_tuple_untoast_attr((struct varlena *) datafield);
499-
pfreeit = true;
500-
}
501-
len = getbytealen(datafield);
512+
getdatafield(data, &datafield, &len, &pfreeit);
502513
if (len > off)
503514
{
504515
n = len - off;
@@ -617,16 +628,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
617628
*
618629
* First, load old data into workbuf
619630
*/
620-
datafield = &(olddata->data); /* see note at top of file */
621-
pfreeit = false;
622-
if (VARATT_IS_EXTENDED(datafield))
623-
{
624-
datafield = (bytea *)
625-
heap_tuple_untoast_attr((struct varlena *) datafield);
626-
pfreeit = true;
627-
}
628-
len = getbytealen(datafield);
629-
Assert(len <= LOBLKSIZE);
631+
getdatafield(olddata, &datafield, &len, &pfreeit);
630632
memcpy(workb, VARDATA(datafield), len);
631633
if (pfreeit)
632634
pfree(datafield);
@@ -799,19 +801,11 @@ inv_truncate(LargeObjectDesc *obj_desc, int len)
799801
if (olddata != NULL && olddata->pageno == pageno)
800802
{
801803
/* First, load old data into workbuf */
802-
bytea *datafield = &(olddata->data); /* see note at top of
803-
* file */
804-
bool pfreeit = false;
804+
bytea *datafield;
805805
int pagelen;
806+
bool pfreeit;
806807

807-
if (VARATT_IS_EXTENDED(datafield))
808-
{
809-
datafield = (bytea *)
810-
heap_tuple_untoast_attr((struct varlena *) datafield);
811-
pfreeit = true;
812-
}
813-
pagelen = getbytealen(datafield);
814-
Assert(pagelen <= LOBLKSIZE);
808+
getdatafield(olddata, &datafield, &pagelen, &pfreeit);
815809
memcpy(workb, VARDATA(datafield), pagelen);
816810
if (pfreeit)
817811
pfree(datafield);

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