gettext: avoid using gettext if the locale dir is not present

In cc5e1bf99247 (gettext: avoid initialization if the locale dir is not present, 2018-04-21) Git was taught to avoid a costly gettext start-up when there are not even any localized messages to work with. But we still called `gettext()` and `ngettext()` functions. Which caused a problem in Git for Windows when the libgettext that is consumed from the MSYS2 project stopped using a runtime prefix in https://github.com/msys2/MINGW-packages/pull/10461 Due to that change, we now use an unintialized gettext machinery that might get auto-initialized _using an unintended locale directory_: `C:\mingw64\share\locale`. Let's record the fact when the gettext initialization was skipped, and skip calling the gettext functions accordingly. This addresses CVE-2023-25815. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

Johannes Schindelin committed Feb 22, 2023 at 12:40 UTC c4137be0f5a6edf9a9044e6e43ecf4468c7a4046
2 files changed +10 -1
gettext.c
+4
@@ -109,6 +109,8 @@ static void init_gettext_charset(const char *domain)
109 setlocale(LC_CTYPE, "C");
110 }
111
112 +int git_gettext_enabled = 0;
113 +
114 void git_setup_gettext(void)
115 {
116 const char *podir = getenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT);
@@ -130,6 +132,8 @@ void git_setup_gettext(void)
132 init_gettext_charset("git");
133 textdomain("git");
134
135 + git_gettext_enabled = 1;
136 +
137 free(p);
138 }
139
gettext.h
+6 -1
@@ -31,9 +31,11 @@
31 int use_gettext_poison(void);
32
33 #ifndef NO_GETTEXT
34 +extern int git_gettext_enabled;
35 void git_setup_gettext(void);
36 int gettext_width(const char *s);
37 #else
38 +#define git_gettext_enabled (0)
39 static inline void git_setup_gettext(void)
40 {
41 use_gettext_poison(); /* getenv() reentrancy paranoia */
@@ -48,7 +50,8 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
50 {
51 if (!*msgid)
52 return "";
51 - return use_gettext_poison() ? "# GETTEXT POISON #" : gettext(msgid);
53 + return use_gettext_poison() ? "# GETTEXT POISON #" :
54 + !git_gettext_enabled ? msgid : gettext(msgid);
55 }
56
57 static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)
@@ -56,6 +59,8 @@ const char *Q_(const char *msgid, const char *plu, unsigned long n)
59 {
60 if (use_gettext_poison())
61 return "# GETTEXT POISON #";
62 + if (!git_gettext_enabled)
63 + return n == 1 ? msgid : plu;
64 return ngettext(msgid, plu, n);
65 }
66