1 /* $NetBSD: paths.c,v 1.43 2025/05/02 23:04:56 riastradh Exp $ */ 2 3 /* 4 * Copyright 1996 Matt Thomas <matt (at) 3am-software.com> 5 * Copyright 2002 Charles M. Hannum <root (at) 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.43 2025/05/02 23:04:56 riastradh 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 #include "../../lib/libc/include/__sysctl.h" /* __sysctl syscall stub */ 59 60 static Search_Path *_rtld_find_path(Search_Path *, const char *, size_t); 61 static Search_Path **_rtld_append_path(Search_Path **, Search_Path **, 62 const char *, const char *, const char *); 63 static void _rtld_process_mapping(Library_Xform **, const char *, 64 const char *); 65 static char *exstrdup(const char *, const char *); 66 static const char *getstr(const char **, const char *, const char *); 67 static const char *getcstr(const char **, const char *, const char *); 68 static const char *getword(const char **, const char *, const char *); 69 static int matchstr(const char *, const char *, const char *); 70 71 static const char WS[] = " \t\n"; 72 73 /* 74 * Like xstrdup(), but takes end of string as a argument. 75 */ 76 static char * 77 exstrdup(const char *bp, const char *ep) 78 { 79 char *cp; 80 size_t len = ep - bp; 81 82 cp = xmalloc(len + 1); 83 memcpy(cp, bp, len); 84 cp[len] = '\0'; 85 return (cp); 86 } 87 88 /* 89 * Like strsep(), but takes end of string and doesn't put any NUL. To 90 * detect empty string, compare `*p' and return value. 91 */ 92 static const char * 93 getstr(const char **p, const char *ep, const char *delim) 94 { 95 const char *cp = *p, *q, *r; 96 97 if (ep < cp) 98 /* End of string */ 99 return (NULL); 100 101 for (q = cp; q < ep; q++) 102 for (r = delim; *r != 0; r++) 103 if (*r == *q) 104 goto done; 105 106 done: 107 *p = q; 108 return (cp); 109 } 110 111 /* 112 * Like getstr() above, but delim[] is complemented. 113 */ 114 static const char * 115 getcstr(const char **p, const char *ep, const char *delim) 116 { 117 const char *cp = *p, *q, *r; 118 119 if (ep < cp) 120 /* End of string */ 121 return (NULL); 122 123 for (q = cp; q < ep; q++) 124 for (r = delim; *r != *q; r++) 125 if (*r == 0) 126 goto done; 127 128 done: 129 *p = q; 130 return (cp); 131 } 132 133 static const char * 134 getword(const char **p, const char *ep, const char *delim) 135 { 136 137 (void)getcstr(p, ep, delim); 138 139 /* 140 * Now, we're looking non-delim, or end of string. 141 */ 142 143 return (getstr(p, ep, delim)); 144 } 145 146 /* 147 * Match `bp' against NUL terminated string pointed by `p'. 148 */ 149 static int 150 matchstr(const char *p, const char *bp, const char *ep) 151 { 152 int c; 153 154 while (bp < ep) 155 if ((c = *p++) == 0 || c != *bp++) 156 return (0); 157 158 return (*p == 0); 159 } 160 161 static Search_Path * 162 _rtld_find_path(Search_Path *path, const char *pathstr, size_t pathlen) 163 { 164 165 for (; path != NULL; path = path->sp_next) { 166 if (pathlen == path->sp_pathlen && 167 memcmp(path->sp_path, pathstr, pathlen) == 0) 168 return path; 169 } 170 return NULL; 171 } 172 173 static Search_Path ** 174 _rtld_append_path(Search_Path **head_p, Search_Path **path_p, 175 const char *execname, const char *bp, const char *ep) 176 { 177 Search_Path *path; 178 char epath[MAXPATHLEN]; 179 size_t len; 180 181 len = _rtld_expand_path(epath, sizeof(epath), execname, bp, ep); 182 if (len == 0) 183 return path_p; 184 185 if (_rtld_find_path(*head_p, bp, ep - bp) != NULL) 186 return path_p; 187 188 path = NEW(Search_Path); 189 path->sp_pathlen = len; 190 path->sp_path = exstrdup(epath, epath + len); 191 path->sp_next = (*path_p); 192 (*path_p) = path; 193 path_p = &path->sp_next; 194 195 dbg((" added path \"%s\"", path->sp_path)); 196 return path_p; 197 } 198 199 void 200 _rtld_add_paths(const char *execname, Search_Path **path_p, const char *pathstr) 201 { 202 Search_Path **head_p = path_p; 203 204 if (pathstr == NULL) 205 return; 206 207 if (pathstr[0] == ':') { 208 /* 209 * Leading colon means append to current path 210 */ 211 while ((*path_p) != NULL) 212 path_p = &(*path_p)->sp_next; 213 pathstr++; 214 } 215 216 for (;;) { 217 const char *bp = pathstr; 218 const char *ep = strchr(bp, ':'); 219 if (ep == NULL) 220 ep = &pathstr[strlen(pathstr)]; 221 222 path_p = _rtld_append_path(head_p, path_p, execname, bp, ep); 223 224 if (ep[0] == '\0') 225 break; 226 pathstr = ep + 1; 227 } 228 } 229 230 /* 231 * Process library mappings of the form: 232 * <library_name> <machdep_variable> <value,...:library_name,...> ... 233 */ 234 static void 235 _rtld_process_mapping(Library_Xform **lib_p, const char *bp, const char *ep) 236 { 237 Library_Xform *hwptr = NULL; 238 const char *ptr, *key, *ekey, *lib, *elib, *l; 239 int i, j; 240 241 dbg((" processing mapping \"%.*s\"", (int)(ep - bp), bp)); 242 243 if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp) 244 return; 245 246 dbg((" library \"%.*s\"", (int)(bp - ptr), ptr)); 247 248 hwptr = xmalloc(sizeof(*hwptr)); 249 memset(hwptr, 0, sizeof(*hwptr)); 250 hwptr->name = exstrdup(ptr, bp); 251 252 bp++; 253 254 if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp) { 255 xwarnx("missing sysctl variable name"); 256 goto cleanup; 257 } 258 259 dbg((" sysctl \"%.*s\"", (int)(bp - ptr), ptr)); 260 261 hwptr->ctlname = exstrdup(ptr, bp); 262 263 for (i = 0; bp++, (ptr = getword(&bp, ep, WS)) != NULL;) { 264 dbg((" ptr = %.*s", (int)(bp - ptr), ptr)); 265 if (ptr == bp) 266 continue; 267 268 if (i == RTLD_MAX_ENTRY) { 269 no_more: 270 xwarnx("maximum library entries exceeded `%s'", 271 hwptr->name); 272 goto cleanup; 273 } 274 if ((key = getstr(&ptr, bp, ":")) == NULL) { 275 xwarnx("missing sysctl variable value for `%s'", 276 hwptr->name); 277 goto cleanup; 278 } 279 ekey = ptr++; 280 if ((lib = getstr(&ptr, bp, ":")) == NULL) { 281 xwarnx("missing sysctl library list for `%s'", 282 hwptr->name); 283 goto cleanup; 284 } 285 elib = ptr; /* No need to advance */ 286 for (j = 0; (l = getstr(&lib, elib, ",")) != NULL; 287 j++, lib++) { 288 if (j == RTLD_MAX_LIBRARY) { 289 xwarnx("maximum library entries exceeded `%s'", 290 hwptr->name); 291 goto cleanup; 292 } 293 dbg((" library \"%.*s\"", (int)(lib - l), l)); 294 hwptr->entry[i].library[j] = exstrdup(l, lib); 295 } 296 if (j == 0) { 297 xwarnx("No library map entries for `%s/%.*s'", 298 hwptr->name, (int)(bp - ptr), ptr); 299 goto cleanup; 300 } 301 j = i; 302 for (; (l = getstr(&key, ekey, ",")) != NULL; i++, key++) { 303 /* 304 * Allow empty key (it is valid as string 305 * value). Thus, we loop at least once and 306 * `i' is incremented. 307 */ 308 309 dbg((" key \"%.*s\"", (int)(key - l), l)); 310 if (i == RTLD_MAX_ENTRY) 311 goto no_more; 312 if (i != j) 313 (void)memcpy(hwptr->entry[i].library, 314 hwptr->entry[j].library, 315 sizeof(hwptr->entry[j].library)); 316 hwptr->entry[i].value = exstrdup(l, key); 317 } 318 } 319 320 if (i == 0) { 321 xwarnx("No library entries for `%s'", hwptr->name); 322 goto cleanup; 323 } 324 325 hwptr->next = *lib_p; 326 *lib_p = hwptr; 327 328 return; 329 330 cleanup: 331 if (hwptr->name) 332 xfree(hwptr->name); 333 xfree(hwptr); 334 } 335 336 void 337 _rtld_process_hints(const char *execname, Search_Path **path_p, 338 Library_Xform **lib_p, const char *fname) 339 { 340 int fd; 341 char *buf, small[128]; 342 const char *b, *ep, *ptr; 343 struct stat st; 344 ssize_t sz; 345 Search_Path **head_p = path_p; 346 347 if ((fd = open(fname, O_RDONLY)) == -1) { 348 /* Don't complain */ 349 return; 350 } 351 352 /* Try to avoid mmap/stat on the file. */ 353 buf = small; 354 buf[0] = '\0'; 355 sz = read(fd, buf, sizeof(small)); 356 if (sz == -1) { 357 xwarn("read: %s", fname); 358 (void)close(fd); 359 return; 360 } 361 if (sz >= (ssize_t)sizeof(small)) { 362 if (fstat(fd, &st) == -1) { 363 /* Complain */ 364 xwarn("fstat: %s", fname); 365 (void)close(fd); 366 return; 367 } 368 369 sz = (ssize_t) st.st_size; 370 371 buf = mmap(0, sz, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0); 372 if (buf == MAP_FAILED) { 373 xwarn("mmap: %s", fname); 374 (void)close(fd); 375 return; 376 } 377 } 378 (void)close(fd); 379 380 while ((*path_p) != NULL) 381 path_p = &(*path_p)->sp_next; 382 383 for (b = buf, ep = buf + sz; b < ep; b++) { 384 (void)getcstr(&b, ep, WS); 385 if (b == ep) 386 break; 387 388 ptr = getstr(&b, ep, "\n#"); 389 if (*ptr == '/') { 390 /* 391 * Since '/' != '\n' and != '#', we know ptr < 392 * b. And we will stop when b[-1] == '/'. 393 */ 394 while (b[-1] == ' ' || b[-1] == '\t') 395 b--; 396 path_p = _rtld_append_path(head_p, path_p, execname, 397 ptr, b); 398 } else 399 _rtld_process_mapping(lib_p, ptr, b); 400 401 /* 402 * b points one of ' ', \t, \n, # or equal to ep. So, 403 * make sure we are at newline or end of string. 404 */ 405 (void)getstr(&b, ep, "\n"); 406 } 407 408 if (buf != small) 409 (void)munmap(buf, sz); 410 } 411 412 /* Basic name -> sysctl MIB translation */ 413 int 414 _rtld_sysctl(const char *name, void *oldp, size_t *oldlen) 415 { 416 const char *node, *ep; 417 struct sysctlnode query, *result, *newresult; 418 int mib[CTL_MAXNAME], r; 419 size_t res_size, n, i; 420 u_int miblen = 0; 421 422 /* Start with 16 entries, will grow it up as needed. */ 423 res_size = 16 * sizeof(struct sysctlnode); 424 result = xmalloc(res_size); 425 if (result == NULL) 426 return (-1); 427 428 ep = name + strlen(name); 429 do { 430 i = ~0ul; 431 while (*name == '/' || *name == '.') 432 name++; 433 if (name >= ep) 434 break; 435 436 mib[miblen] = CTL_QUERY; 437 memset(&query, 0, sizeof(query)); 438 query.sysctl_flags = SYSCTL_VERSION; 439 440 n = res_size; 441 if (__sysctl(mib, miblen + 1, result, &n, &query, 442 sizeof(query)) == -1) { 443 if (errno != ENOMEM) 444 goto bad; 445 /* Grow up result */ 446 res_size = n; 447 newresult = xrealloc(result, res_size); 448 if (newresult == NULL) 449 goto bad; 450 result = newresult; 451 if (__sysctl(mib, miblen + 1, result, &n, &query, 452 sizeof(query)) == -1) 453 goto bad; 454 } 455 n /= sizeof(struct sysctlnode); 456 457 node = getstr(&name, ep, "./"); 458 459 for (i = 0; i < n; i++) 460 if (matchstr(result[i].sysctl_name, node, name)) { 461 mib[miblen] = result[i].sysctl_num; 462 miblen++; 463 break; 464 } 465 } while (name < ep && miblen <= CTL_MAXNAME); 466 467 if (name < ep || i == ~0ul) 468 goto bad; 469 r = SYSCTL_TYPE(result[i].sysctl_flags); 470 471 xfree(result); 472 if (__sysctl(mib, miblen, oldp, oldlen, NULL, 0) == -1) 473 return (-1); 474 return r; 475 476 bad: 477 xfree(result); 478 return (-1); 479 } 480