aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2020-09-23 11:36:13 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2020-09-23 11:36:13 -0400
commit5d0c80658328aa654864ce4e0d0b4031fda0ddd5 (patch)
tree9a1192ea9ca181d3f525475a7fb61c9c02e50961
parent11071da39e3969430b9c24ff792d806317e8d736 (diff)
downloadpostgresql-5d0c80658328aa654864ce4e0d0b4031fda0ddd5.tar.gz
postgresql-5d0c80658328aa654864ce4e0d0b4031fda0ddd5.zip
Avoid possible dangling-pointer access in tsearch_readline_callback.
tsearch_readline() saves the string pointer it returns to the caller for possible use in the associated error context callback. However, the caller will usually pfree that string sometime before it next calls tsearch_readline(), so that there is a window where an ereport will try to print an already-freed string. The built-in users of tsearch_readline() happen to all do that pfree at the bottoms of their loops, so that the window is effectively empty for them. However, this is not documented as a requirement, and contrib/dict_xsyn doesn't do it like that, so it seems likely that third-party dictionaries might have live bugs here. The practical consequences of this seem pretty limited in any case, since production builds wouldn't clobber the freed string immediately, besides which you'd not expect syntax errors in dictionary files being used in production. Still, it's clearly a bug waiting to bite somebody. Fix by pstrdup'ing the string to be saved for the error callback, and then pfree'ing it next time through. It's been like this for a long time, so back-patch to all supported branches. Discussion: https://postgr.es/m/48A4FA71-524E-41B9-953A-FD04EF36E2E7@yesql.se
-rw-r--r--src/backend/tsearch/ts_locale.c31
1 files changed, 29 insertions, 2 deletions
diff --git a/src/backend/tsearch/ts_locale.c b/src/backend/tsearch/ts_locale.c
index 309f7c0459a..c57103b656b 100644
--- a/src/backend/tsearch/ts_locale.c
+++ b/src/backend/tsearch/ts_locale.c
@@ -147,10 +147,28 @@ tsearch_readline(tsearch_readline_state *stp)
{
char *result;
+ /* Advance line number to use in error reports */
stp->lineno++;
- stp->curline = NULL;
+
+ /* Clear curline, it's no longer relevant */
+ if (stp->curline)
+ {
+ pfree(stp->curline);
+ stp->curline = NULL;
+ }
+
+ /* Collect next line, if there is one */
result = t_readline(stp->fp);
- stp->curline = result;
+ if (!result)
+ return NULL;
+
+ /*
+ * Save a copy of the line for possible use in error reports. (We cannot
+ * just save "result", since it's likely to get pfree'd at some point by
+ * the caller; an error after that would try to access freed data.)
+ */
+ stp->curline = pstrdup(result);
+
return result;
}
@@ -160,7 +178,16 @@ tsearch_readline(tsearch_readline_state *stp)
void
tsearch_readline_end(tsearch_readline_state *stp)
{
+ /* Suppress use of curline in any error reported below */
+ if (stp->curline)
+ {
+ pfree(stp->curline);
+ stp->curline = NULL;
+ }
+
+ /* Release other resources */
FreeFile(stp->fp);
+
/* Pop the error context stack */
error_context_stack = stp->cb.previous;
}