1 | /* ld.so error exception allocation and deallocation. |
2 | Copyright (C) 1995-2021 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library 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 GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <ldsodefs.h> |
20 | #include <limits.h> |
21 | #include <stdarg.h> |
22 | #include <stdbool.h> |
23 | #include <stdint.h> |
24 | #include <string.h> |
25 | #include <unistd.h> |
26 | #include <_itoa.h> |
27 | |
28 | /* This message we return as a last resort. We define the string in a |
29 | variable since we have to avoid freeing it and so have to enable |
30 | a pointer comparison. See below and in dlfcn/dlerror.c. */ |
31 | static const char _dl_out_of_memory[] = "out of memory" ; |
32 | |
33 | /* Dummy allocation object used if allocating the message buffer |
34 | fails. */ |
35 | static void |
36 | oom_exception (struct dl_exception *exception) |
37 | { |
38 | exception->objname = "" ; |
39 | exception->errstring = _dl_out_of_memory; |
40 | exception->message_buffer = NULL; |
41 | } |
42 | |
43 | static void |
44 | __attribute__ ((noreturn)) |
45 | length_mismatch (void) |
46 | { |
47 | _dl_fatal_printf ("Fatal error: " |
48 | "length accounting in _dl_exception_create_format\n" ); |
49 | } |
50 | |
51 | /* Adjust the message buffer to indicate whether it is possible to |
52 | free it. EXCEPTION->errstring must be a potentially deallocatable |
53 | pointer. */ |
54 | static void |
55 | adjust_message_buffer (struct dl_exception *exception) |
56 | { |
57 | /* If the main executable is relocated it means the libc's malloc |
58 | is used. */ |
59 | bool malloced = true; |
60 | #ifdef SHARED |
61 | malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL |
62 | && (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated != 0)); |
63 | #endif |
64 | if (malloced) |
65 | exception->message_buffer = (char *) exception->errstring; |
66 | else |
67 | exception->message_buffer = NULL; |
68 | } |
69 | |
70 | void |
71 | _dl_exception_create (struct dl_exception *exception, const char *objname, |
72 | const char *errstring) |
73 | { |
74 | if (objname == NULL) |
75 | objname = "" ; |
76 | size_t len_objname = strlen (objname) + 1; |
77 | size_t len_errstring = strlen (errstring) + 1; |
78 | char *errstring_copy = malloc (len_objname + len_errstring); |
79 | if (errstring_copy != NULL) |
80 | { |
81 | /* Make a copy of the object file name and the error string. */ |
82 | exception->objname = memcpy (__mempcpy (errstring_copy, |
83 | errstring, len_errstring), |
84 | objname, len_objname); |
85 | exception->errstring = errstring_copy; |
86 | adjust_message_buffer (exception); |
87 | } |
88 | else |
89 | oom_exception (exception); |
90 | } |
91 | rtld_hidden_def (_dl_exception_create) |
92 | |
93 | void |
94 | _dl_exception_create_format (struct dl_exception *exception, const char *objname, |
95 | const char *fmt, ...) |
96 | { |
97 | if (objname == NULL) |
98 | objname = "" ; |
99 | size_t len_objname = strlen (objname) + 1; |
100 | /* Compute the length of the result. Include room for two NUL |
101 | bytes. */ |
102 | size_t length = len_objname + 1; |
103 | { |
104 | va_list ap; |
105 | va_start (ap, fmt); |
106 | for (const char *p = fmt; *p != '\0'; ++p) |
107 | if (*p == '%') |
108 | { |
109 | ++p; |
110 | switch (*p) |
111 | { |
112 | case 's': |
113 | length += strlen (va_arg (ap, const char *)); |
114 | break; |
115 | /* Recognize the l modifier. It is only important on some |
116 | platforms where long and int have a different size. We |
117 | can use the same code for size_t. */ |
118 | case 'l': |
119 | case 'z': |
120 | if (p[1] == 'x') |
121 | { |
122 | length += LONG_WIDTH / 4; |
123 | ++p; |
124 | break; |
125 | } |
126 | /* Fall through. */ |
127 | case 'x': |
128 | length += INT_WIDTH / 4; |
129 | break; |
130 | default: |
131 | /* Assumed to be '%'. */ |
132 | ++length; |
133 | break; |
134 | } |
135 | } |
136 | else |
137 | ++length; |
138 | va_end (ap); |
139 | } |
140 | |
141 | if (length > PTRDIFF_MAX) |
142 | { |
143 | oom_exception (exception); |
144 | return; |
145 | } |
146 | char *errstring = malloc (length); |
147 | if (errstring == NULL) |
148 | { |
149 | oom_exception (exception); |
150 | return; |
151 | } |
152 | exception->errstring = errstring; |
153 | adjust_message_buffer (exception); |
154 | |
155 | /* Copy the error message to errstring. */ |
156 | { |
157 | /* Next byte to be written in errstring. */ |
158 | char *wptr = errstring; |
159 | /* End of the allocated string. */ |
160 | char *const end = errstring + length; |
161 | |
162 | va_list ap; |
163 | va_start (ap, fmt); |
164 | |
165 | for (const char *p = fmt; *p != '\0'; ++p) |
166 | if (*p == '%') |
167 | { |
168 | ++p; |
169 | switch (*p) |
170 | { |
171 | case 's': |
172 | { |
173 | const char *ptr = va_arg (ap, const char *); |
174 | size_t len_ptr = strlen (ptr); |
175 | if (len_ptr > end - wptr) |
176 | length_mismatch (); |
177 | wptr = __mempcpy (wptr, ptr, len_ptr); |
178 | } |
179 | break; |
180 | case '%': |
181 | if (wptr == end) |
182 | length_mismatch (); |
183 | *wptr = '%'; |
184 | ++wptr; |
185 | break; |
186 | case 'x': |
187 | { |
188 | unsigned long int num = va_arg (ap, unsigned int); |
189 | char *start = wptr; |
190 | wptr += INT_WIDTH / 4; |
191 | char *cp = _itoa (num, wptr, 16, 0); |
192 | /* Pad to the full width with 0. */ |
193 | while (cp != start) |
194 | *--cp = '0'; |
195 | } |
196 | break; |
197 | case 'l': |
198 | case 'z': |
199 | if (p[1] == 'x') |
200 | { |
201 | unsigned long int num = va_arg (ap, unsigned long int); |
202 | char *start = wptr; |
203 | wptr += LONG_WIDTH / 4; |
204 | char *cp = _itoa (num, wptr, 16, 0); |
205 | /* Pad to the full width with 0. */ |
206 | while (cp != start) |
207 | *--cp = '0'; |
208 | ++p; |
209 | break; |
210 | } |
211 | /* FALLTHROUGH */ |
212 | default: |
213 | _dl_fatal_printf ("Fatal error:" |
214 | " invalid format in exception string\n" ); |
215 | } |
216 | } |
217 | else |
218 | { |
219 | if (wptr == end) |
220 | length_mismatch (); |
221 | *wptr = *p; |
222 | ++wptr; |
223 | } |
224 | |
225 | if (wptr == end) |
226 | length_mismatch (); |
227 | *wptr = '\0'; |
228 | ++wptr; |
229 | if (len_objname != end - wptr) |
230 | length_mismatch (); |
231 | exception->objname = memcpy (wptr, objname, len_objname); |
232 | } |
233 | } |
234 | rtld_hidden_def (_dl_exception_create_format) |
235 | |
236 | void |
237 | _dl_exception_free (struct dl_exception *exception) |
238 | { |
239 | free (exception->message_buffer); |
240 | exception->objname = NULL; |
241 | exception->errstring = NULL; |
242 | exception->message_buffer = NULL; |
243 | } |
244 | rtld_hidden_def (_dl_exception_free) |
245 | |