Simon Josefsson wrote:
> The format directive must also match the types, do msgfmt -c check that?
> 
> I'm thinking if someone changed the translation file for
> 
>   int foo = 42;
>   printf (_("foo %d"), foo);
> 
> so that _("foo %d") returned "foo %s", then this would become an
> out-of-bounds memory de-reference.

Sure it does. Let's walk through an example:

$ cat foo.c
int foo = 42;
printf (_("You got %d"), foo);

$ xgettext -k_ -o foo.pot foo.c
$ cat foo.pot
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-26 15:44+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: foo.c:2
#, c-format
msgid "You got %d"
msgstr ""

Case 1: The translator uses %s instead of %d.
---------------------------------------------

$ msginit -l fr -i foo.pot; vi fr.po; cat fr.po
# French translations for PACKAGE package.
# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Bruno Haible <[email protected]>, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-26 15:44+0200\n"
"PO-Revision-Date: 2026-08-26 15:47+0200\n"
"Last-Translator: Bruno Haible <[email protected]>\n"
"Language-Team: French <[email protected]>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#: foo.c:2
#, c-format
msgid "You got %d"
msgstr "Vous obtenez %s"

$ : "Like po/Makefile does it"
  msgmerge --for-msgfmt -o fr.gpo fr.po foo.pot && msgfmt -c --statistics 
--verbose -o t-fr.gmo fr.gpo && mv t-fr.gmo fr.gmo
fr.gpo:2: warning: header field 'Project-Id-Version' still has the initial 
default value
fr.gpo:17: format specifications in 'msgid' and 'msgstr' for argument 1 are not 
the same
msgfmt: found 1 fatal error
fr.gpo: 1 translated message.
$ echo $?
1
$ ls -l fr.gmo
ls: cannot access 'fr.gmo': No such file or directory

Case 2: The translator has maliciously removed the '#, c-format' line.
----------------------------------------------------------------------

$ : "Like po/Makefile does it"
  msgmerge --for-msgfmt -o fr.gpo fr.po foo.pot && msgfmt -c --statistics 
--verbose -o t-fr.gmo fr.gpo && mv t-fr.gmo fr.gmo
fr.gpo:2: warning: header field 'Project-Id-Version' still has the initial 
default value
fr.gpo: 0 translated messages.
$ echo $?
0
$ ls -l fr.gmo
-rw-rw-r-- 1 bruno bruno 392 26. Aug 15:54 fr.gmo
$ msgunfmt --force-po fr.gmo
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2026-08-26 15:47+0200\n"
"Last-Translator: Bruno Haible <[email protected]>\n"
"Language-Team: French <[email protected]>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

In this case, the file is there, but the potentially dangerous message was
eliminated.


Bruno




Reply via email to