From e976fd43c6a5f7f288a8a9d72d21934794db4b31 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 12 Jun 2007 19:46:24 +0000 Subject: [PATCH] Add some simple defenses against null fields in pg_largeobject, and add comments noting that there's an alignment assumption now that the data field could be in 1-byte-header format. Per discussion with Greg Stark. --- src/backend/storage/large_object/inv_api.c | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index ea98ab2952..77b756cabb 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -5,6 +5,13 @@ * contains the user-level large object application interface routines. * * + * Note: we access pg_largeobject.data using its C struct declaration. + * This is safe because it immediately follows pageno which is an int4 field, + * and therefore the data field will always be 4-byte aligned, even if it + * is in the short 1-byte-header format. We have to detoast it since it's + * quite likely to be in compressed or short format. We also need to check + * for NULLs, since initdb will mark loid and pageno but not data as NOT NULL. + * * Note: many of these routines leak memory in CurrentMemoryContext, as indeed * does most of the backend code. We expect that CurrentMemoryContext will * be a short-lived context. Data that must persist across function calls @@ -17,7 +24,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.124 2007/04/06 04:21:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.125 2007/06/12 19:46:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -330,8 +337,10 @@ inv_getsize(LargeObjectDesc *obj_desc) bool pfreeit; found = true; + if (HeapTupleHasNulls(tuple)) /* paranoia */ + elog(ERROR, "null field found in pg_largeobject"); data = (Form_pg_largeobject) GETSTRUCT(tuple); - datafield = &(data->data); + datafield = &(data->data); /* see note at top of file */ pfreeit = false; if (VARATT_IS_EXTENDED(datafield)) { @@ -434,6 +443,8 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes) bytea *datafield; bool pfreeit; + if (HeapTupleHasNulls(tuple)) /* paranoia */ + elog(ERROR, "null field found in pg_largeobject"); data = (Form_pg_largeobject) GETSTRUCT(tuple); /* @@ -457,7 +468,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes) off = (int) (obj_desc->offset - pageoff); Assert(off >= 0 && off < LOBLKSIZE); - datafield = &(data->data); + datafield = &(data->data); /* see note at top of file */ pfreeit = false; if (VARATT_IS_EXTENDED(datafield)) { @@ -558,6 +569,8 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) { if ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL) { + if (HeapTupleHasNulls(oldtuple)) /* paranoia */ + elog(ERROR, "null field found in pg_largeobject"); olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple); Assert(olddata->pageno >= pageno); } @@ -575,7 +588,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) * * First, load old data into workbuf */ - datafield = &(olddata->data); + datafield = &(olddata->data); /* see note at top of file */ pfreeit = false; if (VARATT_IS_EXTENDED(datafield)) { @@ -737,6 +750,8 @@ inv_truncate(LargeObjectDesc *obj_desc, int len) olddata = NULL; if ((oldtuple = index_getnext(sd, ForwardScanDirection)) != NULL) { + if (HeapTupleHasNulls(oldtuple)) /* paranoia */ + elog(ERROR, "null field found in pg_largeobject"); olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple); Assert(olddata->pageno >= pageno); } @@ -749,7 +764,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int len) if (olddata != NULL && olddata->pageno == pageno) { /* First, load old data into workbuf */ - bytea *datafield = &(olddata->data); + bytea *datafield = &(olddata->data); /* see note at top of file */ bool pfreeit = false; int pagelen;