summaryrefslogtreecommitdiff
path: root/libexec
diff options
context:
space:
mode:
authorDale Rahn <drahn@cvs.openbsd.org>2006-06-15 22:09:33 +0000
committerDale Rahn <drahn@cvs.openbsd.org>2006-06-15 22:09:33 +0000
commit47d06606bbbf0d3ff167444e5d12359d03dc3a93 (patch)
tree7553ce24b3b1f9c08dc263570912d41658d6f1da /libexec
parenteb72c88524d7fac5c9a2ccb8306990a74581b569 (diff)
When writing a TXTBUSY file, make certain to preserve all file metadata,
also add -S flag, like install's -S flag to safely perform operations. Getting it in so it gets tested.
Diffstat (limited to 'libexec')
-rw-r--r--libexec/ld.so/ldconfig/ldconfig.89
-rw-r--r--libexec/ld.so/ldconfig/ldconfig.c10
-rw-r--r--libexec/ld.so/ldconfig/prebind.c319
-rw-r--r--libexec/ld.so/ldconfig/prebind_delete.c72
-rw-r--r--libexec/ld.so/ldconfig/prebind_struct.h3
5 files changed, 296 insertions, 117 deletions
diff --git a/libexec/ld.so/ldconfig/ldconfig.8 b/libexec/ld.so/ldconfig/ldconfig.8
index e6e6061737b..fb85889d996 100644
--- a/libexec/ld.so/ldconfig/ldconfig.8
+++ b/libexec/ld.so/ldconfig/ldconfig.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ldconfig.8,v 1.20 2006/05/12 23:23:20 deraadt Exp $
+.\" $OpenBSD: ldconfig.8,v 1.21 2006/06/15 22:09:32 drahn Exp $
.\"
.\" Copyright (c) 1993,1995 Paul Kranenburg
.\" All rights reserved.
@@ -116,6 +116,11 @@ The hints file will not be modified.
Do not scan the built-in system directory
.Pq Dq /usr/lib
for shared libraries.
+.It Fl S
+Perform prelinking operations in a safe mode, always copies the binaries
+when prelinking data is added to a file. Use to elminate the possibility
+that ETXTBUSY will occur when attempting to run a binary while prelinking
+is running.
.It Fl U
Unconfigure directories specified on the command line or remove inaccessible
directories from search path if no directories specified.
@@ -135,7 +140,7 @@ program startup can be significantly improved because
.Xr ld.so 1
can initialize the shared library environment much faster.
Prebinding information adds a small amount of data to the end of each
-specified library and program.
+specified program and associated shared libraries.
.Sh SECURITY
Special care must be taken when loading shared libraries into the address
space of
diff --git a/libexec/ld.so/ldconfig/ldconfig.c b/libexec/ld.so/ldconfig/ldconfig.c
index 7b91fd726b6..6ec779f4913 100644
--- a/libexec/ld.so/ldconfig/ldconfig.c
+++ b/libexec/ld.so/ldconfig/ldconfig.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ldconfig.c,v 1.22 2006/05/18 17:00:06 deraadt Exp $ */
+/* $OpenBSD: ldconfig.c,v 1.23 2006/06/15 22:09:32 drahn Exp $ */
/*
* Copyright (c) 1993,1995 Paul Kranenburg
@@ -64,6 +64,7 @@ static int doprebind;
static int nostd;
static int justread;
int merge;
+int safe;
static int rescan;
static int unconfig;
@@ -91,7 +92,7 @@ void
usage(void)
{
fprintf(stderr,
- "usage: %s [-DmPrRsUv] [path ...]\n", __progname);
+ "usage: %s [-DmPrRsSUv] [path ...]\n", __progname);
exit(1);
}
@@ -101,7 +102,7 @@ main(int argc, char *argv[])
int i, c;
int rval = 0;
- while ((c = getopt(argc, argv, "DmPrRsUv")) != -1) {
+ while ((c = getopt(argc, argv, "DmPrRsSUv")) != -1) {
switch (c) {
case 'R':
rescan = 1;
@@ -118,6 +119,9 @@ main(int argc, char *argv[])
case 's':
nostd = 1;
break;
+ case 'S':
+ safe = 1;
+ break;
case 'v':
verbose = 1;
break;
diff --git a/libexec/ld.so/ldconfig/prebind.c b/libexec/ld.so/ldconfig/prebind.c
index ce08c3f36a3..3cafd117212 100644
--- a/libexec/ld.so/ldconfig/prebind.c
+++ b/libexec/ld.so/ldconfig/prebind.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: prebind.c,v 1.5 2006/05/18 17:00:06 deraadt Exp $ */
+/* $OpenBSD: prebind.c,v 1.6 2006/06/15 22:09:32 drahn Exp $ */
/*
* Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
*
@@ -71,6 +71,8 @@ char *shstrtab;
#define RELOC_JMP_SLOT 21
#endif
+#define BUFSZ (256 * 1024)
+
#include "prebind_struct.h"
struct proglist *curbin;
@@ -94,10 +96,28 @@ struct objarray_list {
TAILQ_HEAD(, objlist) inst_list;
} *objarray;
+
+struct prebind_info {
+ struct elf_object *object;
+ struct prebind_footer *footer;
+ u_int32_t footer_offset;
+ u_int32_t nfixup;
+ struct nameidx *nameidx;
+ struct symcachetab *symcache;
+ struct symcachetab *pltsymcache;
+ u_int32_t *fixuptab;
+ u_int32_t *fixupcnt;
+ struct fixup **fixup;
+ u_int32_t *maptab;
+ u_int32_t **libmap;
+ u_int32_t *libmapcnt;
+ char *nametab;
+ u_int32_t nametablen;
+};
+
int objarray_cnt;
int objarray_sz;
-int write_txtbusy_file(char *name);
void copy_oldsymcache(int objidx, void *prebind_data);
void elf_load_existing_prebind(struct elf_object *object, int fd);
@@ -128,8 +148,9 @@ int elf_prep_lib_prebind(struct elf_object *object);
int elf_prep_bin_prebind(struct proglist *pl);
void add_fixup_prog(struct elf_object *prog, struct elf_object *obj, int idx,
const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag);
-void add_fixup_oldprog(struct elf_object *prog, struct elf_object *obj, int idx,
- const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag);
+void add_fixup_oldprog(struct elf_object *prog, struct elf_object *obj,
+ int idx, const struct elf_object *ref_obj, const Elf_Sym *ref_sym,
+ int flag);
void elf_dump_footer(struct prebind_footer *footer);
@@ -149,6 +170,8 @@ int elf_find_symbol_obj(elf_object_t *object, const char *name,
unsigned long hash, int flags, const Elf_Sym **this,
const Elf_Sym **weak_sym, elf_object_t **weak_object);
+int prebind_writefile(int fd, struct prebind_info *info);
+
struct elf_object *load_object;
struct elf_object *load_file(const char *filename, int lib);
@@ -173,7 +196,6 @@ prebind(char **argv)
}
elf_sum_reloc();
- printf("total new blocks %lld\n", prebind_blocks);
return (0);
}
@@ -1657,10 +1679,8 @@ elf_prep_bin_prebind(struct proglist *pl)
free(nameidx);
free(nametab);
free(libmap);
- return ret;
}
-int64_t prebind_blocks;
int
elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
@@ -1670,44 +1690,52 @@ elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
struct symcachetab *symcachetab, int symcache_cnt,
struct symcachetab *pltsymcachetab, int pltsymcache_cnt)
{
+ struct prebind_footer footer;
+ struct prebind_info info;
u_int32_t footer_offset, *maptab = NULL;
u_int32_t next_start, *fixuptab = NULL;
- struct prebind_footer footer;
struct stat ifstat;
off_t base_offset;
size_t len;
- int fd, i;
+ int fd = -1, i;
+ int readonly = 0;
- /* open the file */
- fd = open(object->load_name, O_RDWR);
+ /* open the file, if in safe mode, only open it readonly */
+ if (safe == 0)
+ fd = open(object->load_name, O_RDWR);
if (fd == -1) {
- if (errno == ETXTBSY)
- fd = write_txtbusy_file(object->load_name);
+ if (safe != 0 || errno == ETXTBSY)
+ fd = open(object->load_name, O_RDONLY);
if (fd == -1) {
perror(object->load_name);
return 1;
}
+ readonly = 1;
}
lseek(fd, -((off_t)sizeof(struct prebind_footer)), SEEK_END);
len = read(fd, &footer, sizeof(struct prebind_footer));
+ if (fstat(fd, &ifstat) == -1) {
+ perror(object->load_name);
+ exit(10);
+ }
+
if (footer.bind_id[0] == BIND_ID0 &&
footer.bind_id[1] == BIND_ID1 &&
footer.bind_id[2] == BIND_ID2 &&
- footer.bind_id[3] == BIND_ID3) {
+ footer.bind_id[3] == BIND_ID3 &&
+ readonly == 0) {
ftruncate(fd, footer.orig_size);
elf_clear_prog_load(fd, object);
- }
- if (fstat(fd, &ifstat) == -1) {
- perror(object->load_name);
- exit(10);
+ base_offset = footer.orig_size;
+ } else {
+ base_offset = ifstat.st_size;
}
+
bzero(&footer, sizeof(struct prebind_footer));
- base_offset = ifstat.st_size;
- prebind_blocks -= ifstat.st_blocks; /* subtract old size */
/* verify dev/inode - do we care about last modified? */
@@ -1755,8 +1783,8 @@ elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
maptab[i] = next_start;
next_start += libmapcnt[i] * sizeof(u_int32_t);
}
-
}
+
footer.nametab_idx = next_start;
next_start += nametablen;
next_start = ELF_ROUND(next_start, sizeof(u_int64_t));
@@ -1774,56 +1802,41 @@ elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
footer.bind_id[2] = BIND_ID2;
footer.bind_id[3] = BIND_ID3;
- lseek(fd, footer.prebind_base, SEEK_SET);
- write(fd, &footer_offset, sizeof(u_int32_t));
- lseek(fd, footer.prebind_base+footer.nameidx_idx, SEEK_SET);
- write(fd, nameidx, numlibs * sizeof (struct nameidx));
+ info.object = object;
+ info.footer = &footer;
+ info.footer_offset = footer_offset;
+ info.nameidx = nameidx;
+ info.symcache = symcachetab;
- lseek(fd, footer.prebind_base+footer.symcache_idx, SEEK_SET);
- write(fd, symcachetab, symcache_cnt * sizeof (struct symcachetab));
- lseek(fd, footer.prebind_base+footer.pltsymcache_idx, SEEK_SET);
- write(fd, pltsymcachetab, pltsymcache_cnt *
- sizeof (struct symcachetab));
+ info.pltsymcache = pltsymcachetab;
+ info.nfixup = nfixup;
+ if (nfixup != 0) {
+ info.fixuptab = fixuptab;
+ info.fixupcnt = fixupcnt;
+ info.fixup = fixup;
+ info.maptab = maptab;
+ info.libmap = libmap;
+ info.libmapcnt = libmapcnt;
+ }
- if (verbose > 3)
- dump_symcachetab(symcachetab, symcache_cnt, object, 0);
- if (verbose > 3)
- dump_symcachetab(pltsymcachetab, pltsymcache_cnt, object, 0);
+ info.nametab = nametab;
+ info.nametablen = nametablen;
- if (nfixup != 0) {
- lseek(fd, footer.prebind_base+footer.fixup_idx, SEEK_SET);
- write(fd, fixuptab, 2*nfixup * sizeof(u_int32_t));
- lseek(fd, footer.prebind_base+footer.fixupcnt_idx, SEEK_SET);
- write(fd, fixupcnt, 2*nfixup * sizeof(u_int32_t));
- for (i = 0; i < 2*nfixup; i++) {
- lseek(fd, footer.prebind_base+fixuptab[i],
- SEEK_SET);
- write(fd, fixup[i], fixupcnt[i] * sizeof(struct fixup));
- }
- lseek(fd, footer.prebind_base+footer.libmap_idx, SEEK_SET);
- write(fd, maptab, nfixup * sizeof(u_int32_t));
- for (i = 0; i < nfixup; i++) {
- lseek(fd, footer.prebind_base+maptab[i],
- SEEK_SET);
- write(fd, libmap[i], libmapcnt[i] * sizeof(u_int32_t));
- }
+ if (readonly) {
+ prebind_writenewfile(fd, object->load_name, ifstat,
+ footer.orig_size, &info);
+ } else {
+ prebind_writefile(fd, &info);
}
- lseek(fd, footer.prebind_base+footer.nametab_idx, SEEK_SET);
- write(fd, nametab, nametablen);
- lseek(fd, footer.prebind_base+footer_offset, SEEK_SET);
- write(fd, &footer, sizeof (struct prebind_footer));
if (fstat(fd, &ifstat) == -1) {
perror(object->load_name);
exit(10);
}
- prebind_blocks += ifstat.st_blocks; /* add new size */
if (nfixup != 0) {
- elf_fixup_prog_load(fd, &footer, object);
-
free(fixuptab);
free(maptab);
}
@@ -1839,6 +1852,145 @@ elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
close(fd);
return 0;
}
+
+int
+prebind_writefile(int fd, struct prebind_info *info)
+{
+ int i;
+
+ struct prebind_footer *footer = info->footer;
+
+ lseek(fd, footer->prebind_base, SEEK_SET);
+ write(fd, &info->footer_offset, sizeof(u_int32_t));
+
+ lseek(fd, footer->prebind_base+footer->nameidx_idx, SEEK_SET);
+ write(fd, info->nameidx, footer->numlibs * sizeof (struct nameidx));
+
+ lseek(fd, footer->prebind_base+footer->symcache_idx, SEEK_SET);
+ write(fd, info->symcache, footer->symcache_cnt *
+ sizeof (struct symcachetab));
+
+ lseek(fd, footer->prebind_base+footer->pltsymcache_idx, SEEK_SET);
+ write(fd, info->pltsymcache, footer->pltsymcache_cnt *
+ sizeof (struct symcachetab));
+
+ if (info->nfixup != 0) {
+ lseek(fd, footer->prebind_base+footer->fixup_idx, SEEK_SET);
+ write(fd, info->fixuptab, 2*info->nfixup * sizeof(u_int32_t));
+ lseek(fd, footer->prebind_base+footer->fixupcnt_idx, SEEK_SET);
+ write(fd, info->fixupcnt, 2*info->nfixup * sizeof(u_int32_t));
+ for (i = 0; i < 2*info->nfixup; i++) {
+ lseek(fd, footer->prebind_base+info->fixuptab[i],
+ SEEK_SET);
+ write(fd, info->fixup[i], info->fixupcnt[i] *
+ sizeof(struct fixup));
+ }
+
+ lseek(fd, footer->prebind_base+footer->libmap_idx, SEEK_SET);
+ write(fd, info->maptab, info->nfixup * sizeof(u_int32_t));
+ for (i = 0; i < info->nfixup; i++) {
+ lseek(fd, footer->prebind_base+info->maptab[i],
+ SEEK_SET);
+ write(fd, info->libmap[i], info->libmapcnt[i] *
+ sizeof(u_int32_t));
+ }
+ }
+ lseek(fd, footer->prebind_base+footer->nametab_idx, SEEK_SET);
+ write(fd, info->nametab, info->nametablen);
+
+ lseek(fd, footer->prebind_base+info->footer_offset, SEEK_SET);
+ write(fd, footer, sizeof (struct prebind_footer));
+
+ if (info->object->obj_type == OBJTYPE_EXE)
+ elf_fixup_prog_load(fd, info->footer, info->object);
+
+}
+
+int
+prebind_writenewfile(int infd, char *name, struct stat *st, off_t orig_size,
+ struct prebind_info *info)
+{
+ struct timeval tv[2];
+ char *newname, *buf;
+ ssize_t len, wlen;
+ int outfd;
+
+ if (asprintf(&newname, "%s.XXXXXXXXXX", name) == -1) {
+ if (verbose)
+ warn("asprintf");
+ return (-1);
+ }
+ outfd = open(newname, O_CREAT|O_RDWR|O_TRUNC, 0600);
+ if (outfd == -1) {
+ warn("%s", newname);
+ free(newname);
+ return (-1);
+ }
+
+ buf = malloc(BUFSZ);
+ if (buf == NULL) {
+ if (verbose)
+ warn("malloc");
+ goto fail;
+ }
+
+ /* copy old file to new file */
+ lseek(infd, (off_t)0, SEEK_SET);
+ while (1) {
+ len = read(infd, buf, BUFSIZ);
+ if (len == -1) {
+ if (verbose)
+ warn("read");
+ free(buf);
+ goto fail;
+ }
+ if (len == 0)
+ break;
+ wlen = write(outfd, buf, len);
+ if (wlen != len) {
+ free(buf);
+ goto fail;
+ }
+ }
+ free(buf);
+
+ /* now back track, and delete the header */
+ if (prebind_remove_load_section(outfd, newname) == -1)
+ goto fail;
+ if (orig_size != (off_t)-1 &&
+ ftruncate(outfd, orig_size) == -1)
+ goto fail;
+
+ prebind_writefile(outfd, info);
+
+ /* move new file into place */
+ TIMESPEC_TO_TIMEVAL(&tv[0], &st->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &st->st_mtimespec);
+ if (futimes(outfd, tv) == -1)
+ goto fail;
+ if (fchown(outfd, st->st_uid, st->st_gid) == -1)
+ goto fail;
+ if (fchmod(outfd, st->st_mode) == -1)
+ goto fail;
+ if (fchflags(outfd, st->st_flags) == -1)
+ goto fail;
+ if (fstat(outfd, st) == -1) {
+ /* XXX */
+ goto fail;
+ }
+ if (rename(newname, name) == -1)
+ goto fail;
+
+ close (outfd);
+ return (0);
+
+fail:
+ free(newname);
+ unlink(newname);
+ close(outfd);
+ return (-1);
+}
+
void
elf_fixup_prog_load(int fd, struct prebind_footer *footer,
struct elf_object *object)
@@ -2013,57 +2165,6 @@ elf_print_objarray(void)
}
}
-int
-write_txtbusy_file(char *name)
-{
- char *prebind_name;
- int fd;
- int oldfd;
- int err;
- struct stat sb;
- void *buf;
- size_t len, wlen;
-
- err = lstat(name, &sb); /* get mode of old file (preserve mode) */
- if (err != 0)
- return -1; /* stat shouldn't fail but if it does */
-
- /* pick a better filename (pulling apart string?) */
- err = asprintf(&prebind_name, "%s%s", name, ".prebXXXXXXXXXX");
- if (err == -1) {
- /* fail */
- exit(10); /* bail on memory failure */
- }
- mkstemp(prebind_name);
-
- /* allocate a 256k buffer to copy the file */
-#define BUFSZ (256 * 1024)
- buf = xmalloc(BUFSZ);
-
- fd = open(prebind_name, O_RDWR|O_CREAT|O_TRUNC, sb.st_mode);
- oldfd = open(name, O_RDONLY);
- while ((len = read(oldfd, buf, BUFSZ)) > 0) {
- wlen = write(fd, buf, len);
- if (wlen != len) {
- /* write failed */
- close(fd);
- close(oldfd);
- unlink(prebind_name);
- free(buf);
- return -1;
- }
- }
-
- /* this mode is used above, but is modified by umask */
- chmod(prebind_name, sb.st_mode);
- close(oldfd);
- unlink(name);
- rename(prebind_name, name);
- free(buf);
-
- return fd;
-}
-
void
elf_load_existing_prebind(struct elf_object *object, int fd)
{
diff --git a/libexec/ld.so/ldconfig/prebind_delete.c b/libexec/ld.so/ldconfig/prebind_delete.c
index 0c07e4a96e6..c009d0befa2 100644
--- a/libexec/ld.so/ldconfig/prebind_delete.c
+++ b/libexec/ld.so/ldconfig/prebind_delete.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: prebind_delete.c,v 1.5 2006/05/17 02:59:08 deraadt Exp $ */
+/* $OpenBSD: prebind_delete.c,v 1.6 2006/06/15 22:09:32 drahn Exp $ */
/*
* Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
+#include <dirent.h>
#include <errno.h>
#include "prebind.h"
@@ -41,7 +42,7 @@ int
prebind_delete(char **argv)
{
while (*argv) {
- if (strip_prebind(*argv) == -1)
+ if (strip_file_or_dir(*argv) == -1)
return (1);
argv++;
}
@@ -49,6 +50,73 @@ prebind_delete(char **argv)
}
int
+strip_file_or_dir(char *name)
+{
+ struct stat sb;
+ int ret = -1;
+
+ ret = lstat(name, &sb);
+ if (ret != 0)
+ return;
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFREG:
+ ret = strip_prebind(name);
+ break;
+ case S_IFDIR:
+ if (verbose > 0)
+ printf("loading dir %s\n", name);
+ ret = strip_dir(name);
+ break;
+ default:
+ ; /* links and other files we skip */
+ }
+ return -1;
+}
+
+int
+strip_dir(char *dir)
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ char *buf;
+ int ret;
+
+ dirp = opendir(dir);
+
+ /* if dir failes to open, skip */
+ if (dirp == NULL)
+ return;
+
+ while ((dp = readdir(dirp)) != NULL && ret != -1) {
+ ret = -1;
+ switch (dp->d_type) {
+ case DT_UNKNOWN:
+ /*
+ * NFS will return unknown, since load_file
+ * does stat the file, this just
+ */
+ asprintf(&buf, "%s/%s", dir, dp->d_name);
+ lstat(buf, &sb);
+ if (sb.st_mode == S_IFREG)
+ ret = strip_prebind(buf);
+ free(buf);
+ break;
+ case DT_REG:
+ asprintf(&buf, "%s/%s", dir, dp->d_name);
+ ret = strip_prebind(buf);
+ free(buf);
+ break;
+ default:
+ /* other files symlinks, dirs, ... we ignore */
+ ret = 0;
+ ;
+ }
+ }
+ return ret;
+}
+
+int
strip_prebind(char *file)
{
struct prebind_footer footer;
diff --git a/libexec/ld.so/ldconfig/prebind_struct.h b/libexec/ld.so/ldconfig/prebind_struct.h
index 47fab74a8ad..0b5dfe7365b 100644
--- a/libexec/ld.so/ldconfig/prebind_struct.h
+++ b/libexec/ld.so/ldconfig/prebind_struct.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: prebind_struct.h,v 1.2 2006/05/13 05:59:28 deraadt Exp $ */
+/* $OpenBSD: prebind_struct.h,v 1.3 2006/06/15 22:09:32 drahn Exp $ */
/*
* Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
*
@@ -76,6 +76,7 @@ void elf_dump_footer(struct prebind_footer *footer);
extern int verbose;
extern int merge;
+extern int safe;
extern int64_t prebind_blocks;
extern struct elf_object *load_object;