xref: /netbsd-src/libexec/ld.elf_so/paths.c (revision a21e6644478f427f359ffeb0672f3dc904c9fa4f)
1 /*	$NetBSD: paths.c,v 1.42 2016/01/24 01:56:04 christos Exp $	 */
2 
3 /*
4  * Copyright 1996 Matt Thomas <matt@3am-software.com>
5  * Copyright 2002 Charles M. Hannum <root@ihack.net>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 #ifndef lint
33 __RCSID("$NetBSD: paths.c,v 1.42 2016/01/24 01:56:04 christos Exp $");
34 #endif /* not lint */
35 
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/sysctl.h>
47 #include <sys/mman.h>
48 #include <sys/stat.h>
49 #include <sys/gmon.h>
50 #include <sys/socket.h>
51 #include <sys/mount.h>
52 #include <sys/resource.h>
53 #include <machine/cpu.h>
54 
55 #include "debug.h"
56 #include "rtld.h"
57 
58 static Search_Path *_rtld_find_path(Search_Path *, const char *, size_t);
59 static Search_Path **_rtld_append_path(Search_Path **, Search_Path **,
60     const char *, const char *, const char *);
61 static void _rtld_process_mapping(Library_Xform **, const char *,
62     const char *);
63 static char *exstrdup(const char *, const char *);
64 static const char *getstr(const char **, const char *, const char *);
65 static const char *getcstr(const char **, const char *, const char *);
66 static const char *getword(const char **, const char *, const char *);
67 static int matchstr(const char *, const char *, const char *);
68 
69 static const char WS[] = " \t\n";
70 
71 /*
72  * Like xstrdup(), but takes end of string as a argument.
73  */
74 static char *
exstrdup(const char * bp,const char * ep)75 exstrdup(const char *bp, const char *ep)
76 {
77 	char *cp;
78 	size_t len = ep - bp;
79 
80 	cp = xmalloc(len + 1);
81 	memcpy(cp, bp, len);
82 	cp[len] = '\0';
83 	return (cp);
84 }
85 
86 /*
87  * Like strsep(), but takes end of string and doesn't put any NUL.  To
88  * detect empty string, compare `*p' and return value.
89  */
90 static const char *
getstr(const char ** p,const char * ep,const char * delim)91 getstr(const char **p, const char *ep, const char *delim)
92 {
93 	const char *cp = *p, *q, *r;
94 
95 	if (ep < cp)
96 		/* End of string */
97 		return (NULL);
98 
99 	for (q = cp; q < ep; q++)
100 		for (r = delim; *r != 0; r++)
101 			if (*r == *q)
102 				goto done;
103 
104 done:
105 	*p = q;
106 	return (cp);
107 }
108 
109 /*
110  * Like getstr() above, but delim[] is complemented.
111  */
112 static const char *
getcstr(const char ** p,const char * ep,const char * delim)113 getcstr(const char **p, const char *ep, const char *delim)
114 {
115 	const char *cp = *p, *q, *r;
116 
117 	if (ep < cp)
118 		/* End of string */
119 		return (NULL);
120 
121 	for (q = cp; q < ep; q++)
122 		for (r = delim; *r != *q; r++)
123 			if (*r == 0)
124 				goto done;
125 
126 done:
127 	*p = q;
128 	return (cp);
129 }
130 
131 static const char *
getword(const char ** p,const char * ep,const char * delim)132 getword(const char **p, const char *ep, const char *delim)
133 {
134 
135 	(void)getcstr(p, ep, delim);
136 
137 	/*
138 	 * Now, we're looking non-delim, or end of string.
139 	 */
140 
141 	return (getstr(p, ep, delim));
142 }
143 
144 /*
145  * Match `bp' against NUL terminated string pointed by `p'.
146  */
147 static int
matchstr(const char * p,const char * bp,const char * ep)148 matchstr(const char *p, const char *bp, const char *ep)
149 {
150 	int c;
151 
152 	while (bp < ep)
153 		if ((c = *p++) == 0 || c != *bp++)
154 			return (0);
155 
156 	return (*p == 0);
157 }
158 
159 static Search_Path *
_rtld_find_path(Search_Path * path,const char * pathstr,size_t pathlen)160 _rtld_find_path(Search_Path *path, const char *pathstr, size_t pathlen)
161 {
162 
163 	for (; path != NULL; path = path->sp_next) {
164 		if (pathlen == path->sp_pathlen &&
165 		    memcmp(path->sp_path, pathstr, pathlen) == 0)
166 			return path;
167 	}
168 	return NULL;
169 }
170 
171 static Search_Path **
_rtld_append_path(Search_Path ** head_p,Search_Path ** path_p,const char * execname,const char * bp,const char * ep)172 _rtld_append_path(Search_Path **head_p, Search_Path **path_p,
173     const char *execname, const char *bp, const char *ep)
174 {
175 	Search_Path *path;
176 	char epath[MAXPATHLEN];
177 	size_t len;
178 
179 	len = _rtld_expand_path(epath, sizeof(epath), execname, bp, ep);
180 	if (len == 0)
181 		return path_p;
182 
183 	if (_rtld_find_path(*head_p, bp, ep - bp) != NULL)
184 		return path_p;
185 
186 	path = NEW(Search_Path);
187 	path->sp_pathlen = len;
188 	path->sp_path = exstrdup(epath, epath + len);
189 	path->sp_next = (*path_p);
190 	(*path_p) = path;
191 	path_p = &path->sp_next;
192 
193 	dbg((" added path \"%s\"", path->sp_path));
194 	return path_p;
195 }
196 
197 void
_rtld_add_paths(const char * execname,Search_Path ** path_p,const char * pathstr)198 _rtld_add_paths(const char *execname, Search_Path **path_p, const char *pathstr)
199 {
200 	Search_Path **head_p = path_p;
201 
202 	if (pathstr == NULL)
203 		return;
204 
205 	if (pathstr[0] == ':') {
206 		/*
207 		 * Leading colon means append to current path
208 		 */
209 		while ((*path_p) != NULL)
210 			path_p = &(*path_p)->sp_next;
211 		pathstr++;
212 	}
213 
214 	for (;;) {
215 		const char *bp = pathstr;
216 		const char *ep = strchr(bp, ':');
217 		if (ep == NULL)
218 			ep = &pathstr[strlen(pathstr)];
219 
220 		path_p = _rtld_append_path(head_p, path_p, execname, bp, ep);
221 
222 		if (ep[0] == '\0')
223 			break;
224 		pathstr = ep + 1;
225 	}
226 }
227 
228 /*
229  * Process library mappings of the form:
230  *	<library_name>	<machdep_variable> <value,...:library_name,...> ...
231  */
232 static void
_rtld_process_mapping(Library_Xform ** lib_p,const char * bp,const char * ep)233 _rtld_process_mapping(Library_Xform **lib_p, const char *bp, const char *ep)
234 {
235 	Library_Xform *hwptr = NULL;
236 	const char *ptr, *key, *ekey, *lib, *elib, *l;
237 	int i, j;
238 
239 	dbg((" processing mapping \"%.*s\"", (int)(ep - bp), bp));
240 
241 	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp)
242 		return;
243 
244 	dbg((" library \"%.*s\"", (int)(bp - ptr), ptr));
245 
246 	hwptr = xmalloc(sizeof(*hwptr));
247 	memset(hwptr, 0, sizeof(*hwptr));
248 	hwptr->name = exstrdup(ptr, bp);
249 
250 	bp++;
251 
252 	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp) {
253 		xwarnx("missing sysctl variable name");
254 		goto cleanup;
255 	}
256 
257 	dbg((" sysctl \"%.*s\"", (int)(bp - ptr), ptr));
258 
259 	hwptr->ctlname = exstrdup(ptr, bp);
260 
261 	for (i = 0; bp++, (ptr = getword(&bp, ep, WS)) != NULL;) {
262 		dbg((" ptr = %.*s", (int)(bp - ptr), ptr));
263 		if (ptr == bp)
264 			continue;
265 
266 		if (i == RTLD_MAX_ENTRY) {
267 no_more:
268 			xwarnx("maximum library entries exceeded `%s'",
269 			    hwptr->name);
270 			goto cleanup;
271 		}
272 		if ((key = getstr(&ptr, bp, ":")) == NULL) {
273 			xwarnx("missing sysctl variable value for `%s'",
274 			    hwptr->name);
275 			goto cleanup;
276 		}
277 		ekey = ptr++;
278 		if ((lib = getstr(&ptr, bp, ":")) == NULL) {
279 			xwarnx("missing sysctl library list for `%s'",
280 			    hwptr->name);
281 			goto cleanup;
282 		}
283 		elib = ptr;		/* No need to advance */
284 		for (j = 0; (l = getstr(&lib, elib, ",")) != NULL;
285 		    j++, lib++) {
286 			if (j == RTLD_MAX_LIBRARY) {
287 				xwarnx("maximum library entries exceeded `%s'",
288 				    hwptr->name);
289 				goto cleanup;
290 			}
291 			dbg((" library \"%.*s\"", (int)(lib - l), l));
292 			hwptr->entry[i].library[j] = exstrdup(l, lib);
293 		}
294 		if (j == 0) {
295 			xwarnx("No library map entries for `%s/%.*s'",
296 			    hwptr->name, (int)(bp - ptr), ptr);
297 			goto cleanup;
298 		}
299 		j = i;
300 		for (; (l = getstr(&key, ekey, ",")) != NULL; i++, key++) {
301 			/*
302 			 * Allow empty key (it is valid as string
303 			 * value).  Thus, we loop at least once and
304 			 * `i' is incremented.
305 			 */
306 
307 			dbg((" key \"%.*s\"", (int)(key - l), l));
308 			if (i == RTLD_MAX_ENTRY)
309 				goto no_more;
310 			if (i != j)
311 				(void)memcpy(hwptr->entry[i].library,
312 				    hwptr->entry[j].library,
313 				    sizeof(hwptr->entry[j].library));
314 			hwptr->entry[i].value = exstrdup(l, key);
315 		}
316 	}
317 
318 	if (i == 0) {
319 		xwarnx("No library entries for `%s'", hwptr->name);
320 		goto cleanup;
321 	}
322 
323 	hwptr->next = *lib_p;
324 	*lib_p = hwptr;
325 
326 	return;
327 
328 cleanup:
329 	if (hwptr->name)
330 		xfree(hwptr->name);
331 	xfree(hwptr);
332 }
333 
334 void
_rtld_process_hints(const char * execname,Search_Path ** path_p,Library_Xform ** lib_p,const char * fname)335 _rtld_process_hints(const char *execname, Search_Path **path_p,
336     Library_Xform **lib_p, const char *fname)
337 {
338 	int fd;
339 	char *buf, small[128];
340 	const char *b, *ep, *ptr;
341 	struct stat st;
342 	ssize_t sz;
343 	Search_Path **head_p = path_p;
344 
345 	if ((fd = open(fname, O_RDONLY)) == -1) {
346 		/* Don't complain */
347 		return;
348 	}
349 
350 	/* Try to avoid mmap/stat on the file. */
351 	buf = small;
352 	buf[0] = '\0';
353 	sz = read(fd, buf, sizeof(small));
354 	if (sz == -1) {
355 		xwarn("read: %s", fname);
356 		(void)close(fd);
357 		return;
358 	}
359 	if (sz >= (ssize_t)sizeof(small)) {
360 		if (fstat(fd, &st) == -1) {
361 			/* Complain */
362 			xwarn("fstat: %s", fname);
363 			(void)close(fd);
364 			return;
365 		}
366 
367 		sz = (ssize_t) st.st_size;
368 
369 		buf = mmap(0, sz, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
370 		if (buf == MAP_FAILED) {
371 			xwarn("mmap: %s", fname);
372 			(void)close(fd);
373 			return;
374 		}
375 	}
376 	(void)close(fd);
377 
378 	while ((*path_p) != NULL)
379 		path_p = &(*path_p)->sp_next;
380 
381 	for (b = buf, ep = buf + sz; b < ep; b++) {
382 		(void)getcstr(&b, ep, WS);
383 		if (b == ep)
384 			break;
385 
386 		ptr = getstr(&b, ep, "\n#");
387 		if (*ptr == '/') {
388 			/*
389 			 * Since '/' != '\n' and != '#', we know ptr <
390 			 * b.  And we will stop when b[-1] == '/'.
391 			 */
392 			while (b[-1] == ' ' || b[-1] == '\t')
393 				b--;
394 			path_p = _rtld_append_path(head_p, path_p, execname,
395 			    ptr, b);
396 		} else
397 			_rtld_process_mapping(lib_p, ptr, b);
398 
399 		/*
400 		 * b points one of ' ', \t, \n, # or equal to ep.  So,
401 		 * make sure we are at newline or end of string.
402 		 */
403 		(void)getstr(&b, ep, "\n");
404 	}
405 
406 	if (buf != small)
407 		(void)munmap(buf, sz);
408 }
409 
410 /* Basic name -> sysctl MIB translation */
411 int
_rtld_sysctl(const char * name,void * oldp,size_t * oldlen)412 _rtld_sysctl(const char *name, void *oldp, size_t *oldlen)
413 {
414 	const char *node, *ep;
415 	struct sysctlnode query, *result, *newresult;
416 	int mib[CTL_MAXNAME], r;
417 	size_t res_size, n, i;
418 	u_int miblen = 0;
419 
420 	/* Start with 16 entries, will grow it up as needed. */
421 	res_size = 16 * sizeof(struct sysctlnode);
422 	result = xmalloc(res_size);
423 	if (result == NULL)
424 		return (-1);
425 
426 	ep = name + strlen(name);
427 	do {
428 		i = ~0ul;
429 		while (*name == '/' || *name == '.')
430 			name++;
431 		if (name >= ep)
432 			break;
433 
434 		mib[miblen] = CTL_QUERY;
435 		memset(&query, 0, sizeof(query));
436 		query.sysctl_flags = SYSCTL_VERSION;
437 
438 		n = res_size;
439 		if (sysctl(mib, miblen + 1, result, &n, &query,
440 		    sizeof(query)) == -1) {
441 			if (errno != ENOMEM)
442 				goto bad;
443 			/* Grow up result */
444 			res_size = n;
445 			newresult = xrealloc(result, res_size);
446 			if (newresult == NULL)
447 				goto bad;
448 			result = newresult;
449 			if (sysctl(mib, miblen + 1, result, &n, &query,
450 			    sizeof(query)) == -1)
451 				goto bad;
452 		}
453 		n /= sizeof(struct sysctlnode);
454 
455 		node = getstr(&name, ep, "./");
456 
457 		for (i = 0; i < n; i++)
458 			if (matchstr(result[i].sysctl_name, node, name)) {
459 				mib[miblen] = result[i].sysctl_num;
460 				miblen++;
461 				break;
462 			}
463 	} while (name < ep && miblen <= CTL_MAXNAME);
464 
465 	if (name < ep || i == ~0ul)
466 		goto bad;
467 	r = SYSCTL_TYPE(result[i].sysctl_flags);
468 
469 	xfree(result);
470 	if (sysctl(mib, miblen, oldp, oldlen, NULL, 0) == -1)
471 		return (-1);
472 	return r;
473 
474 bad:
475 	xfree(result);
476 	return (-1);
477 }
478