pgsql-4700

Version:

8.3.6

Bug Link:

http://postgresql.1045698.n5.nabble.com/BUG-4700-SIGSEGV-with-incorrect-input-to-to-char-function-td2129181.html

Symptom:

When supply to_char with certain incorrect input, it result in SIGSEGV, instead of giving error message.

Failure type:

Crash

Is there any log message?

Yes

How it is diagnosed:

Reproduced!

How to reproduce:

$ select to_char(0, 'TMMON TMMon TMmon TMMONTH TMMonth TMDAY TMDay TMday TMDY TMDy TMdy');

server closed the connection unexpectedly

        This probably means the server terminated abnormally

        before or while processing the request.

//should be

//ERROR:  multiple decimal points

Root Cause:

Uninitialized pointer.

In converting int to char, a series of conversion functions will be called, when it decodes there’s something wrong with the conversion format, it tries to remove from the num cache, which is allocated an entry in the cache during conversion. But when it tries to remove, it will dereference a global static entry pointer called last_NUMCacheEntry, which is unfortunately uninitialized. The patch is to initialize it the point to the first entry of the cache.

src/backend/utils/adt/formatting.c

static NUMCacheEntry NUMCache[NUM_CACHE_FIELDS + 1];
-  static NUMCacheEntry *last_NUMCacheEntry;

+ static NUMCacheEntry *last_NUMCacheEntry = NUMCache + 0;

Datum

int4_to_char(PG_FUNCTION_ARGS)

{

        ...

        NUM_TOCHAR_prepare;

        ...

        NUM_TOCHAR_finish;

        PG_RETURN_TEXT_P(result);

}

/* ----------

 * MACRO: Start part of NUM - for all NUM's to_char variants

 *        (sorry, but I hate copy same code - macro is better..)

 * ----------

 */

#define NUM_TOCHAR_prepare \

do { \

        len = VARSIZE(fmt) - VARHDRSZ;                                        \

        if (len <= 0 || len >= (INT_MAX-VARHDRSZ)/NUM_MAX_ITEM_SIZ)                \

                return DirectFunctionCall1(textin, CStringGetDatum(""));        \

        result        = (text *) palloc0((len * NUM_MAX_ITEM_SIZ) + 1 + VARHDRSZ);        \

        format        = NUM_cache(len, &Num, VARDATA(fmt), &shouldFree);                \

} while (0)

/* ----------

 * Cache routine for NUM to_char version

 * ----------

 */

static FormatNode *

NUM_cache(int len, NUMDesc *Num, char *pars_str, bool *shouldFree)

{

        FormatNode *format = NULL;

        char           *str;

        /*

         * Convert VARDATA() to string

         */

        str = (char *) palloc(len + 1);

        memcpy(str, pars_str, len);

        *(str + len) = '\0';

        /*

         * Allocate new memory if format picture is bigger than static cache and

         * not use cache (call parser always). This branches sets shouldFree to

         * true, accordingly.

         */

        if (len > NUM_CACHE_SIZE)

        {

                format = (FormatNode *) palloc((len + 1) * sizeof(FormatNode));

                *shouldFree = true;

                zeroize_NUM(Num);

                parse_format(format, str, NUM_keywords,

                                         NULL, NUM_index, NUM_TYPE, Num);

                (format + len)->type = NODE_TYPE_END;        /* Paranoia? */

        }

        else

                ...

        pfree(str);

        return format;

}

/* ----------

 * Format parser, search small keywords and keyword's suffixes, and make

 * format-node tree.

 *

 * for DATE-TIME & NUMBER version

 * ----------

 */

static void

parse_format(FormatNode *node, char *str, const KeyWord *kw,

                         KeySuffix *suf, const int *index, int ver, NUMDesc *Num)

{

...

        while (*str)

        {

                suffix = 0;

                ...

                if (*str && (n->key = index_seq_search(str, kw, index)) != NULL)

                {

                        ...

                        /*

                         * NUM version: Prepare global NUMDesc struct

                         */

                        if (ver == NUM_TYPE)

                                NUMDesc_prepare(Num, n);

                        ...

                }

                else if (*str)

                {

                        ...

                }

                ...

        }

        n->type = NODE_TYPE_END;

        n->suffix = 0;

        return;

}

/* ----------

 * Prepare NUMDesc (number description struct) via FormatNode struct

 * ----------

 */

static void

NUMDesc_prepare(NUMDesc *num, FormatNode *n)

{

        if (n->type != NODE_TYPE_ACTION)

                return;

        switch (n->key->id)

        {

                ...

                case NUM_DEC:

                        if (IS_DECIMAL(num))

                        {

                                NUM_cache_remove(last_NUMCacheEntry);

                                ereport(ERROR,

                                                (errcode(ERRCODE_SYNTAX_ERROR),

                                                 errmsg("multiple decimal points")));

                        }

                        ...

                        num->flag |= NUM_F_DECIMAL;

                        break;

                ...

        }

        return;

}

static void

NUM_cache_remove(NUMCacheEntry *ent)

{

#ifdef DEBUG_TO_FROM_CHAR

        elog(DEBUG_elog_output, "REMOVING ENTRY (%s)", ent->str);

#endif

        *ent->str = '\0';//ent is NULL, deference it will cause seg fault!

        ent->age = 0;

}

Can we anticipate error?

signal handler

What is sufficient for diagnosing the failure?

signal handler + back trace