pgsql-4700
Version:
8.3.6
Bug Link:
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