Update copyright dates with scripts/update-copyrights.
[platform/upstream/glibc.git] / elf / dl-exception.c
1 /* ld.so error exception allocation and deallocation.
2    Copyright (C) 1995-2020 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)