]> sourceware.org Git - glibc.git/blame - intl/localealias.c
syslog: Fix integer overflow in __vsyslog_internal (CVE-2023-6780)
[glibc.git] / intl / localealias.c
CommitLineData
c84142e8 1/* Handle aliases for locale names.
dff8da6b 2 Copyright (C) 1995-2024 Free Software Foundation, Inc.
24906b43 3
6d248857
WN
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 2.1 of the License, or
7 (at your option) any later version.
0393dfd6 8
6d248857 9 This program is distributed in the hope that it will be useful,
c84142e8 10 but WITHOUT ANY WARRANTY; without even the implied warranty of
6d248857
WN
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
24906b43 13
6d248857 14 You should have received a copy of the GNU Lesser General Public License
5a82c748 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
24906b43 16
17c389fc
UD
17/* Tell glibc's <string.h> to provide a prototype for mempcpy().
18 This must come before <config.h> because <config.h> may include
19 <features.h>, and once <features.h> has been included, it's too late. */
20#ifndef _GNU_SOURCE
21# define _GNU_SOURCE 1
22#endif
23
24906b43
RM
24#ifdef HAVE_CONFIG_H
25# include <config.h>
26#endif
27
28#include <ctype.h>
29#include <stdio.h>
2706ee38
UD
30#if defined _LIBC || defined HAVE___FSETLOCKING
31# include <stdio_ext.h>
32#endif
24906b43
RM
33#include <sys/types.h>
34
35#ifdef __GNUC__
0f283ffc 36# undef alloca
24906b43 37# define alloca __builtin_alloca
fa00327f 38# define HAVE_ALLOCA 1
24906b43 39#else
6d248857
WN
40# ifdef _MSC_VER
41# include <malloc.h>
42# define alloca _alloca
24906b43 43# else
6d248857
WN
44# if defined HAVE_ALLOCA_H || defined _LIBC
45# include <alloca.h>
24906b43 46# else
6d248857
WN
47# ifdef _AIX
48 #pragma alloca
49# else
50# ifndef alloca
24906b43 51char *alloca ();
6d248857 52# endif
24906b43
RM
53# endif
54# endif
55# endif
56#endif
57
0555fcce
UD
58#include <stdlib.h>
59#include <string.h>
24906b43 60
24906b43
RM
61#include "gettextP.h"
62
6d248857
WN
63#ifdef ENABLE_RELOCATABLE
64# include "relocatable.h"
65#else
66# define relocate(pathname) (pathname)
67#endif
68
24906b43
RM
69/* @@ end of prolog @@ */
70
71#ifdef _LIBC
72/* Rename the non ANSI C functions. This is required by the standard
73 because some ANSI C functions will require linking with this object
74 file and the name space must not be polluted. */
5f078c32 75# define strcasecmp(s1, s2) __strcasecmp_l (s1, s2, _nl_C_locobj_ptr)
40a55d20 76
e7c5513d
UD
77# ifndef mempcpy
78# define mempcpy __mempcpy
79# endif
1618c590 80# define HAVE_MEMPCPY 1
2706ee38 81# define HAVE___FSETLOCKING 1
6d248857 82#endif
1618c590 83
6d248857
WN
84/* Handle multi-threaded applications. */
85#ifdef _LIBC
ec999b8e 86# include <libc-lock.h>
6d248857
WN
87#else
88# include "lock.h"
24906b43
RM
89#endif
90
2706ee38
UD
91/* Some optimizations for glibc. */
92#ifdef _LIBC
7fc03cf3 93# define FEOF(fp) __feof_unlocked (fp)
cc67478e 94# define FGETS(buf, n, fp) __fgets_unlocked (buf, n, fp)
2706ee38
UD
95#else
96# define FEOF(fp) feof (fp)
97# define FGETS(buf, n, fp) fgets (buf, n, fp)
98#endif
99
4a4d50f3 100/* For those losing systems which don't have `alloca' we have to add
5f2eab42
RM
101 some additional code emulating it. */
102#ifdef HAVE_ALLOCA
4a4d50f3 103# define freea(p) /* nothing */
5f2eab42 104#else
4a4d50f3
UD
105# define alloca(n) malloc (n)
106# define freea(p) free (p)
107#endif
5f2eab42 108
6d248857 109#if defined _LIBC_REENTRANT || defined HAVE_DECL_FGETS_UNLOCKED
77ccaba1
UD
110# undef fgets
111# define fgets(buf, len, s) fgets_unlocked (buf, len, s)
112#endif
6d248857 113#if defined _LIBC_REENTRANT || defined HAVE_DECL_FEOF_UNLOCKED
77ccaba1
UD
114# undef feof
115# define feof(s) feof_unlocked (s)
116#endif
117
5f2eab42 118
6d248857
WN
119__libc_lock_define_initialized (static, lock)
120
121
24906b43
RM
122struct alias_map
123{
124 const char *alias;
125 const char *value;
126};
127
128
88677348 129static char *string_space;
390500b1
UD
130static size_t string_space_act;
131static size_t string_space_max;
88677348 132static struct alias_map *map;
390500b1
UD
133static size_t nmap;
134static size_t maxmap;
24906b43
RM
135
136
137/* Prototypes for local functions. */
d7ccc6c9 138static size_t read_alias_file (const char *fname, int fname_len);
6d248857
WN
139static int extend_alias_table (void);
140static int alias_compare (const struct alias_map *map1,
141 const struct alias_map *map2);
24906b43
RM
142
143
144const char *
6d248857 145_nl_expand_alias (const char *name)
24906b43 146{
6d248857 147 static const char *locale_alias_path;
24906b43 148 struct alias_map *retval;
40a55d20 149 const char *result = NULL;
24906b43
RM
150 size_t added;
151
40a55d20 152 __libc_lock_lock (lock);
6d248857
WN
153
154 if (locale_alias_path == NULL)
155 locale_alias_path = LOCALE_ALIAS_PATH;
40a55d20 156
24906b43
RM
157 do
158 {
159 struct alias_map item;
160
161 item.alias = name;
162
163 if (nmap > 0)
164 retval = (struct alias_map *) bsearch (&item, map, nmap,
165 sizeof (struct alias_map),
6d248857
WN
166 (int (*) (const void *,
167 const void *)
be10a868 168 ) alias_compare);
24906b43
RM
169 else
170 retval = NULL;
171
172 /* We really found an alias. Return the value. */
173 if (retval != NULL)
40a55d20
UD
174 {
175 result = retval->value;
176 break;
177 }
24906b43
RM
178
179 /* Perhaps we can find another alias file. */
180 added = 0;
181 while (added == 0 && locale_alias_path[0] != '\0')
182 {
183 const char *start;
184
6d248857 185 while (locale_alias_path[0] == PATH_SEPARATOR)
24906b43
RM
186 ++locale_alias_path;
187 start = locale_alias_path;
188
6d248857
WN
189 while (locale_alias_path[0] != '\0'
190 && locale_alias_path[0] != PATH_SEPARATOR)
24906b43
RM
191 ++locale_alias_path;
192
193 if (start < locale_alias_path)
194 added = read_alias_file (start, locale_alias_path - start);
195 }
196 }
197 while (added != 0);
198
40a55d20 199 __libc_lock_unlock (lock);
40a55d20
UD
200
201 return result;
24906b43
RM
202}
203
204
205static size_t
6d248857 206read_alias_file (const char *fname, int fname_len)
24906b43
RM
207{
208 FILE *fp;
209 char *full_fname;
210 size_t added;
86d2c878 211 static const char aliasfile[] = "/locale.alias";
24906b43 212
86d2c878 213 full_fname = (char *) alloca (fname_len + sizeof aliasfile);
1618c590
UD
214#ifdef HAVE_MEMPCPY
215 mempcpy (mempcpy (full_fname, fname, fname_len),
216 aliasfile, sizeof aliasfile);
217#else
86d2c878
RM
218 memcpy (full_fname, fname, fname_len);
219 memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
1618c590 220#endif
24906b43 221
6d248857 222#ifdef _LIBC
ee8449f7
UD
223 /* Note the file is opened with cancellation in the I/O functions
224 disabled. */
6d248857
WN
225 fp = fopen (relocate (full_fname), "rce");
226#else
227 fp = fopen (relocate (full_fname), "r");
228#endif
4a4d50f3 229 freea (full_fname);
24906b43 230 if (fp == NULL)
4a4d50f3 231 return 0;
24906b43 232
2706ee38
UD
233#ifdef HAVE___FSETLOCKING
234 /* No threads present. */
235 __fsetlocking (fp, FSETLOCKING_BYCALLER);
236#endif
237
24906b43 238 added = 0;
2706ee38 239 while (!FEOF (fp))
24906b43
RM
240 {
241 /* It is a reasonable approach to use a fix buffer here because
242 a) we are only interested in the first two fields
243 b) these fields must be usable as file names and so must not
244 be that long
51b3c8f6
UD
245 We avoid a multi-kilobyte buffer here since this would use up
246 stack space which we might not have if the program ran out of
247 memory. */
248 char buf[400];
8cb569b7
UD
249 char *alias;
250 char *value;
251 char *cp;
fae49c62 252 int complete_line;
24906b43 253
2706ee38 254 if (FGETS (buf, sizeof buf, fp) == NULL)
24906b43
RM
255 /* EOF reached. */
256 break;
257
fae49c62
UD
258 /* Determine whether the line is complete. */
259 complete_line = strchr (buf, '\n') != NULL;
260
24906b43
RM
261 cp = buf;
262 /* Ignore leading white space. */
0555fcce 263 while (isspace ((unsigned char) cp[0]))
24906b43
RM
264 ++cp;
265
266 /* A leading '#' signals a comment line. */
267 if (cp[0] != '\0' && cp[0] != '#')
268 {
269 alias = cp++;
0555fcce 270 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
24906b43
RM
271 ++cp;
272 /* Terminate alias name. */
273 if (cp[0] != '\0')
274 *cp++ = '\0';
275
276 /* Now look for the beginning of the value. */
0555fcce 277 while (isspace ((unsigned char) cp[0]))
24906b43
RM
278 ++cp;
279
280 if (cp[0] != '\0')
281 {
24906b43 282 value = cp++;
0555fcce 283 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
24906b43
RM
284 ++cp;
285 /* Terminate value. */
286 if (cp[0] == '\n')
287 {
288 /* This has to be done to make the following test
289 for the end of line possible. We are looking for
290 the terminating '\n' which do not overwrite here. */
291 *cp++ = '\0';
292 *cp = '\n';
293 }
294 else if (cp[0] != '\0')
295 *cp++ = '\0';
296
6d248857
WN
297#ifdef IN_LIBGLOCALE
298 /* glibc's locale.alias contains entries for ja_JP and ko_KR
299 that make it impossible to use a Japanese or Korean UTF-8
300 locale under the name "ja_JP" or "ko_KR". Ignore these
301 entries. */
302 if (strchr (alias, '_') == NULL)
303#endif
304 {
305 size_t alias_len;
306 size_t value_len;
24906b43 307
6d248857
WN
308 if (nmap >= maxmap)
309 if (__builtin_expect (extend_alias_table (), 0))
310 goto out;
24906b43 311
6d248857
WN
312 alias_len = strlen (alias) + 1;
313 value_len = strlen (value) + 1;
42be70d4 314
6d248857
WN
315 if (string_space_act + alias_len + value_len > string_space_max)
316 {
7845064d
MS
317#pragma GCC diagnostic push
318
319#if defined __GNUC__ && __GNUC__ >= 12
320 /* Suppress the valid GCC 12 warning until the code below is changed
321 to avoid using pointers to the reallocated block. */
322# pragma GCC diagnostic ignored "-Wuse-after-free"
323#endif
324
325 /* Increase size of memory pool. */
6d248857
WN
326 size_t new_size = (string_space_max
327 + (alias_len + value_len > 1024
328 ? alias_len + value_len : 1024));
329 char *new_pool = (char *) realloc (string_space, new_size);
330 if (new_pool == NULL)
331 goto out;
332
333 if (__builtin_expect (string_space != new_pool, 0))
42be70d4 334 {
6d248857
WN
335 size_t i;
336
337 for (i = 0; i < nmap; i++)
338 {
339 map[i].alias += new_pool - string_space;
340 map[i].value += new_pool - string_space;
341 }
42be70d4 342 }
42be70d4 343
6d248857
WN
344 string_space = new_pool;
345 string_space_max = new_size;
346 }
a5a0310d 347
6d248857
WN
348 map[nmap].alias =
349 (const char *) memcpy (&string_space[string_space_act],
350 alias, alias_len);
351 string_space_act += alias_len;
a5a0310d 352
6d248857
WN
353 map[nmap].value =
354 (const char *) memcpy (&string_space[string_space_act],
355 value, value_len);
356 string_space_act += value_len;
24906b43 357
7845064d
MS
358#pragma GCC diagnostic pop
359
6d248857
WN
360 ++nmap;
361 ++added;
362 }
24906b43
RM
363 }
364 }
51b3c8f6
UD
365
366 /* Possibly not the whole line fits into the buffer. Ignore
367 the rest of the line. */
fae49c62
UD
368 if (! complete_line)
369 do
370 if (FGETS (buf, sizeof buf, fp) == NULL)
371 /* Make sure the inner loop will be left. The outer loop
372 will exit at the `feof' test. */
373 break;
f6c93bd9 374 while (strchr (buf, '\n') == NULL);
24906b43
RM
375 }
376
6d248857 377 out:
24906b43
RM
378 /* Should we test for ferror()? I think we have to silently ignore
379 errors. --drepper */
380 fclose (fp);
381
382 if (added > 0)
383 qsort (map, nmap, sizeof (struct alias_map),
6d248857 384 (int (*) (const void *, const void *)) alias_compare);
24906b43
RM
385
386 return added;
387}
388
389
17c389fc 390static int
60d2f8f3 391extend_alias_table (void)
24906b43
RM
392{
393 size_t new_size;
394 struct alias_map *new_map;
395
396 new_size = maxmap == 0 ? 100 : 2 * maxmap;
a5a0310d
UD
397 new_map = (struct alias_map *) realloc (map, (new_size
398 * sizeof (struct alias_map)));
24906b43
RM
399 if (new_map == NULL)
400 /* Simply don't extend: we don't have any more core. */
17c389fc 401 return -1;
24906b43 402
24906b43
RM
403 map = new_map;
404 maxmap = new_size;
17c389fc 405 return 0;
24906b43
RM
406}
407
408
409static int
6d248857 410alias_compare (const struct alias_map *map1, const struct alias_map *map2)
24906b43
RM
411{
412#if defined _LIBC || defined HAVE_STRCASECMP
413 return strcasecmp (map1->alias, map2->alias);
414#else
415 const unsigned char *p1 = (const unsigned char *) map1->alias;
416 const unsigned char *p2 = (const unsigned char *) map2->alias;
417 unsigned char c1, c2;
418
419 if (p1 == p2)
420 return 0;
421
422 do
423 {
424 /* I know this seems to be odd but the tolower() function in
425 some systems libc cannot handle nonalpha characters. */
75914335
RM
426 c1 = isupper (*p1) ? tolower (*p1) : *p1;
427 c2 = isupper (*p2) ? tolower (*p2) : *p2;
24906b43
RM
428 if (c1 == '\0')
429 break;
be10a868
RM
430 ++p1;
431 ++p2;
24906b43
RM
432 }
433 while (c1 == c2);
434
435 return c1 - c2;
436#endif
437}
88677348
AZN
438
439void
440__libc_localealias_freemem (void)
441{
442 free (string_space);
443 free (map);
444}
This page took 0.724279 seconds and 5 git commands to generate.