Hi hackers,

The allocations in src/backend/commands/explain_state.c
used sizeof(char *) instead of sizeof(ExplainExtensionOption),
which could cause a crash if an extension would register
more than 8 extension EXPLAIN options:

Attached small example extension, test_explain_state.txt,
to demonstrate the bug:

LOAD 'test_explain_state';
LOAD
EXPLAIN (test_explain_opt9) SELECT 1;
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
connection to server was lost

/Joel
/*-------------------------------------------------------------------------
 *
 * test_explain_state.c
 *        Crash reproducer for the sizeof bug in RegisterExtensionExplainOption.
 *
 *        RegisterExtensionExplainOption allocates its 
ExplainExtensionOptionArray
 *        using sizeof(char *) (8 bytes) instead of 
sizeof(ExplainExtensionOption)
 *        (16 bytes).  The initial allocation is for 16 "slots" at 8 bytes each 
=
 *        128 bytes, but each ExplainExtensionOption is 16 bytes, so only 8 
entries
 *        actually fit.  Registering a 9th option writes past the end of the
 *        allocation, corrupting the heap.
 *
 *        This module registers 10 options, which is enough to trigger the bug.
 */
#include "postgres.h"

#include "commands/defrem.h"
#include "commands/explain_state.h"
#include "fmgr.h"

PG_MODULE_MAGIC;

static const char *const option_names[] = {
        "test_explain_opt0",
        "test_explain_opt1",
        "test_explain_opt2",
        "test_explain_opt3",
        "test_explain_opt4",
        "test_explain_opt5",
        "test_explain_opt6",
        "test_explain_opt7",
        "test_explain_opt8",
        "test_explain_opt9",
};

#define NUM_OPTIONS lengthof(option_names)

static void test_explain_option_handler(ExplainState *es, DefElem *opt,
                                                                                
ParseState *pstate);

void
_PG_init(void)
{
        for (int i = 0; i < NUM_OPTIONS; i++)
                RegisterExtensionExplainOption(option_names[i],
                                                                           
test_explain_option_handler);
}

static void
test_explain_option_handler(ExplainState *es, DefElem *opt,
                                                        ParseState *pstate)
{
        ereport(NOTICE,
                        (errmsg("test_explain_state: processed option \"%s\"",
                                        opt->defname)));
}

Attachment: 0001-Fix-sizeof-bug-in-RegisterExtensionExplainOption.patch
Description: Binary data

Reply via email to