write_archive_locales: Fix memory leak
[platform/upstream/glibc.git] / locale / loadlocale.c
1 /* Functions to read locale data files.
2    Copyright (C) 1996-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <locale.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #ifdef _POSIX_MAPPED_FILES
28 # include <sys/mman.h>
29 #endif
30 #include <sys/stat.h>
31
32 #include <not-cancel.h>
33 #include "localeinfo.h"
34
35
36 static const size_t _nl_category_num_items[] =
37 {
38 #define DEFINE_CATEGORY(category, category_name, items, a) \
39   [category] = _NL_ITEM_INDEX (_NL_NUM_##category),
40 #include "categories.def"
41 #undef  DEFINE_CATEGORY
42 };
43
44
45 #define NO_PAREN(arg, rest...) arg, ##rest
46
47 /* The size of the array must be specified explicitly because some of
48    the 'items' may be subarrays, which will cause the compiler to deduce
49    an incorrect size from the initializer.  */
50 #define DEFINE_CATEGORY(category, category_name, items, a) \
51 static const enum value_type _nl_value_type_##category     \
52   [_NL_ITEM_INDEX (_NL_NUM_##category)] = { NO_PAREN items };
53 #define DEFINE_ELEMENT(element, element_name, optstd, type, rest...) \
54   [_NL_ITEM_INDEX (element)] = type,
55 #include "categories.def"
56 #undef DEFINE_CATEGORY
57
58 static const enum value_type *const _nl_value_types[] =
59 {
60 #define DEFINE_CATEGORY(category, category_name, items, a) \
61   [category] = _nl_value_type_##category,
62 #include "categories.def"
63 #undef DEFINE_CATEGORY
64 };
65
66
67 struct __locale_data *
68 _nl_intern_locale_data (int category, const void *data, size_t datasize)
69 {
70   const struct
71     {
72       unsigned int magic;
73       unsigned int nstrings;
74       unsigned int strindex[0];
75     } *const filedata = data;
76   struct __locale_data *newdata;
77   size_t cnt;
78
79   if (__builtin_expect (datasize < sizeof *filedata, 0)
80       || __builtin_expect (filedata->magic != LIMAGIC (category), 0))
81     {
82       /* Bad data file.  */
83       __set_errno (EINVAL);
84       return NULL;
85     }
86
87   if (__builtin_expect (filedata->nstrings < _nl_category_num_items[category],
88                         0)
89       || (__builtin_expect (sizeof *filedata
90                             + filedata->nstrings * sizeof (unsigned int)
91                             >= datasize, 0)))
92     {
93       /* Insufficient data.  */
94       __set_errno (EINVAL);
95       return NULL;
96     }
97
98   newdata = malloc (sizeof *newdata
99                     + filedata->nstrings * sizeof (union locale_data_value));
100   if (newdata == NULL)
101     return NULL;
102
103   newdata->filedata = (void *) filedata;
104   newdata->filesize = datasize;
105   newdata->private.data = NULL;
106   newdata->private.cleanup = NULL;
107   newdata->usage_count = 0;
108   newdata->use_translit = 0;
109   newdata->nstrings = filedata->nstrings;
110   for (cnt = 0; cnt < newdata->nstrings; ++cnt)
111     {
112       size_t idx = filedata->strindex[cnt];
113       if (__glibc_unlikely (idx > (size_t) newdata->filesize))
114         {
115         puntdata:
116           free (newdata);
117           __set_errno (EINVAL);
118           return NULL;
119         }
120
121       /* Determine the type.  There is one special case: the LC_CTYPE
122          category can have more elements than there are in the
123          _nl_value_type_LC_XYZ array.  There are all pointers.  */
124       switch (category)
125         {
126 #define CATTEST(cat) \
127         case LC_##cat:                                          \
128           if (cnt >= (sizeof (_nl_value_type_LC_##cat)          \
129                       / sizeof (_nl_value_type_LC_##cat[0])))   \
130             goto puntdata;                                      \
131           break
132           CATTEST (NUMERIC);
133           CATTEST (TIME);
134           CATTEST (COLLATE);
135           CATTEST (MONETARY);
136           CATTEST (MESSAGES);
137           CATTEST (PAPER);
138           CATTEST (NAME);
139           CATTEST (ADDRESS);
140           CATTEST (TELEPHONE);
141           CATTEST (MEASUREMENT);
142           CATTEST (IDENTIFICATION);
143         default:
144           assert (category == LC_CTYPE);
145           break;
146         }
147
148       if ((category == LC_CTYPE
149            && cnt >= (sizeof (_nl_value_type_LC_CTYPE)
150                       / sizeof (_nl_value_type_LC_CTYPE[0])))
151           || __builtin_expect (_nl_value_types[category][cnt] != word, 1))
152         newdata->values[cnt].string = newdata->filedata + idx;
153       else
154         {
155           if (!LOCFILE_ALIGNED_P (idx))
156             goto puntdata;
157           newdata->values[cnt].word =
158             *((const uint32_t *) (newdata->filedata + idx));
159         }
160     }
161
162   return newdata;
163 }
164
165 void
166 _nl_load_locale (struct loaded_l10nfile *file, int category)
167 {
168   int fd;
169   void *filedata;
170   struct stat64 st;
171   struct __locale_data *newdata;
172   int save_err;
173   int alloc = ld_mapped;
174
175   file->decided = 1;
176   file->data = NULL;
177
178   fd = __open_nocancel (file->filename, O_RDONLY | O_CLOEXEC);
179   if (__builtin_expect (fd, 0) < 0)
180     /* Cannot open the file.  */
181     return;
182
183   if (__builtin_expect (__fstat64 (fd, &st), 0) < 0)
184     {
185     puntfd:
186       __close_nocancel_nostatus (fd);
187       return;
188     }
189   if (__glibc_unlikely (S_ISDIR (st.st_mode)))
190     {
191       /* LOCALE/LC_foo is a directory; open LOCALE/LC_foo/SYS_LC_foo
192            instead.  */
193       char *newp;
194       size_t filenamelen;
195
196       __close_nocancel_nostatus (fd);
197
198       filenamelen = strlen (file->filename);
199       newp = (char *) alloca (filenamelen
200                               + 5 + _nl_category_name_sizes[category] + 1);
201       __mempcpy (__mempcpy (__mempcpy (newp, file->filename, filenamelen),
202                             "/SYS_", 5), _nl_category_names_get (category),
203                  _nl_category_name_sizes[category] + 1);
204
205       fd = __open_nocancel (newp, O_RDONLY | O_CLOEXEC);
206       if (__builtin_expect (fd, 0) < 0)
207         return;
208
209       if (__builtin_expect (__fstat64 (fd, &st), 0) < 0)
210         goto puntfd;
211     }
212
213   /* Map in the file's data.  */
214   save_err = errno;
215 #ifdef _POSIX_MAPPED_FILES
216 # ifndef MAP_COPY
217   /* Linux seems to lack read-only copy-on-write.  */
218 #  define MAP_COPY MAP_PRIVATE
219 # endif
220 # ifndef MAP_FILE
221   /* Some systems do not have this flag; it is superfluous.  */
222 #  define MAP_FILE 0
223 # endif
224   filedata = __mmap ((caddr_t) 0, st.st_size,
225                      PROT_READ, MAP_FILE|MAP_COPY, fd, 0);
226   if (__glibc_unlikely (filedata == MAP_FAILED))
227     {
228       filedata = NULL;
229       if (__builtin_expect (errno, ENOSYS) == ENOSYS)
230         {
231 #endif  /* _POSIX_MAPPED_FILES */
232           /* No mmap; allocate a buffer and read from the file.  */
233           alloc = ld_malloced;
234           filedata = malloc (st.st_size);
235           if (filedata != NULL)
236             {
237               off_t to_read = st.st_size;
238               ssize_t nread;
239               char *p = (char *) filedata;
240               while (to_read > 0)
241                 {
242                   nread = __read_nocancel (fd, p, to_read);
243                   if (__builtin_expect (nread, 1) <= 0)
244                     {
245                       free (filedata);
246                       if (nread == 0)
247                         __set_errno (EINVAL); /* Bizarreness going on.  */
248                       goto puntfd;
249                     }
250                   p += nread;
251                   to_read -= nread;
252                 }
253               __set_errno (save_err);
254             }
255 #ifdef _POSIX_MAPPED_FILES
256         }
257     }
258 #endif  /* _POSIX_MAPPED_FILES */
259
260   /* We have mapped the data, so we no longer need the descriptor.  */
261   __close_nocancel_nostatus (fd);
262
263   if (__glibc_unlikely (filedata == NULL))
264     /* We failed to map or read the data.  */
265     return;
266
267   newdata = _nl_intern_locale_data (category, filedata, st.st_size);
268   if (__glibc_unlikely (newdata == NULL))
269     /* Bad data.  */
270     {
271 #ifdef _POSIX_MAPPED_FILES
272       if (alloc == ld_mapped)
273         __munmap ((caddr_t) filedata, st.st_size);
274 #endif
275       return;
276     }
277
278   /* _nl_intern_locale_data leaves us these fields to initialize.  */
279   newdata->name = NULL; /* This will be filled if necessary in findlocale.c. */
280   newdata->alloc = alloc;
281
282   file->data = newdata;
283 }
284
285 void
286 _nl_unload_locale (struct __locale_data *locale)
287 {
288   if (locale->private.cleanup)
289     (*locale->private.cleanup) (locale);
290
291   switch (__builtin_expect (locale->alloc, ld_mapped))
292     {
293     case ld_malloced:
294       free ((void *) locale->filedata);
295       break;
296     case ld_mapped:
297 #ifdef _POSIX_MAPPED_FILES
298       __munmap ((caddr_t) locale->filedata, locale->filesize);
299       break;
300 #endif
301     case ld_archive:            /* Nothing to do.  */
302       break;
303     }
304
305   if (__builtin_expect (locale->alloc, ld_mapped) != ld_archive)
306     free ((char *) locale->name);
307
308   free (locale);
309 }