aboutsummaryrefslogtreecommitdiff
path: root/contrib/pageinspect/gistfuncs.c
diff options
context:
space:
mode:
authorMichael Paquier <michael@paquier.xyz>2023-03-02 14:03:02 +0900
committerMichael Paquier <michael@paquier.xyz>2023-03-02 14:03:02 +0900
commitbe753639d35df72c1a7b42ec114393fd962f2b01 (patch)
treedab6af8cd49933df45347e547ce85a22679e0ffe /contrib/pageinspect/gistfuncs.c
parentd7056bc1c71d2d876adb60dda8e0ba962e8279df (diff)
downloadpostgresql-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.c82
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))
{