xref: /openbsd-src/libexec/ld.so/sod.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: sod.c,v 1.33 2015/10/29 13:07:41 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1993 Paul Kranenburg
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Paul Kranenburg.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 
34 #include <sys/types.h>
35 #include <sys/mman.h>
36 #include <machine/exec.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <fcntl.h>
40 #include <nlist.h>
41 #include <link.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 
46 #include "syscall.h"
47 #include "archdep.h"
48 #include "path.h"
49 #include "util.h"
50 #include "sod.h"
51 
52 int _dl_hinthash(char *cp, int vmajor, int vminor);
53 void _dl_maphints(void);
54 
55 /*
56  * Populate sod struct for dlopen's call to map_object
57  */
58 void
59 _dl_build_sod(const char *name, struct sod *sodp)
60 {
61 	unsigned int	tuplet;
62 	int		major, minor;
63 	char		*realname, *tok, *etok, *cp;
64 
65 	/* default is an absolute or relative path */
66 	sodp->sod_name = (long)_dl_strdup(name);    /* strtok is destructive */
67 	if (sodp->sod_name == 0)
68 		_dl_exit(7);
69 	sodp->sod_library = 0;
70 	sodp->sod_major = sodp->sod_minor = 0;
71 
72 	/* does it look like /^lib/ ? */
73 	if (_dl_strncmp((char *)sodp->sod_name, "lib", 3) != 0)
74 		goto backout;
75 
76 	/* is this a filename? */
77 	if (_dl_strchr((char *)sodp->sod_name, '/'))
78 		goto backout;
79 
80 	/* skip over 'lib' */
81 	cp = (char *)sodp->sod_name + 3;
82 
83 	realname = cp;
84 
85 	/* dot guardian */
86 	if ((_dl_strchr(cp, '.') == NULL) || (*(cp+_dl_strlen(cp)-1) == '.'))
87 		goto backout;
88 
89 	cp = _dl_strstr(cp, ".so");
90 	if (cp == NULL)
91 		goto backout;
92 
93 	/* default */
94 	major = minor = -1;
95 
96 	/* loop through name - parse skipping name */
97 	for (tuplet = 0; (tok = _dl_strsep(&cp, ".")) != NULL; tuplet++) {
98 		switch (tuplet) {
99 		case 0:
100 			/* empty tok, we already skipped to "\.so.*" */
101 			break;
102 		case 1:
103 			/* 'so' extension */
104 			break;
105 		case 2:
106 			/* major version extension */
107 			major = _dl_strtol(tok, &etok, 10);
108 			if (*tok == '\0' || *etok != '\0')
109 				goto backout;
110 			break;
111 		case 3:
112 			/* minor version extension */
113 			minor = _dl_strtol(tok, &etok, 10);
114 			if (*tok == '\0' || *etok != '\0')
115 				goto backout;
116 			break;
117 		/* if we get here, it must be weird */
118 		default:
119 			goto backout;
120 		}
121 	}
122 	if (realname == NULL)
123 		goto backout;
124 	cp = (char *)sodp->sod_name;
125 	sodp->sod_name = (long)_dl_strdup(realname);
126 	if (sodp->sod_name == 0)
127 		_dl_exit(7);
128 	_dl_free(cp);
129 	sodp->sod_library = 1;
130 	sodp->sod_major = major;
131 	sodp->sod_minor = minor;
132 	return;
133 
134 backout:
135 	_dl_free((char *)sodp->sod_name);
136 	sodp->sod_name = (long)_dl_strdup(name);
137 	if (sodp->sod_name == 0)
138 		_dl_exit(7);
139 }
140 
141 void
142 _dl_set_sod(const char *path, struct sod *sod)
143 {
144 	char *fname = _dl_strrchr(path, '/');
145 
146 	if (fname != NULL)
147 		_dl_build_sod(++fname, sod);
148 	else
149 		_dl_build_sod(path, sod);
150 }
151 
152 static struct hints_header	*hheader = NULL;
153 static struct hints_bucket	*hbuckets;
154 static char			*hstrtab;
155 char				**_dl_hint_search_path = NULL;
156 
157 #define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
158 
159 void
160 _dl_maphints(void)
161 {
162 	struct stat	sb;
163 	caddr_t		addr = MAP_FAILED;
164 	long		hsize = 0;
165 	int		hfd;
166 
167 	if ((hfd = _dl_open(_PATH_LD_HINTS, O_RDONLY | O_CLOEXEC)) < 0)
168 		goto bad_hints;
169 
170 	if (_dl_fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
171 	    sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX)
172 		goto bad_hints;
173 
174 	hsize = (long)sb.st_size;
175 	addr = (void *)_dl_mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
176 	if (_dl_mmap_error(addr))
177 		goto bad_hints;
178 
179 	hheader = (struct hints_header *)addr;
180 	if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize)
181 		goto bad_hints;
182 
183 	if (hheader->hh_version != LD_HINTS_VERSION_2)
184 		goto bad_hints;
185 
186 	hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
187 	hstrtab = (char *)(addr + hheader->hh_strtab);
188 	if (hheader->hh_version >= LD_HINTS_VERSION_2)
189 		_dl_hint_search_path = _dl_split_path(hstrtab + hheader->hh_dirlist);
190 
191 	/* close the file descriptor, leaving the hints mapped */
192 	_dl_close(hfd);
193 
194 	return;
195 
196 bad_hints:
197 	if (!_dl_mmap_error(addr))
198 		_dl_munmap(addr, hsize);
199 	if (hfd != -1)
200 		_dl_close(hfd);
201 	hheader = (struct hints_header *)-1;
202 }
203 
204 char *
205 _dl_findhint(char *name, int major, int minor, char *preferred_path)
206 {
207 	struct hints_bucket	*bp;
208 
209 	/*
210 	 * If not mapped, and we have not tried before, try to map the
211 	 * hints, if previous attempts failed hheader is -1 and we
212 	 * do not wish to retry it.
213 	 */
214 	if (hheader == NULL)
215 		_dl_maphints();
216 
217 	/* if it failed to map, return failure */
218 	if (!(HINTS_VALID))
219 		return NULL;
220 
221 	if (hheader->hh_nbucket == 0)
222 		return NULL;
223 
224 	bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
225 
226 	while (1) {
227 		/* Sanity check */
228 		if (bp->hi_namex >= hheader->hh_strtab_sz) {
229 			_dl_printf("Bad name index: %#x\n", bp->hi_namex);
230 			_dl_exit(7);
231 			break;
232 		}
233 		if (bp->hi_pathx >= hheader->hh_strtab_sz) {
234 			_dl_printf("Bad path index: %#x\n", bp->hi_pathx);
235 			_dl_exit(7);
236 			break;
237 		}
238 
239 		if (_dl_strcmp(name, hstrtab + bp->hi_namex) == 0) {
240 			/* It's `name', check version numbers */
241 			if (bp->hi_major == major &&
242 			    (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
243 				if (preferred_path == NULL) {
244 					return hstrtab + bp->hi_pathx;
245 				} else {
246 					char *path = hstrtab + bp->hi_pathx;
247 					char *edir = _dl_strrchr(path, '/');
248 
249 					if ((_dl_strncmp(preferred_path, path,
250 					    (edir - path)) == 0) &&
251 					    (preferred_path[edir - path] == '\0'))
252 						return path;
253 				}
254 			}
255 		}
256 
257 		if (bp->hi_next == -1)
258 			break;
259 
260 		/* Move on to next in bucket */
261 		bp = &hbuckets[bp->hi_next];
262 	}
263 
264 	/* No hints available for name */
265 	return NULL;
266 }
267 
268 int
269 _dl_hinthash(char *cp, int vmajor, int vminor)
270 {
271 	int	k = 0;
272 
273 	while (*cp)
274 		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
275 
276 	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
277 
278 	return k;
279 }
280