Page MenuHomeFreeBSD

D28883.diff
No OneTemporary

D28883.diff

diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile
--- a/libexec/rtld-elf/Makefile
+++ b/libexec/rtld-elf/Makefile
@@ -90,6 +90,10 @@
${PROG_FULL}: ${VERSION_MAP}
.include <bsd.symver.mk>
+.if ${MK_SHLIBRANDOM} != "no"
+CFLAGS+= -DSHLIBRANDOM
+.endif
+
.if ${COMPILER_TYPE} == "gcc"
# GCC warns about redeclarations even though they have __exported
# and are therefore not identical to the ones from the system headers.
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -141,6 +141,10 @@
static int parse_integer(const char *);
static void *path_enumerate(const char *, path_enum_proc, const char *, void *);
static void print_usage(const char *argv0);
+static const char *printable_path(const char *);
+#if defined(SHLIBRANDOM)
+static void randomize_neededs(Obj_Entry *, int);
+#endif
static void release_object(Obj_Entry *);
static int relocate_object_dag(Obj_Entry *root, bool bind_now,
Obj_Entry *rtldobj, int flags, RtldLockState *lockstate);
@@ -2448,6 +2452,338 @@
return (0);
}
+#if defined(SHLIBRANDOM)
+#define MAX_SHL_SYMBOLS (16*1024)
+struct shl_symbol {
+ const char *name;
+ const char *lib_name;
+ uint32_t order;
+ Needed_Entry *need;
+};
+
+static int
+_symtab_cmp(void* v1, void* v2)
+{
+ struct shl_symbol *s1, *s2;
+ int ret;
+
+ s1 = v1;
+ s2 = v2;
+
+ ret = strcmp(s1->name, s2->name);
+ if (ret != 0)
+ return ret;
+
+ return (s1->order > s2->order);
+}
+
+static void
+_swap(void* v1, void* v2, int size)
+{
+ uint8_t buffer[size];
+
+ memcpy(buffer, v1, size);
+ memcpy(v1, v2, size);
+ memcpy(v2, buffer, size);
+}
+
+static void
+_qsort(void *v, int size, int left, int right, int (*comp)(void*, void*))
+{
+ void *vt, *v3;
+ int i, last, mid = (left + right) / 2;
+ if (left >= right)
+ return;
+
+ void *vl = (void*) ((uintptr_t)v + (left * size));
+ void *vr = (void*) ((uintptr_t)v + (mid * size));
+ _swap(vl, vr, size);
+ last = left;
+ for (i = left + 1; i <= right; i++) {
+ vt = (void*) ((uintptr_t)v + (i * size));
+ if ((*comp)(vl, vt) > 0) {
+ ++last;
+ v3 = (void*) ((uintptr_t)v + (last * size));
+ _swap(vt, v3, size);
+ }
+ }
+ v3 = (void*) ((uintptr_t)v + (last * size));
+ _swap(vl, v3, size);
+ _qsort(v, size, left, last - 1, comp);
+ _qsort(v, size, last + 1, right, comp);
+}
+
+/*
+ * Function checks if needed libraries can be safely randomized.
+ * It disables randomization if it detects problems.
+ * By default, neededs chain is divided inti critical and
+ * non-critical path. Critical-path must retain loading order.
+ *
+ * @return 1 if randomization can be done, 0 otherwise
+ */
+static int
+can_randomize(Obj_Entry *refobj, uint32_t nneeded, Needed_Entry **critpath, uint32_t *critpath_cnt,
+ Needed_Entry **otherneeded, uint32_t *otherneeded_cnt)
+{
+ Needed_Entry *need;
+ char *path;
+ int *fd = NULL;
+ Obj_Entry **obj = NULL;
+ struct stat sb;
+ int ret = 1;
+ uint32_t a, b;
+ struct shl_symbol *symtab = NULL;
+ uint32_t symtab_cnt = 0;
+ Needed_Entry **critpath_tmp = NULL;
+
+ /* Allocate helper structures */
+ obj = xcalloc(nneeded, sizeof(Obj_Entry));
+ if (obj == NULL) {
+ ret = 0;
+ goto exit1;
+ }
+ fd = xcalloc(nneeded, sizeof(int));
+ if (fd == NULL) {
+ ret = 0;
+ goto exit1;
+ }
+ for (a = 0; a < nneeded; a++)
+ fd[a] = -1;
+ symtab = xcalloc(MAX_SHL_SYMBOLS, sizeof(struct shl_symbol));
+ if (symtab == NULL) {
+ ret = 0;
+ goto exit1;
+ }
+ critpath_tmp = xcalloc(nneeded, sizeof(Needed_Entry *));
+ if (critpath_tmp == NULL) {
+ ret = 0;
+ goto exit1;
+ }
+
+ /* Map objects */
+ a = 0;
+ for (need = refobj->needed; need != NULL; need = need->next, a++) {
+ path = find_library(refobj->strtab + need->name, refobj, &fd[a]);
+
+ if (fstat(fd[a], &sb) == -1) {
+ ret = 0;
+ goto exit;
+ }
+
+ obj[a] = map_object(fd[a], printable_path(path), &sb);
+ if (obj[a] == NULL) {
+ ret = 0;
+ goto exit;
+ }
+ if (!digest_dynamic(obj[a], 0)) {
+ ret = 0;
+ goto exit;
+ }
+ }
+
+ /* Fill symtab */
+ a = 0;
+ for (need = refobj->needed; need != NULL; need = need->next, a++) {
+ const Elf_Sym *sym = obj[a]->symtab;
+ for (b = 0; b < obj[a]->dynsymcount; b++, sym++) {
+ if (sym->st_size == 0)
+ continue;
+
+ if (symtab_cnt == MAX_SHL_SYMBOLS) {
+ ret = 0;
+ if (getenv(_LD("SHLIBRANDOM_VERBOSE")))
+ rtld_printf("max symbols reached\n");
+ goto exit;
+ }
+ symtab[symtab_cnt].name = obj[a]->strtab + sym->st_name;
+ symtab[symtab_cnt].order = a;
+ symtab[symtab_cnt].need = need;
+ symtab[symtab_cnt].lib_name = refobj->strtab + need->name;
+ symtab_cnt++;
+ }
+ }
+
+ /* Sort symtab */
+ _qsort(symtab, sizeof(struct shl_symbol), 0, symtab_cnt - 1, _symtab_cmp);
+
+ /*
+ * Find duplicates
+ * Ignore duplicates in the same lib (might be different symbol version, WEAK attribute etc.).
+ */
+ for (a = 1; a < symtab_cnt; a++) {
+ if ((strcmp(symtab[a-1].name, symtab[a].name) == 0) &&
+ (strcmp(symtab[a-1].lib_name, symtab[a].lib_name) != 0)) {
+
+ if (getenv(_LD("SHLIBRANDOM_VERBOSE")))
+ rtld_printf("duplicate found %s:%d:(%s and %s)\n", symtab[a].name,
+ symtab[a].order, symtab[a - 1].lib_name, symtab[a].lib_name);
+
+ /* If collision, mark that current and previous order are on the critical path */
+ critpath_tmp[symtab[a].order] = symtab[a].need;
+ critpath_tmp[symtab[a - 1].order] = symtab[a - 1].need;
+ }
+ }
+
+ /* Fill critical and non-critical path */
+ *critpath_cnt = 0;
+ *otherneeded_cnt = 0;
+ a = 0;
+ for (need = refobj->needed; need != NULL; need = need->next, a++) {
+ if (critpath_tmp[a] != NULL) {
+ critpath[*critpath_cnt] = critpath_tmp[a];
+ *critpath_cnt = *critpath_cnt + 1;
+ } else {
+ otherneeded[*otherneeded_cnt] = need;
+ *otherneeded_cnt = *otherneeded_cnt + 1;
+ }
+ }
+
+ if (getenv(_LD("SHLIBRANDOM_VERBOSE"))) {
+ /* Dump critical path */
+ rtld_printf("CRITICAL PATH: ");
+ for (a = 0; a < *critpath_cnt; a++) {
+ rtld_printf("%s ", refobj->strtab + critpath[a]->name);
+ }
+ rtld_printf("\n");
+
+ /* Dump others */
+ rtld_printf("NON-CRITICAL PATH: ");
+ for (a = 0; a < *otherneeded_cnt; a++) {
+ rtld_printf("%s ", refobj->strtab + otherneeded[a]->name);
+ }
+ rtld_printf("\n");
+ }
+
+exit:
+ /* Unmap objects and free helper structures */
+ for (a = 0; a < nneeded; a++) {
+ if (obj[a] != NULL) {
+ munmap(obj[a]->mapbase, obj[a]->mapsize);
+ obj_free(obj[a]);
+ }
+ if (fd[a] != -1) {
+ close(fd[a]);
+ }
+ }
+
+exit1:
+ if (fd)
+ free(fd);
+ if (obj)
+ free(obj);
+ if (symtab)
+ free(symtab);
+ if (critpath_tmp)
+ free(critpath_tmp);
+
+ return (ret);
+}
+
+static void
+randomize_neededs(Obj_Entry *obj, int flags)
+{
+ Needed_Entry *need, **critpath=NULL, **noncritpath=NULL;
+ unsigned int i, j, k, nneed, critpath_cnt, noncritpath_cnt;
+ size_t sz = sizeof(unsigned int);
+ int mib[2];
+
+ if (!(obj->needed) || (flags & RTLD_LO_FILTEES))
+ return;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_ARND;
+
+ for (nneed = 0, need = obj->needed; need != NULL; need = need->next)
+ nneed++;
+
+ if (nneed < 2)
+ return;
+
+ critpath = xcalloc(nneed, sizeof(Needed_Entry *));
+ if (!critpath)
+ goto err;
+ noncritpath = xcalloc(nneed, sizeof(Needed_Entry *));
+ if (!noncritpath)
+ goto err;
+
+ if (can_randomize(obj, nneed, critpath, &critpath_cnt, noncritpath, &noncritpath_cnt) == 0) {
+ if (getenv(_LD("SHLIBRANDOM_VERBOSE")))
+ rtld_printf("randomization disabled for %d needed for object %s\n", nneed, obj->path);
+ goto err;
+ } else {
+ if (getenv(_LD("SHLIBRANDOM_VERBOSE")))
+ rtld_printf("randomization enabled for %d needed for object %s\n", nneed, obj->path);
+ }
+
+ /* Shuffle non-critical path neededs */
+ if (noncritpath_cnt < 2) {
+ /* do nothing */
+ } else if (noncritpath_cnt == 2) {
+ if (sysctl(mib, 2, &j, &sz, NULL, 0))
+ goto err;
+
+ if (j % 2) {
+ need = noncritpath[0];
+ noncritpath[0] = noncritpath[1];
+ noncritpath[1] = need;
+ }
+ } else {
+ for (i=0; i < noncritpath_cnt; i++) {
+ do {
+ if (sysctl(mib, 2, &j, &sz, NULL, 0))
+ goto err;
+
+ j %= noncritpath_cnt;
+ } while (j == i);
+
+ need = noncritpath[i];
+ noncritpath[i] = noncritpath[j];
+ noncritpath[j] = need;
+ }
+ }
+
+ /* Merge non-critical path entries to critical path */
+ for (i = 0; i < noncritpath_cnt; i++) {
+ /* Get a place where to put i-th non-critical path entry */
+ if (sysctl(mib, 2, &j, &sz, NULL, 0))
+ goto err;
+ j = j % (critpath_cnt + 1);
+
+ /* Move all data from j-th place to the right */
+ for (k = critpath_cnt; k > j; k--)
+ critpath[k] = critpath[k-1];
+ /* Store non-critical path object to critical path table */
+ critpath[j] = noncritpath[i];
+
+ /* Increase critical path length */
+ critpath_cnt++;
+ }
+
+ if (getenv(_LD("SHLIBRANDOM_VERBOSE"))) {
+ /* Dump critical path */
+ rtld_printf("RANDOMIZED NEEDEDs: ");
+ for (i = 0; i < critpath_cnt; i++) {
+ rtld_printf("%s ", obj->strtab + critpath[i]->name);
+ }
+ rtld_printf("\n");
+ }
+
+ for (i=0; i < critpath_cnt; i++)
+ critpath[i]->next = i + 1 < critpath_cnt ? critpath[i + 1] : NULL;
+
+ obj->needed = critpath[0];
+
+err:
+ if (critpath)
+ free(critpath);
+ if (noncritpath)
+ free(noncritpath);
+
+ return;
+}
+#endif
+
+
/*
* Given a shared object, traverse its list of needed objects, and load
* each of them. Returns 0 on success. Generates an error message and
@@ -2461,6 +2797,9 @@
for (obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) {
if (obj->marker)
continue;
+#if defined(SHLIBRANDOM)
+ randomize_neededs(obj, flags);
+#endif
if (process_needed(obj, obj->needed, flags) == -1)
return (-1);
}
diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk
--- a/share/mk/src.opts.mk
+++ b/share/mk/src.opts.mk
@@ -212,6 +212,7 @@
OPENLDAP \
REPRODUCIBLE_BUILD \
RPCBIND_WARMSTART_SUPPORT \
+ SHLIBRANDOM \
SORT_THREADS \
SVN \
ZONEINFO_LEAPSECONDS_SUPPORT \
diff --git a/tools/build/options/WITH_SHLIBRANDOM b/tools/build/options/WITH_SHLIBRANDOM
new file mode 100644
--- /dev/null
+++ b/tools/build/options/WITH_SHLIBRANDOM
@@ -0,0 +1 @@
+Enable randomizing the load order of shared objects.

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 24, 6:01 PM (20 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16094515
Default Alt Text
D28883.diff (9 KB)

Event Timeline