Changeset View
Standalone View
libexec/rtld-elf/rtld.c
Show First 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | |||||
static void objlist_call_init(Objlist *, RtldLockState *); | static void objlist_call_init(Objlist *, RtldLockState *); | ||||
static void objlist_clear(Objlist *); | static void objlist_clear(Objlist *); | ||||
static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); | static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); | ||||
static void objlist_init(Objlist *); | static void objlist_init(Objlist *); | ||||
static void objlist_push_head(Objlist *, Obj_Entry *); | static void objlist_push_head(Objlist *, Obj_Entry *); | ||||
static void objlist_push_tail(Objlist *, Obj_Entry *); | static void objlist_push_tail(Objlist *, Obj_Entry *); | ||||
static void objlist_put_after(Objlist *, Obj_Entry *, Obj_Entry *); | static void objlist_put_after(Objlist *, Obj_Entry *, Obj_Entry *); | ||||
static void objlist_remove(Objlist *, Obj_Entry *); | static void objlist_remove(Objlist *, Obj_Entry *); | ||||
static int parse_args(char* argv[], int argc, bool *use_pathp, int *fdp); | |||||
static int parse_integer(const char *); | static int parse_integer(const char *); | ||||
static void *path_enumerate(const char *, path_enum_proc, void *); | static void *path_enumerate(const char *, path_enum_proc, void *); | ||||
static void print_usage(const char *argv0); | |||||
static void release_object(Obj_Entry *); | static void release_object(Obj_Entry *); | ||||
static int relocate_object_dag(Obj_Entry *root, bool bind_now, | static int relocate_object_dag(Obj_Entry *root, bool bind_now, | ||||
Obj_Entry *rtldobj, int flags, RtldLockState *lockstate); | Obj_Entry *rtldobj, int flags, RtldLockState *lockstate); | ||||
static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, | static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, | ||||
int flags, RtldLockState *lockstate); | int flags, RtldLockState *lockstate); | ||||
static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int, | static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int, | ||||
RtldLockState *); | RtldLockState *); | ||||
static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now, | static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now, | ||||
▲ Show 20 Lines • Show All 217 Lines • ▼ Show 20 Lines | _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) | ||||
const Elf_Phdr *phdr; | const Elf_Phdr *phdr; | ||||
Objlist initlist; | Objlist initlist; | ||||
RtldLockState lockstate; | RtldLockState lockstate; | ||||
struct stat st; | struct stat st; | ||||
Elf_Addr *argcp; | Elf_Addr *argcp; | ||||
char **argv, *argv0, **env, **envp, *kexecpath, *library_path_rpath; | char **argv, *argv0, **env, **envp, *kexecpath, *library_path_rpath; | ||||
caddr_t imgentry; | caddr_t imgentry; | ||||
char buf[MAXPATHLEN]; | char buf[MAXPATHLEN]; | ||||
int argc, fd, i, mib[2], phnum; | int argc, fd, i, mib[2], phnum, rtld_argc; | ||||
size_t len; | size_t len; | ||||
bool dir_enable; | bool dir_enable, explicit_fd, search_in_path; | ||||
/* | /* | ||||
* On entry, the dynamic linker itself has not been relocated yet. | * On entry, the dynamic linker itself has not been relocated yet. | ||||
* Be very careful not to reference any global data until after | * Be very careful not to reference any global data until after | ||||
* init_rtld has returned. It is OK to reference file-scope statics | * init_rtld has returned. It is OK to reference file-scope statics | ||||
* and string constants, and to call static and global functions. | * and string constants, and to call static and global functions. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) | ||||
if (phdr == obj_rtld.phdr) { | if (phdr == obj_rtld.phdr) { | ||||
if (!trust) { | if (!trust) { | ||||
rtld_printf("Tainted process refusing to run binary %s\n", | rtld_printf("Tainted process refusing to run binary %s\n", | ||||
argv0); | argv0); | ||||
rtld_die(); | rtld_die(); | ||||
} | } | ||||
dbg("opening main program in direct exec mode"); | dbg("opening main program in direct exec mode"); | ||||
if (argc >= 2) { | if (argc >= 2) { | ||||
argv0 = argv[1]; | rtld_argc = parse_args(argv, argc, &search_in_path, &fd); | ||||
argv0 = argv[rtld_argc]; | |||||
explicit_fd = (fd != -1); | |||||
kib: {} are not needed | |||||
if (!explicit_fd) | |||||
fd = open(argv0, O_RDONLY | O_CLOEXEC | O_VERIFY); | fd = open(argv0, O_RDONLY | O_CLOEXEC | O_VERIFY); | ||||
if (fd == -1) { | if (fd == -1) { | ||||
rtld_printf("Opening %s: %s\n", argv0, | rtld_printf("Opening %s: %s\n", argv0, | ||||
rtld_strerror(errno)); | rtld_strerror(errno)); | ||||
rtld_die(); | rtld_die(); | ||||
} | } | ||||
if (fstat(fd, &st) == -1) { | if (fstat(fd, &st) == -1) { | ||||
rtld_printf("Stat %s: %s\n", argv0, | _rtld_error("failed to fstat FD %d (%s): %s", fd, | ||||
explicit_fd ? "user-provided descriptor" : argv0, | |||||
rtld_strerror(errno)); | rtld_strerror(errno)); | ||||
rtld_die(); | rtld_die(); | ||||
} | } | ||||
/* | /* | ||||
* Rough emulation of the permission checks done by | * Rough emulation of the permission checks done by | ||||
* execve(2), only Unix DACs are checked, ACLs are | * execve(2), only Unix DACs are checked, ACLs are | ||||
* ignored. Preserve the semantic of disabling owner | * ignored. Preserve the semantic of disabling owner | ||||
* to execute if owner x bit is cleared, even if | * to execute if owner x bit is cleared, even if | ||||
Show All 15 Lines | if (phdr == obj_rtld.phdr) { | ||||
if (!dir_enable) { | if (!dir_enable) { | ||||
rtld_printf("No execute permission for binary %s\n", | rtld_printf("No execute permission for binary %s\n", | ||||
argv0); | argv0); | ||||
rtld_die(); | rtld_die(); | ||||
} | } | ||||
/* | /* | ||||
* For direct exec mode, argv[0] is the interpreter | * For direct exec mode, argv[0] is the interpreter | ||||
* name, we must remove it and shift arguments left by | * name, we must remove it and shift arguments left | ||||
* 1 before invoking binary main. Since stack layout | * before invoking binary main. Since stack layout | ||||
* places environment pointers and aux vectors right | * places environment pointers and aux vectors right | ||||
* after the terminating NULL, we must shift | * after the terminating NULL, we must shift | ||||
* environment and aux as well. | * environment and aux as well. | ||||
* XXX Shift will be > 1 when options are implemented. | |||||
*/ | */ | ||||
main_argc = argc - rtld_argc; | |||||
for (i = 0; i <= main_argc; i++) | |||||
argv[i] = argv[i + rtld_argc]; | |||||
*argcp -= rtld_argc; | |||||
environ = env = envp = argv + main_argc + 1; | |||||
do { | do { | ||||
*argv = *(argv + 1); | *envp = *(envp + rtld_argc); | ||||
argv++; | |||||
} while (*argv != NULL); | |||||
*argcp -= 1; | |||||
main_argc = argc - 1; | |||||
environ = env = envp = argv; | |||||
do { | |||||
*envp = *(envp + 1); | |||||
envp++; | envp++; | ||||
} while (*envp != NULL); | } while (*envp != NULL); | ||||
aux = auxp = (Elf_Auxinfo *)envp; | aux = auxp = (Elf_Auxinfo *)envp; | ||||
auxpf = (Elf_Auxinfo *)(envp + 1); | auxpf = (Elf_Auxinfo *)(envp + rtld_argc); | ||||
for (;; auxp++, auxpf++) { | for (;; auxp++, auxpf++) { | ||||
*auxp = *auxpf; | *auxp = *auxpf; | ||||
if (auxp->a_type == AT_NULL) | if (auxp->a_type == AT_NULL) | ||||
break; | break; | ||||
} | } | ||||
} else { | } else { | ||||
rtld_printf("no binary\n"); | rtld_printf("no binary\n"); | ||||
rtld_die(); | rtld_die(); | ||||
▲ Show 20 Lines • Show All 4,769 Lines • ▼ Show 20 Lines | symlook_init_from_req(SymLook *dst, const SymLook *src) | ||||
dst->flags = src->flags; | dst->flags = src->flags; | ||||
dst->defobj_out = NULL; | dst->defobj_out = NULL; | ||||
dst->sym_out = NULL; | dst->sym_out = NULL; | ||||
dst->lockstate = src->lockstate; | dst->lockstate = src->lockstate; | ||||
} | } | ||||
/* | /* | ||||
* Parse a set of command-line arguments. | |||||
*/ | |||||
static int | |||||
parse_args(char* argv[], int argc, bool *use_pathp, int *fdp) | |||||
{ | |||||
const char *arg; | |||||
int fd, i, j, arglen; | |||||
char opt; | |||||
dbg("Parsing command-line arguments"); | |||||
*use_pathp = false; | |||||
*fdp = -1; | |||||
for (i = 1; i < argc; i++ ) { | |||||
arg = argv[i]; | |||||
dbg("argv[%d]: '%s'", i, arg); | |||||
/* | |||||
* rtld arguments end with an explicit "--" or with the first | |||||
* non-prefixed argument. | |||||
*/ | |||||
if (strcmp(arg, "--") == 0) { | |||||
i++; | |||||
Done Inline ActionsThis is bug, --help is matched as well. kib: This is bug, --help is matched as well. | |||||
Done Inline ActionsOk, I missed that the length is 3, so the nul symbol participates. But then use of strncmp is even less sensible. kib: Ok, I missed that the length is 3, so the nul symbol participates. But then use of strncmp is… | |||||
break; | |||||
} | |||||
if (arg[0] != '-') | |||||
break; | |||||
/* | |||||
* All other arguments are single-character options that can | |||||
* be combined, so we need to search through `arg` for them. | |||||
*/ | |||||
arglen = strlen(arg); | |||||
for (j = 1; j < arglen; j++) { | |||||
opt = arg[j]; | |||||
if (opt == 'h') { | |||||
Done Inline ActionsThis is bug, -fdull <n> is matched as well. And, why do you want -fd be multi-char option, while other options are single-char ? -f <n> works fine, IMO. kib: This is bug, -fdull <n> is matched as well.
And, why do you want -fd be multi-char option… | |||||
print_usage(argv[0]); | |||||
rtld_die(); | |||||
} else if (opt == 'f') { | |||||
/* | |||||
* -f XX can be used to specify a descriptor for the | |||||
* binary named at the command line (i.e., the later | |||||
* argument will specify the process name but the | |||||
* descriptor is what will actually be executed) | |||||
*/ | |||||
if (j != arglen - 1) { | |||||
/* -f must be the last option in, e.g., -abcf */ | |||||
_rtld_error("invalid options: %s", arg); | |||||
Done Inline ActionsI do not see why this check is needed. I understand why do you want to make sure that fd is a file descriptor at all, ie. the fstat(2) call above makes sense. But properties of the fd are not important, as far at it quacks like a suitable duck. kib: I do not see why this check is needed. I understand why do you want to make sure that fd is a… | |||||
Done Inline ActionsExcessive () around arglen - 1 expression. kib: Excessive () around arglen - 1 expression. | |||||
rtld_die(); | |||||
} | |||||
i++; | |||||
fd = parse_integer(argv[i]); | |||||
Done Inline ActionsFor this, see D10750. kib: For this, see D10750. | |||||
if (fd == -1) { | |||||
_rtld_error("invalid file descriptor: '%s'", | |||||
argv[i]); | |||||
rtld_die(); | |||||
} | |||||
*fdp = fd; | |||||
break; | |||||
/* TODO: | |||||
Done Inline ActionsWhy do you use strn* functions ? Their use for anything but d_name in V7 struct dirent is plain bug, like all uses in the patch. kib: Why do you use strn* functions ? Their use for anything but d_name in V7 struct dirent is… | |||||
} else if (opt == 'p') { | |||||
*use_pathp = true; | |||||
*/ | |||||
Done Inline ActionsIn fact this stat is not needed, because caller does stat anyway, to check permissions. But the error message at that stat call failure probably needs adjustments when we stating not opened argv0 but fd. kib: In fact this stat is not needed, because caller does stat anyway, to check permissions. But… | |||||
Done Inline ActionsGood point... how does the new message ("failed to FD <fd> for <path>") look? It can either mean "failed to stat this FD that I opened for <path>" or "failed to stat this FD that you gave me for <path>"... I think maybe it works either way? jonathan: Good point... how does the new message ("failed to FD <fd> for <path>") look? It can either… | |||||
Done Inline ActionsIt might be less efforts to formulate something sound, and less confusing to user, if you create a boolean to track the source of fd, was it opened or obtained as the argument. Set it together with open() call. Then you can put specific message about fstat. Arguably strerror(errno) should be included into the message. kib: It might be less efforts to formulate something sound, and less confusing to user, if you… | |||||
Done Inline ActionsSure, I can do that. Also, it looks like I've been using tabs for all levels of indent rather than four spaces for second-level indents... I'll need to fix that too. jonathan: Sure, I can do that.
Also, it looks like I've been using tabs for all levels of indent rather… | |||||
} else { | |||||
rtld_printf("invalid argument: '%s'\n", arg); | |||||
print_usage(argv[0]); | |||||
rtld_die(); | |||||
} | |||||
} | |||||
} | |||||
return (i); | |||||
} | |||||
/* | |||||
* Parse a file descriptor number without pulling in more of libc (e.g. atoi). | * Parse a file descriptor number without pulling in more of libc (e.g. atoi). | ||||
*/ | */ | ||||
static int | static int | ||||
parse_integer(const char *str) | parse_integer(const char *str) | ||||
{ | { | ||||
static const int RADIX = 10; /* XXXJA: possibly support hex? */ | static const int RADIX = 10; /* XXXJA: possibly support hex? */ | ||||
const char *orig; | const char *orig; | ||||
int n; | int n; | ||||
char c; | char c; | ||||
orig = str; | orig = str; | ||||
n = 0; | n = 0; | ||||
for (c = *str; c != '\0'; c = *++str) { | for (c = *str; c != '\0'; c = *++str) { | ||||
if (c < '0' || c > '9') | if (c < '0' || c > '9') | ||||
return (-1); | return (-1); | ||||
n *= RADIX; | n *= RADIX; | ||||
n += c - '0'; | n += c - '0'; | ||||
} | } | ||||
/* Make sure we actually parsed something. */ | /* Make sure we actually parsed something. */ | ||||
if (str == orig) | if (str == orig) | ||||
Done Inline ActionsThis message is lost. Wouldn't it make sense to put it in the dirfd < 0 action above ? kib: This message is lost. Wouldn't it make sense to put it in the dirfd < 0 action above ? | |||||
Done Inline ActionsYes, that would make sense... I'll update the review once I've committed the parse_integer change. jonathan: Yes, that would make sense... I'll update the review once I've committed the `parse_integer`… | |||||
return (-1); | return (-1); | ||||
return (n); | return (n); | ||||
} | |||||
void print_usage(const char *argv0) | |||||
{ | |||||
rtld_printf("Usage: %s [-h] [-f <FD>] [--] <binary> [<args>]\n" | |||||
"\n" | |||||
"Options:\n" | |||||
" -h Display this help message\n" | |||||
/* TODO: " -p Search in PATH for named binary\n" */ | |||||
" -f <FD> Execute <FD> instead of searching for <binary>\n" | |||||
" -- End of RTLD options\n" | |||||
" <binary> Name of process to execute\n" | |||||
" <args> Arguments to the executed process\n", argv0); | |||||
} | } | ||||
/* | /* | ||||
* Overrides for libc_pic-provided functions. | * Overrides for libc_pic-provided functions. | ||||
*/ | */ | ||||
int | int | ||||
__getosreldate(void) | __getosreldate(void) | ||||
▲ Show 20 Lines • Show All 62 Lines • Show Last 20 Lines |
{} are not needed