mirror of
https://github.com/postgres/postgres.git
synced 2025-06-23 14:01:44 +03:00
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
This commit is contained in:
@ -34,29 +34,20 @@ PG_FUNCTION_INFO_V1(gist_page_items_bytea);
|
||||
#define ItemPointerGetDatum(X) PointerGetDatum(X)
|
||||
|
||||
|
||||
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)))
|
||||
@ -76,12 +67,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)
|
||||
@ -103,7 +120,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(flags, nflags,
|
||||
TEXTOID,
|
||||
-1, false, TYPALIGN_INT));
|
||||
@ -124,7 +141,6 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
|
||||
Tuplestorestate *tupstore;
|
||||
MemoryContext oldcontext;
|
||||
Page page;
|
||||
GISTPageOpaque opaq;
|
||||
OffsetNumber offset;
|
||||
OffsetNumber maxoff = InvalidOffsetNumber;
|
||||
|
||||
@ -157,29 +173,11 @@ gist_page_items_bytea(PG_FUNCTION_ARGS)
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
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 = (GISTPageOpaque) PageGetSpecialPointer(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");
|
||||
@ -276,7 +274,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))
|
||||
{
|
||||
|
Reference in New Issue
Block a user