diff options
author | Michael Paquier <michael@paquier.xyz> | 2023-03-02 14:03:02 +0900 |
---|---|---|
committer | Michael Paquier <michael@paquier.xyz> | 2023-03-02 14:03:02 +0900 |
commit | be753639d35df72c1a7b42ec114393fd962f2b01 (patch) | |
tree | dab6af8cd49933df45347e547ce85a22679e0ffe /contrib/pageinspect/gistfuncs.c | |
parent | d7056bc1c71d2d876adb60dda8e0ba962e8279df (diff) | |
download | postgresql-be753639d35df72c1a7b42ec114393fd962f2b01.tar.gz postgresql-be753639d35df72c1a7b42ec114393fd962f2b01.zip |
pageinspect: Fix crash with gist_page_items()
Attempting to use this function with a raw page not coming from a GiST
index would cause a crash, as it was missing the same sanity checks as
gist_page_items_bytea(). This slightly refactors the code so as all the
basic validation checks for GiST pages are done in a single routine,
in the same fashion as the pageinspect functions for hash and BRIN.
This fixes an issue similar to 076f4d9. A test is added to stress for
this case. While on it, I have added a similar test for
brin_page_items() with a combination make of a valid GiST index and a
raw btree page. This one was already protected, but it was not tested.
Reported-by: Egor Chindyaskin
Author: Dmitry Koval
Discussion: https://postgr.es/m/17815-fc4a2d3b74705703@postgresql.org
Backpatch-through: 14
Diffstat (limited to 'contrib/pageinspect/gistfuncs.c')
-rw-r--r-- | contrib/pageinspect/gistfuncs.c | 82 |
1 files changed, 40 insertions, 42 deletions
diff --git a/contrib/pageinspect/gistfuncs.c b/contrib/pageinspect/gistfuncs.c index 100697814dc..3dca7f1318f 100644 --- a/contrib/pageinspect/gistfuncs.c +++ b/contrib/pageinspect/gistfuncs.c @@ -32,29 +32,20 @@ PG_FUNCTION_INFO_V1(gist_page_items_bytea); #define IS_GIST(r) ((r)->rd_rel->relam == GIST_AM_OID) -Datum -gist_page_opaque_info(PG_FUNCTION_ARGS) +static Page verify_gist_page(bytea *raw_page); + +/* + * Verify that the given bytea contains a GIST page or die in the attempt. + * A pointer to the page is returned. + */ +static Page +verify_gist_page(bytea *raw_page) { - bytea *raw_page = PG_GETARG_BYTEA_P(0); - TupleDesc tupdesc; - Page page; + Page page = get_page_from_raw(raw_page); GISTPageOpaque opaq; - HeapTuple resultTuple; - Datum values[4]; - bool nulls[4]; - Datum flags[16]; - int nflags = 0; - uint16 flagbits; - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to use raw page functions"))); - - page = get_page_from_raw(raw_page); if (PageIsNew(page)) - PG_RETURN_NULL(); + return page; /* verify the special space has the expected size */ if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData))) @@ -74,12 +65,38 @@ gist_page_opaque_info(PG_FUNCTION_ARGS) GIST_PAGE_ID, opaq->gist_page_id))); + return page; +} + +Datum +gist_page_opaque_info(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + TupleDesc tupdesc; + Page page; + HeapTuple resultTuple; + Datum values[4]; + bool nulls[4]; + Datum flags[16]; + int nflags = 0; + uint16 flagbits; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + page = verify_gist_page(raw_page); + + if (PageIsNew(page)) + PG_RETURN_NULL(); + /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); /* Convert the flags bitmask to an array of human-readable names */ - flagbits = opaq->flags; + flagbits = GistPageGetOpaque(page)->flags; if (flagbits & F_LEAF) flags[nflags++] = CStringGetTextDatum("leaf"); if (flagbits & F_DELETED) @@ -101,7 +118,7 @@ gist_page_opaque_info(PG_FUNCTION_ARGS) values[0] = LSNGetDatum(PageGetLSN(page)); values[1] = LSNGetDatum(GistPageGetNSN(page)); - values[2] = Int64GetDatum(opaq->rightlink); + values[2] = Int64GetDatum(GistPageGetOpaque(page)->rightlink); values[3] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID)); /* Build and return the result tuple. */ @@ -116,7 +133,6 @@ gist_page_items_bytea(PG_FUNCTION_ARGS) bytea *raw_page = PG_GETARG_BYTEA_P(0); ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; Page page; - GISTPageOpaque opaq; OffsetNumber offset; OffsetNumber maxoff = InvalidOffsetNumber; @@ -127,29 +143,11 @@ gist_page_items_bytea(PG_FUNCTION_ARGS) InitMaterializedSRF(fcinfo, 0); - page = get_page_from_raw(raw_page); + page = verify_gist_page(raw_page); if (PageIsNew(page)) PG_RETURN_NULL(); - /* verify the special space has the expected size */ - if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData))) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input page is not a valid %s page", "GiST"), - errdetail("Expected special size %d, got %d.", - (int) MAXALIGN(sizeof(GISTPageOpaqueData)), - (int) PageGetSpecialSize(page)))); - - opaq = GistPageGetOpaque(page); - if (opaq->gist_page_id != GIST_PAGE_ID) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input page is not a valid %s page", "GiST"), - errdetail("Expected %08x, got %08x.", - GIST_PAGE_ID, - opaq->gist_page_id))); - /* Avoid bogus PageGetMaxOffsetNumber() call with deleted pages */ if (GistPageIsDeleted(page)) elog(NOTICE, "page is deleted"); @@ -220,7 +218,7 @@ gist_page_items(PG_FUNCTION_ARGS) errmsg("\"%s\" is not a %s index", RelationGetRelationName(indexRel), "GiST"))); - page = get_page_from_raw(raw_page); + page = verify_gist_page(raw_page); if (PageIsNew(page)) { |