Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108306668
D28883.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D28883.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D28883: Shared library randomization
Attached
Detach File
Event Timeline
Log In to Comment