ld.so: Introduce struct dl_exception
[platform/upstream/glibc.git] / elf / dl-exception.c
1 /* ld.so error exception allocation and deallocation.
2    Copyright (C) 1995-2017 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    <http://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
27 /* This message we return as a last resort.  We define the string in a
28    variable since we have to avoid freeing it and so have to enable
29    a pointer comparison.  See below and in dlfcn/dlerror.c.  */
30 static const char _dl_out_of_memory[] = "out of memory";
31
32 /* Dummy allocation object used if allocating the message buffer
33    fails.  */
34 static void
35 oom_exception (struct dl_exception *exception)
36 {
37   exception->objname = "";
38   exception->errstring = _dl_out_of_memory;
39   exception->message_buffer = NULL;
40 }
41
42 static void
43 __attribute__ ((noreturn))
44 length_mismatch (void)
45 {
46   _dl_fatal_printf ("Fatal error: "
47                     "length accounting in _dl_exception_create_format\n");
48 }
49
50 /* Adjust the message buffer to indicate whether it is possible to
51    free it.  EXCEPTION->errstring must be a potentially deallocatable
52    pointer.  */
53 static void
54 adjust_message_buffer (struct dl_exception *exception)
55 {
56   /* If the main executable is relocated it means the libc's malloc
57      is used.  */
58   bool malloced = true;
59 #ifdef SHARED
60   malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL
61               && (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated != 0));
62 #endif
63   if (malloced)
64     exception->message_buffer = (char *) exception->errstring;
65   else
66     exception->message_buffer = NULL;
67 }
68
69 void
70 _dl_exception_create (struct dl_exception *exception, const char *objname,
71                       const char *errstring)
72 {
73   if (objname == NULL)
74     objname = "";
75   size_t len_objname = strlen (objname) + 1;
76   size_t len_errstring = strlen (errstring) + 1;
77   char *errstring_copy = malloc (len_objname + len_errstring);
78   if (errstring_copy != NULL)
79     {
80       /* Make a copy of the object file name and the error string.  */
81       exception->objname = memcpy (__mempcpy (errstring_copy,
82                                               errstring, len_errstring),
83                                    objname, len_objname);
84       exception->errstring = errstring_copy;
85       adjust_message_buffer (exception);
86     }
87   else
88     oom_exception (exception);
89 }
90 rtld_hidden_def (_dl_exception_create)
91
92 void
93 _dl_exception_create_format (struct dl_exception *exception, const char *objname,
94                              const char *fmt, ...)
95 {
96   if (objname == NULL)
97     objname = "";
98   size_t len_objname = strlen (objname) + 1;
99   /* Compute the length of the result.  Include room for two NUL
100      bytes.  */
101   size_t length = len_objname + 1;
102   {
103     va_list ap;
104     va_start (ap, fmt);
105     for (const char *p = fmt; *p != '\0'; ++p)
106       if (*p == '%')
107         {
108           ++p;
109           switch (*p)
110             {
111             case 's':
112               length += strlen (va_arg (ap, const char *));
113               break;
114             default:
115               /* Assumed to be '%'.  */
116               ++length;
117               break;
118             }
119         }
120       else
121         ++length;
122     va_end (ap);
123   }
124
125   if (length > PTRDIFF_MAX)
126     {
127       oom_exception (exception);
128       return;
129     }
130   char *errstring = malloc (length);
131   if (errstring == NULL)
132     {
133       oom_exception (exception);
134       return;
135     }
136   exception->errstring = errstring;
137   adjust_message_buffer (exception);
138
139   /* Copy the error message to errstring.  */
140   {
141     /* Next byte to be written in errstring.  */
142     char *wptr = errstring;
143     /* End of the allocated string.  */
144     char *const end = errstring + length;
145
146     va_list ap;
147     va_start (ap, fmt);
148
149     for (const char *p = fmt; *p != '\0'; ++p)
150       if (*p == '%')
151         {
152           ++p;
153           switch (*p)
154             {
155             case 's':
156               {
157                 const char *ptr = va_arg (ap, const char *);
158                 size_t len_ptr = strlen (ptr);
159                 if (len_ptr > end - wptr)
160                   length_mismatch ();
161                 wptr = __mempcpy (wptr, ptr, len_ptr);
162               }
163               break;
164             case '%':
165               if (wptr == end)
166                 length_mismatch ();
167               *wptr = '%';
168               ++wptr;
169               break;
170             default:
171               _dl_fatal_printf ("Fatal error:"
172                                 " invalid format in exception string\n");
173             }
174         }
175       else
176         {
177           if (wptr == end)
178             length_mismatch ();
179           *wptr = *p;
180           ++wptr;
181         }
182
183     if (wptr == end)
184       length_mismatch ();
185     *wptr = '\0';
186     ++wptr;
187     if (len_objname != end - wptr)
188       length_mismatch ();
189     exception->objname = memcpy (wptr, objname, len_objname);
190   }
191 }
192 rtld_hidden_def (_dl_exception_create_format)
193
194 void
195 _dl_exception_free (struct dl_exception *exception)
196 {
197   free (exception->message_buffer);
198   exception->objname = NULL;
199   exception->errstring = NULL;
200   exception->message_buffer = NULL;
201 }
202 rtld_hidden_def (_dl_exception_free)