Thread: BUG #8702: psql \df+ translate_columns[] overflow and unexpected gettext translation
BUG #8702: psql \df+ translate_columns[] overflow and unexpected gettext translation
From
eshkinkot@gmail.com
Date:
The following bug has been logged on the website: Bug reference: 8702 Logged by: Sergey Burladyan Email address: eshkinkot@gmail.com PostgreSQL version: 9.3.1 Operating system: Debian testing Description: I get this result from \df+ foo: --- echo $LANG --- ru_RU.UTF-8 --- --- create function foo() returns void language plpgsql as $$ begin end $$; СпиÑок ÑÑнкÑий -[ RECORD 1 ]----------+--------------------------------------------------------------------------------------------------------------------- Ð¡Ñ ÐµÐ¼Ð° | public ÐÐ¼Ñ | foo Тип даннÑÑ ÑезÑлÑÑаÑа | void Ð¢Ð¸Ð¿Ñ Ð´Ð°Ð½Ð½ÑÑ Ð°ÑгÑменÑов | Тип | обÑÑÐ½Ð°Ñ ÐезопаÑноÑÑÑ | вÑзÑваÑÑего ÐзменÑивоÑÑÑ | volatile ÐÐ»Ð°Ð´ÐµÐ»ÐµÑ | seb ЯзÑк | plpgsql ÐÑÑ Ð¾Ð´Ð½Ñй код | begin end ÐпиÑание | Project-Id-Version: PostgreSQL 9 current | Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org | POT-Creation-Date: 2013-05-20 02:16+0000 | PO-Revision-Date: 2013-05-20 20:02+0400 | Last-Translator: Alexander Lakhin <exclusion@gmail.com> | Language-Team: Russian <pgtranslation-translators@pgfoundry.org> | Language: ru | MIME-Version: 1.0 | Content-Type: text/plain; charset=UTF-8 | Content-Transfer-Encoding: 8bit | X-Poedit-Language: Russian | X-Poedit-Country: RUSSIAN FEDERATION | X-Generator: Lokalize 1.5 | Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); | looks like "Description" field contains header from ru.mo messages file. src/bin/psql/describe.c:describeFunctions translate_columns[] array size is less than the maximum number of columns in query and it overflowed in src/bin/psql/print.c:printQuery at ... translate = (opt->translate_columns && opt->translate_columns[c]); ... so in my case opt->translate_columns[10] have garbage == true and "Description" field content translated unexpectedly.
Re: BUG #8702: psql \df+ translate_columns[] overflow and unexpected gettext translation
From
Peter Eisentraut
Date:
On Fri, 2013-12-27 at 00:00 +0000, eshkinkot@gmail.com wrote: > I get this result from \df+ foo: > --- echo $LANG > --- ru_RU.UTF-8 > --- > --- create function foo() returns void language plpgsql as $$ begin end $$; > src/bin/psql/describe.c:describeFunctions translate_columns[] array size > is less than the maximum number of columns in query and it overflowed in > src/bin/psql/print.c:printQuery at > ... > translate = (opt->translate_columns && > opt->translate_columns[c]); > ... > so in my case opt->translate_columns[10] have garbage == true and > "Description" field > content translated unexpectedly. There are several places that are similarly broken. See attached patch. Obviously, this is not a very robust programming interface, if the same mistake has been repeated three times independently.
Attachment
Re: BUG #8702: psql \df+ translate_columns[] overflow and unexpected gettext translation
From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes: > There are several places that are similarly broken. See attached patch. > Obviously, this is not a very robust programming interface, if the same > mistake has been repeated three times independently. Yeah :-(. How about adding an Assert as in the attached? I had to make the Assert say ">=" not just "==" because there are numerous callers that use the same array for both normal and verbose output. This seems a bit shaky but it's not actually broken anywhere. I think. The breakage in \df appears to be the fault of the addition of a translated "security definer/invoker" column, which clearly nobody really tested the translations for, or they'd have noticed that the column marking in translate_columns[] was wrong :-(. I believe the "translation support" in \dy (event triggers) is pretty bogus as well. regards, tom lane diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 96322ca..e915262 100644 *** a/src/bin/psql/describe.c --- b/src/bin/psql/describe.c *************** describeFunctions(const char *functypes, *** 224,230 **** PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; ! static const bool translate_columns[] = {false, false, false, false, true, true, false, false, false, false}; if (strlen(functypes) != strspn(functypes, "antwS+")) { --- 224,230 ---- PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; ! static const bool translate_columns[] = {false, false, false, false, true, true, true, false, false, false, false}; if (strlen(functypes) != strspn(functypes, "antwS+")) { *************** describeFunctions(const char *functypes, *** 457,462 **** --- 457,463 ---- myopt.title = _("List of functions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** permissionsList(const char *pattern) *** 789,794 **** --- 790,796 ---- myopt.title = buf.data; myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** listDefaultACLs(const char *pattern) *** 862,867 **** --- 864,870 ---- myopt.title = buf.data; myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** objectDescription(const char *pattern, b *** 1034,1039 **** --- 1037,1043 ---- myopt.title = _("Object descriptions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** listTables(const char *tabtypes, const c *** 2818,2823 **** --- 2822,2828 ---- myopt.title = _("List of relations"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); } *************** listConversions(const char *pattern, boo *** 2999,3005 **** PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; ! static const bool translate_columns[] = {false, false, false, false, true}; initPQExpBuffer(&buf); --- 3004,3011 ---- PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; ! static const bool translate_columns[] = ! {false, false, false, false, true, false}; initPQExpBuffer(&buf); *************** listConversions(const char *pattern, boo *** 3055,3060 **** --- 3061,3067 ---- myopt.title = _("List of conversions"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** listEventTriggers(const char *pattern, b *** 3116,3121 **** --- 3123,3129 ---- myopt.title = _("List of event triggers"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** listCasts(const char *pattern, bool verb *** 3134,3140 **** PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; ! static const bool translate_columns[] = {false, false, false, true}; initPQExpBuffer(&buf); --- 3142,3148 ---- PQExpBufferData buf; PGresult *res; printQueryOpt myopt = pset.popt; ! static const bool translate_columns[] = {false, false, false, true, false}; initPQExpBuffer(&buf); *************** listCasts(const char *pattern, bool verb *** 3214,3219 **** --- 3222,3228 ---- myopt.title = _("List of casts"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** listCollations(const char *pattern, bool *** 3289,3294 **** --- 3298,3304 ---- myopt.title = _("List of collations"); myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** describeOneTSParser(const char *oid, con *** 3548,3553 **** --- 3558,3564 ---- myopt.topt.default_footer = false; myopt.translate_header = true; myopt.translate_columns = translate_columns; + myopt.n_translate_columns = lengthof(translate_columns); printQuery(res, &myopt, pset.queryFout, pset.logfile); *************** describeOneTSParser(const char *oid, con *** 3579,3584 **** --- 3590,3596 ---- myopt.topt.default_footer = true; myopt.translate_header = true; myopt.translate_columns = NULL; + myopt.n_translate_columns = 0; printQuery(res, &myopt, pset.queryFout, pset.logfile); diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 736225c..fdf4dcc 100644 *** a/src/bin/psql/print.c --- b/src/bin/psql/print.c *************** printQuery(const PGresult *result, const *** 2596,2601 **** --- 2596,2605 ---- printTableInit(&cont, &opt->topt, opt->title, PQnfields(result), PQntuples(result)); + /* Assert caller supplied enough translate_columns[] entries */ + Assert(opt->translate_columns == NULL || + opt->n_translate_columns >= cont.ncolumns); + for (i = 0; i < cont.ncolumns; i++) { char align; diff --git a/src/bin/psql/print.h b/src/bin/psql/print.h index 9cfa3b6..41ba798 100644 *** a/src/bin/psql/print.h --- b/src/bin/psql/print.h *************** typedef struct printQueryOpt *** 146,151 **** --- 146,152 ---- bool translate_header; /* do gettext on column headers */ const bool *translate_columns; /* translate_columns[i-1] => do * gettext on col i */ + int n_translate_columns; /* length of translate_columns[] */ } printQueryOpt; diff --git a/src/bin/scripts/createlang.c b/src/bin/scripts/createlang.c index 5cfba8e..92ab975 100644 *** a/src/bin/scripts/createlang.c --- b/src/bin/scripts/createlang.c *************** main(int argc, char *argv[]) *** 160,165 **** --- 160,167 ---- popt.title = _("Procedural Languages"); popt.translate_header = true; popt.translate_columns = translate_columns; + popt.n_translate_columns = lengthof(translate_columns); + printQuery(result, &popt, stdout, NULL); PQfinish(conn); diff --git a/src/bin/scripts/droplang.c b/src/bin/scripts/droplang.c index b9664a9..3650096 100644 *** a/src/bin/scripts/droplang.c --- b/src/bin/scripts/droplang.c *************** main(int argc, char *argv[]) *** 159,164 **** --- 159,166 ---- popt.title = _("Procedural Languages"); popt.translate_header = true; popt.translate_columns = translate_columns; + popt.n_translate_columns = lengthof(translate_columns); + printQuery(result, &popt, stdout, NULL); PQfinish(conn);
Re: BUG #8702: psql \df+ translate_columns[] overflow and unexpected gettext translation
From
Tom Lane
Date:
Tom Lane <tgl@sss.pgh.pa.us> writes: > Peter Eisentraut <peter_e@gmx.net> writes: >> Obviously, this is not a very robust programming interface, if the same >> mistake has been repeated three times independently. > Yeah :-(. How about adding an Assert as in the attached? I cleaned up the issues in \dy and committed this. regards, tom lane