mirror of
https://github.com/neondatabase/neon.git
synced 2025-12-22 21:59:59 +00:00
fix(compute): validate prewarm_local_cache() input (#12648)
## Problem
```
postgres=> select neon.prewarm_local_cache('\xfcfcfcfc01000000ffffffff070000000000000000000000000000000000000000000000000000000000000000000000000000ff', 1);
WARNING: terminating connection because of crash of another server process
DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
HINT: In a moment you should be able to reconnect to the database and repeat your command.
FATAL: server conn crashed?
```
The function takes a bytea argument and casts it to a C struct, without
validating the contents.
## Summary of changes
Added validation for number of pages to be prefetched and for the chunks
as well.
This commit is contained in:
@@ -49,6 +49,7 @@
|
|||||||
#include "neon.h"
|
#include "neon.h"
|
||||||
#include "neon_lwlsncache.h"
|
#include "neon_lwlsncache.h"
|
||||||
#include "neon_perf_counters.h"
|
#include "neon_perf_counters.h"
|
||||||
|
#include "neon_utils.h"
|
||||||
#include "pagestore_client.h"
|
#include "pagestore_client.h"
|
||||||
#include "communicator.h"
|
#include "communicator.h"
|
||||||
|
|
||||||
@@ -673,8 +674,19 @@ lfc_get_state(size_t max_entries)
|
|||||||
{
|
{
|
||||||
if (GET_STATE(entry, j) != UNAVAILABLE)
|
if (GET_STATE(entry, j) != UNAVAILABLE)
|
||||||
{
|
{
|
||||||
BITMAP_SET(bitmap, i*lfc_blocks_per_chunk + j);
|
/* Validate the buffer tag before including it */
|
||||||
n_pages += 1;
|
BufferTag test_tag = entry->key;
|
||||||
|
test_tag.blockNum += j;
|
||||||
|
|
||||||
|
if (BufferTagIsValid(&test_tag))
|
||||||
|
{
|
||||||
|
BITMAP_SET(bitmap, i*lfc_blocks_per_chunk + j);
|
||||||
|
n_pages += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elog(ERROR, "LFC: Skipping invalid buffer tag during cache state capture: blockNum=%u", test_tag.blockNum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (++i == n_entries)
|
if (++i == n_entries)
|
||||||
@@ -683,7 +695,7 @@ lfc_get_state(size_t max_entries)
|
|||||||
Assert(i == n_entries);
|
Assert(i == n_entries);
|
||||||
fcs->n_pages = n_pages;
|
fcs->n_pages = n_pages;
|
||||||
Assert(pg_popcount((char*)bitmap, ((n_entries << lfc_chunk_size_log) + 7)/8) == n_pages);
|
Assert(pg_popcount((char*)bitmap, ((n_entries << lfc_chunk_size_log) + 7)/8) == n_pages);
|
||||||
elog(LOG, "LFC: save state of %d chunks %d pages", (int)n_entries, (int)n_pages);
|
elog(LOG, "LFC: save state of %d chunks %d pages (validated)", (int)n_entries, (int)n_pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
LWLockRelease(lfc_lock);
|
LWLockRelease(lfc_lock);
|
||||||
@@ -702,6 +714,7 @@ lfc_prewarm(FileCacheState* fcs, uint32 n_workers)
|
|||||||
size_t n_entries;
|
size_t n_entries;
|
||||||
size_t prewarm_batch = Min(lfc_prewarm_batch, readahead_buffer_size);
|
size_t prewarm_batch = Min(lfc_prewarm_batch, readahead_buffer_size);
|
||||||
size_t fcs_size;
|
size_t fcs_size;
|
||||||
|
uint32_t max_prefetch_pages;
|
||||||
dsm_segment *seg;
|
dsm_segment *seg;
|
||||||
BackgroundWorkerHandle* bgw_handle[MAX_PREWARM_WORKERS];
|
BackgroundWorkerHandle* bgw_handle[MAX_PREWARM_WORKERS];
|
||||||
|
|
||||||
@@ -746,6 +759,11 @@ lfc_prewarm(FileCacheState* fcs, uint32 n_workers)
|
|||||||
n_entries = Min(fcs->n_chunks, lfc_prewarm_limit);
|
n_entries = Min(fcs->n_chunks, lfc_prewarm_limit);
|
||||||
Assert(n_entries != 0);
|
Assert(n_entries != 0);
|
||||||
|
|
||||||
|
max_prefetch_pages = n_entries << fcs_chunk_size_log;
|
||||||
|
if (fcs->n_pages > max_prefetch_pages) {
|
||||||
|
elog(ERROR, "LFC: Number of pages in file cache state (%d) is more than the limit (%d)", fcs->n_pages, max_prefetch_pages);
|
||||||
|
}
|
||||||
|
|
||||||
LWLockAcquire(lfc_lock, LW_EXCLUSIVE);
|
LWLockAcquire(lfc_lock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
/* Do not prewarm more entries than LFC limit */
|
/* Do not prewarm more entries than LFC limit */
|
||||||
@@ -898,6 +916,11 @@ lfc_prewarm_main(Datum main_arg)
|
|||||||
{
|
{
|
||||||
tag = fcs->chunks[snd_idx >> fcs_chunk_size_log];
|
tag = fcs->chunks[snd_idx >> fcs_chunk_size_log];
|
||||||
tag.blockNum += snd_idx & ((1 << fcs_chunk_size_log) - 1);
|
tag.blockNum += snd_idx & ((1 << fcs_chunk_size_log) - 1);
|
||||||
|
|
||||||
|
if (!BufferTagIsValid(&tag)) {
|
||||||
|
elog(ERROR, "LFC: Invalid buffer tag: %u", tag.blockNum);
|
||||||
|
}
|
||||||
|
|
||||||
if (!lfc_cache_contains(BufTagGetNRelFileInfo(tag), tag.forkNum, tag.blockNum))
|
if (!lfc_cache_contains(BufTagGetNRelFileInfo(tag), tag.forkNum, tag.blockNum))
|
||||||
{
|
{
|
||||||
(void)communicator_prefetch_register_bufferv(tag, NULL, 1, NULL);
|
(void)communicator_prefetch_register_bufferv(tag, NULL, 1, NULL);
|
||||||
|
|||||||
@@ -183,3 +183,22 @@ alloc_curl_handle(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a BufferTag is valid by verifying all its fields are not invalid.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
BufferTagIsValid(const BufferTag *tag)
|
||||||
|
{
|
||||||
|
#if PG_MAJORVERSION_NUM >= 16
|
||||||
|
return (tag->spcOid != InvalidOid) &&
|
||||||
|
(tag->relNumber != InvalidRelFileNumber) &&
|
||||||
|
(tag->forkNum != InvalidForkNumber) &&
|
||||||
|
(tag->blockNum != InvalidBlockNumber);
|
||||||
|
#else
|
||||||
|
return (tag->rnode.spcNode != InvalidOid) &&
|
||||||
|
(tag->rnode.relNode != InvalidOid) &&
|
||||||
|
(tag->forkNum != InvalidForkNumber) &&
|
||||||
|
(tag->blockNum != InvalidBlockNumber);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define __NEON_UTILS_H__
|
#define __NEON_UTILS_H__
|
||||||
|
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
|
#include "storage/buf_internals.h"
|
||||||
|
|
||||||
#ifndef WALPROPOSER_LIB
|
#ifndef WALPROPOSER_LIB
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
@@ -16,6 +17,9 @@ void pq_sendint32_le(StringInfo buf, uint32 i);
|
|||||||
void pq_sendint64_le(StringInfo buf, uint64 i);
|
void pq_sendint64_le(StringInfo buf, uint64 i);
|
||||||
void disable_core_dump(void);
|
void disable_core_dump(void);
|
||||||
|
|
||||||
|
/* Buffer tag validation function */
|
||||||
|
bool BufferTagIsValid(const BufferTag *tag);
|
||||||
|
|
||||||
#ifndef WALPROPOSER_LIB
|
#ifndef WALPROPOSER_LIB
|
||||||
|
|
||||||
CURL * alloc_curl_handle(void);
|
CURL * alloc_curl_handle(void);
|
||||||
|
|||||||
Reference in New Issue
Block a user