File: | lib/gl/time_rz.c |
Warning: | line 277, column 37 Potential leak of memory pointed to by 'old_tz' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* Time zone functions such as tzalloc and localtime_rz | |||
2 | ||||
3 | Copyright 2015-2022 Free Software Foundation, Inc. | |||
4 | ||||
5 | This file is free software: you can redistribute it and/or modify | |||
6 | it under the terms of the GNU Lesser General Public License as | |||
7 | published by the Free Software Foundation, either version 3 of the | |||
8 | License, or (at your option) any later version. | |||
9 | ||||
10 | This file is distributed in the hope that it will be useful, | |||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
13 | GNU Lesser General Public License for more details. | |||
14 | ||||
15 | You should have received a copy of the GNU Lesser General Public License | |||
16 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |||
17 | ||||
18 | /* Written by Paul Eggert. */ | |||
19 | ||||
20 | /* Although this module is not thread-safe, any races should be fairly | |||
21 | rare and reasonably benign. For complete thread-safety, use a C | |||
22 | library with a working timezone_t type, so that this module is not | |||
23 | needed. */ | |||
24 | ||||
25 | #include <config.h> | |||
26 | ||||
27 | #include <time.h> | |||
28 | ||||
29 | #include <errno(*__errno_location ()).h> | |||
30 | #include <stdbool.h> | |||
31 | #include <stddef.h> | |||
32 | #include <stdlib.h> | |||
33 | #include <string.h> | |||
34 | ||||
35 | #include "flexmember.h" | |||
36 | #include "idx.h" | |||
37 | #include "time-internal.h" | |||
38 | ||||
39 | /* The approximate size to use for small allocation requests. This is | |||
40 | the largest "small" request for the GNU C library malloc. */ | |||
41 | enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 }; | |||
42 | ||||
43 | /* Minimum size of the ABBRS member of struct tm_zone. ABBRS is larger | |||
44 | only in the unlikely case where an abbreviation longer than this is | |||
45 | used. */ | |||
46 | enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs)__builtin_offsetof(struct tm_zone, abbrs) }; | |||
47 | ||||
48 | /* Magic cookie timezone_t value, for local time. It differs from | |||
49 | NULL and from all other timezone_t values. Only the address | |||
50 | matters; the pointer is never dereferenced. */ | |||
51 | static timezone_t const local_tz = (timezone_t) 1; | |||
52 | ||||
53 | /* Copy to ABBRS the abbreviation at ABBR with size ABBR_SIZE (this | |||
54 | includes its trailing null byte). Append an extra null byte to | |||
55 | mark the end of ABBRS. */ | |||
56 | static void | |||
57 | extend_abbrs (char *abbrs, char const *abbr, size_t abbr_size) | |||
58 | { | |||
59 | memcpy (abbrs, abbr, abbr_size); | |||
60 | abbrs[abbr_size] = '\0'; | |||
61 | } | |||
62 | ||||
63 | /* Return a newly allocated time zone for NAME, or NULL on failure. | |||
64 | A null NAME stands for wall clock time (which is like unset TZ). */ | |||
65 | timezone_t | |||
66 | tzalloc (char const *name) | |||
67 | { | |||
68 | size_t name_size = name
| |||
69 | size_t abbr_size = name_size
| |||
70 | timezone_t tz = malloc (FLEXSIZEOF (struct tm_zone, abbrs, abbr_size)((__builtin_offsetof(struct tm_zone, abbrs) + _Alignof (struct tm_zone) - 1 + (abbr_size)) & ~ (_Alignof (struct tm_zone ) - 1))); | |||
71 | if (tz) | |||
72 | { | |||
73 | tz->next = NULL((void*)0); | |||
74 | #if HAVE_TZNAME && !HAVE_STRUCT_TM_TM_ZONE1 | |||
75 | tz->tzname_copy[0] = tz->tzname_copy[1] = NULL((void*)0); | |||
76 | #endif | |||
77 | tz->tz_is_set = !!name; | |||
78 | tz->abbrs[0] = '\0'; | |||
79 | if (name
| |||
80 | extend_abbrs (tz->abbrs, name, name_size); | |||
81 | } | |||
82 | return tz; | |||
83 | } | |||
84 | ||||
85 | /* Save into TZ any nontrivial time zone abbreviation used by TM, and | |||
86 | update *TM (if HAVE_STRUCT_TM_TM_ZONE) or *TZ (if | |||
87 | !HAVE_STRUCT_TM_TM_ZONE && HAVE_TZNAME) if they use the abbreviation. | |||
88 | Return true if successful, false (setting errno) otherwise. */ | |||
89 | static bool_Bool | |||
90 | save_abbr (timezone_t tz, struct tm *tm) | |||
91 | { | |||
92 | #if HAVE_STRUCT_TM_TM_ZONE1 || HAVE_TZNAME | |||
93 | char const *zone = NULL((void*)0); | |||
94 | char *zone_copy = (char *) ""; | |||
95 | ||||
96 | # if HAVE_TZNAME | |||
97 | int tzname_index = -1; | |||
98 | # endif | |||
99 | ||||
100 | # if HAVE_STRUCT_TM_TM_ZONE1 | |||
101 | zone = tm->tm_zone; | |||
102 | # endif | |||
103 | ||||
104 | # if HAVE_TZNAME | |||
105 | if (! (zone && *zone) && 0 <= tm->tm_isdst) | |||
106 | { | |||
107 | tzname_index = tm->tm_isdst != 0; | |||
108 | zone = tzname[tzname_index]; | |||
109 | } | |||
110 | # endif | |||
111 | ||||
112 | /* No need to replace null zones, or zones within the struct tm. */ | |||
113 | if (!zone || ((char *) tm <= zone && zone < (char *) (tm + 1))) | |||
114 | return true1; | |||
115 | ||||
116 | if (*zone) | |||
117 | { | |||
118 | zone_copy = tz->abbrs; | |||
119 | ||||
120 | while (strcmp (zone_copy, zone) != 0) | |||
121 | { | |||
122 | if (! (*zone_copy || (zone_copy == tz->abbrs && tz->tz_is_set))) | |||
123 | { | |||
124 | idx_t zone_size = strlen (zone) + 1; | |||
125 | if (zone_size < tz->abbrs + ABBR_SIZE_MIN - zone_copy) | |||
126 | extend_abbrs (zone_copy, zone, zone_size); | |||
127 | else | |||
128 | { | |||
129 | tz = tz->next = tzalloc (zone); | |||
130 | if (!tz) | |||
131 | return false0; | |||
132 | tz->tz_is_set = 0; | |||
133 | zone_copy = tz->abbrs; | |||
134 | } | |||
135 | break; | |||
136 | } | |||
137 | ||||
138 | zone_copy += strlen (zone_copy) + 1; | |||
139 | if (!*zone_copy && tz->next) | |||
140 | { | |||
141 | tz = tz->next; | |||
142 | zone_copy = tz->abbrs; | |||
143 | } | |||
144 | } | |||
145 | } | |||
146 | ||||
147 | /* Replace the zone name so that its lifetime matches that of TZ. */ | |||
148 | # if HAVE_STRUCT_TM_TM_ZONE1 | |||
149 | tm->tm_zone = zone_copy; | |||
150 | # else | |||
151 | if (0 <= tzname_index) | |||
152 | tz->tzname_copy[tzname_index] = zone_copy; | |||
153 | # endif | |||
154 | #endif | |||
155 | ||||
156 | return true1; | |||
157 | } | |||
158 | ||||
159 | /* Free a time zone. */ | |||
160 | void | |||
161 | tzfree (timezone_t tz) | |||
162 | { | |||
163 | if (tz != local_tz) | |||
164 | while (tz) | |||
165 | { | |||
166 | timezone_t next = tz->next; | |||
167 | free (tz); | |||
168 | tz = next; | |||
169 | } | |||
170 | } | |||
171 | ||||
172 | /* Get and set the TZ environment variable. These functions can be | |||
173 | overridden by programs like Emacs that manage their own environment. */ | |||
174 | ||||
175 | #ifndef getenv_TZ | |||
176 | static char * | |||
177 | getenv_TZ (void) | |||
178 | { | |||
179 | return getenv ("TZ"); | |||
180 | } | |||
181 | #endif | |||
182 | ||||
183 | #ifndef setenv_TZ | |||
184 | static int | |||
185 | setenv_TZ (char const *tz) | |||
186 | { | |||
187 | return tz ? setenv ("TZ", tz, 1) : unsetenv ("TZ"); | |||
188 | } | |||
189 | #endif | |||
190 | ||||
191 | /* Change the environment to match the specified timezone_t value. | |||
192 | Return true if successful, false (setting errno) otherwise. */ | |||
193 | static bool_Bool | |||
194 | change_env (timezone_t tz) | |||
195 | { | |||
196 | if (setenv_TZ (tz->tz_is_set ? tz->abbrs : NULL((void*)0)) != 0) | |||
197 | return false0; | |||
198 | tzset (); | |||
199 | return true1; | |||
200 | } | |||
201 | ||||
202 | /* Temporarily set the time zone to TZ, which must not be null. | |||
203 | Return LOCAL_TZ if the time zone setting is already correct. | |||
204 | Otherwise return a newly allocated time zone representing the old | |||
205 | setting, or NULL (setting errno) on failure. */ | |||
206 | static timezone_t | |||
207 | set_tz (timezone_t tz) | |||
208 | { | |||
209 | char *env_tz = getenv_TZ (); | |||
210 | if (env_tz |
3.1 | 'env_tz' is null |
14.1 | 'old_tz' is non-null |
1 | Assuming 'tz' is non-null |
17.1 | 'old_tz' is non-null |
23 | Potential leak of memory pointed to by 'old_tz' |