1/* ld.so error exception allocation and deallocation.
2 Copyright (C) 1995-2018 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. */
30static const char _dl_out_of_memory[] = "out of memory";
31
32/* Dummy allocation object used if allocating the message buffer
33 fails. */
34static void
35oom_exception (struct dl_exception *exception)
36{
37 exception->objname = "";
38 exception->errstring = _dl_out_of_memory;
39 exception->message_buffer = NULL;
40}
41
42static void
43__attribute__ ((noreturn))
44length_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. */
53static void
54adjust_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
69void
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}
90rtld_hidden_def (_dl_exception_create)
91
92void
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}
192rtld_hidden_def (_dl_exception_create_format)
193
194void
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}
202rtld_hidden_def (_dl_exception_free)
203