1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-27 00:12:01 +03:00
Files
postgres/src/backend/storage/aio/aio_target.c
Andres Freund 0d9114b704 aio: Fix crash potential for pg_aios views due to late state update
pgaio_io_reclaim() reset the fields in PgAioHandle before updating the state
to IDLE or incrementing the generation. For most things that's OK, but for
pg_get_aios() it is not - if it copied the PgAioHandle while fields were being
reset, we wouldn't detect that and could call
pgaio_io_get_target_description() with ioh->target == PGAIO_TID_INVALID,
leading to a crash.

Fix this issue by incrementing the generation and state earlier, before
resetting.

Also add an assertion to pgaio_io_get_target_description() for the target to
be valid - that'd have made this case a bit easier to debug. While at it,
add/update a few related assertions.

Author: Alexander Lakhin <exclusion@gmail.com>
Discussion: https://postgr.es/m/062daca9-dfad-4750-9da8-b13388301ad9@gmail.com
2025-04-25 13:31:13 -04:00

123 lines
3.2 KiB
C

/*-------------------------------------------------------------------------
*
* aio_target.c
* AIO - Functionality related to executing IO for different targets
*
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/storage/aio/aio_target.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "storage/aio.h"
#include "storage/aio_internal.h"
#include "storage/smgr.h"
/*
* Registry for entities that can be the target of AIO.
*/
static const PgAioTargetInfo *pgaio_target_info[] = {
[PGAIO_TID_INVALID] = &(PgAioTargetInfo) {
.name = "invalid",
},
[PGAIO_TID_SMGR] = &aio_smgr_target_info,
};
/* --------------------------------------------------------------------------------
* Public target related functions operating on IO Handles
* --------------------------------------------------------------------------------
*/
bool
pgaio_io_has_target(PgAioHandle *ioh)
{
return ioh->target != PGAIO_TID_INVALID;
}
/*
* Return the name for the target associated with the IO. Mostly useful for
* debugging/logging.
*/
const char *
pgaio_io_get_target_name(PgAioHandle *ioh)
{
/* explicitly allow INVALID here, function used by debug messages */
Assert(ioh->target >= PGAIO_TID_INVALID && ioh->target < PGAIO_TID_COUNT);
return pgaio_target_info[ioh->target]->name;
}
/*
* Assign a target to the IO.
*
* This has to be called exactly once before pgaio_io_start_*() is called.
*/
void
pgaio_io_set_target(PgAioHandle *ioh, PgAioTargetID targetid)
{
Assert(ioh->state == PGAIO_HS_HANDED_OUT);
Assert(ioh->target == PGAIO_TID_INVALID);
ioh->target = targetid;
}
PgAioTargetData *
pgaio_io_get_target_data(PgAioHandle *ioh)
{
return &ioh->target_data;
}
/*
* Return a stringified description of the IO's target.
*
* The string is localized and allocated in the current memory context.
*/
char *
pgaio_io_get_target_description(PgAioHandle *ioh)
{
/* disallow INVALID, there wouldn't be a description */
Assert(ioh->target > PGAIO_TID_INVALID && ioh->target < PGAIO_TID_COUNT);
return pgaio_target_info[ioh->target]->describe_identity(&ioh->target_data);
}
/* --------------------------------------------------------------------------------
* Internal target related functions operating on IO Handles
* --------------------------------------------------------------------------------
*/
/*
* Internal: Check if pgaio_io_reopen() is available for the IO.
*/
bool
pgaio_io_can_reopen(PgAioHandle *ioh)
{
Assert(ioh->target > PGAIO_TID_INVALID && ioh->target < PGAIO_TID_COUNT);
return pgaio_target_info[ioh->target]->reopen != NULL;
}
/*
* Internal: Before executing an IO outside of the context of the process the
* IO has been staged in, the file descriptor has to be reopened - any FD
* referenced in the IO itself, won't be valid in the separate process.
*/
void
pgaio_io_reopen(PgAioHandle *ioh)
{
Assert(ioh->target > PGAIO_TID_INVALID && ioh->target < PGAIO_TID_COUNT);
Assert(ioh->op > PGAIO_OP_INVALID && ioh->op < PGAIO_OP_COUNT);
pgaio_target_info[ioh->target]->reopen(ioh);
}