summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2016-03-22 22:51:09 +0000
committerPatrick Mooney <pmooney@pfmooney.com>2016-03-23 19:38:41 +0000
commitfcc3d074e6a2ad9529ce014e16d99720f33a053d (patch)
tree4a1962c1cccd07dd28ef33a85585b3a0c00f8bfd
parentc11422a43e3c04c55f76ea9544fd8650f35c3309 (diff)
downloadillumos-joyent-fcc3d074e6a2ad9529ce014e16d99720f33a053d.tar.gz
OS-5259 lxbrand mmap(2) should heed personality
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Cody Mello <melloc@joyent.com>
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_brand.c4
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/mem.c15
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/misc.c55
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h1
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_brand.c37
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_syscall.c4
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_proc.h2
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prvnops.c36
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_brand.h23
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_syscalls.h1
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_personality.c112
-rw-r--r--usr/src/uts/intel/Makefile.files1
12 files changed, 220 insertions, 71 deletions
diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
index 87d89cf34f..e7d5ddccc3 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
@@ -1098,7 +1098,7 @@ static lx_syscall_handler_t lx_handlers[] = {
lx_utime, /* 132: utime */
lx_mknod, /* 133: mknod */
NULL, /* 134: uselib */
- lx_personality, /* 135: personality */
+ NULL, /* 135: personality */
NULL, /* 136: ustat */
lx_statfs, /* 137: statfs */
lx_fstatfs, /* 138: fstatfs */
@@ -1430,7 +1430,7 @@ static lx_syscall_handler_t lx_handlers[] = {
lx_fchdir, /* 133: fchdir */
NULL, /* 134: bdflush */
lx_sysfs, /* 135: sysfs */
- lx_personality, /* 136: personality */
+ NULL, /* 136: personality */
NULL, /* 137: afs_syscall */
lx_setfsuid16, /* 138: setfsuid16 */
lx_setfsgid16, /* 139: setfsgid16 */
diff --git a/usr/src/lib/brand/lx/lx_brand/common/mem.c b/usr/src/lib/brand/lx/lx_brand/common/mem.c
index c84a07d76d..234348753f 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/mem.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/mem.c
@@ -139,9 +139,20 @@ mmap_common(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
* show that segments mmap'd from userland (such as libraries mapped in
* by the dynamic linker) all have exec the permission set, even for
* data segments.
+ *
+ * This insanity is tempered by the fact that the behavior is disabled
+ * for ELF binaries bearing a PT_GNU_STACK header which lacks PF_X
+ * (which most do). Such a header will clear the READ_IMPLIES_EXEC
+ * flag from the process personality.
*/
- if (prot & PROT_READ)
- prot |= PROT_EXEC;
+ if (prot & PROT_READ) {
+ unsigned int personality;
+
+ personality = syscall(SYS_brand, B_GET_PERSONALITY);
+ if ((personality & LX_PER_READ_IMPLIES_EXEC) != 0) {
+ prot |= PROT_EXEC;
+ }
+ }
ret = mmap64(addr, len, prot, ltos_mmap_flags(flags), fd, off);
diff --git a/usr/src/lib/brand/lx/lx_brand/common/misc.c b/usr/src/lib/brand/lx/lx_brand/common/misc.c
index ed4f812973..5fd637babe 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/misc.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/misc.c
@@ -304,61 +304,6 @@ lx_setgroups16(uintptr_t p1, uintptr_t p2)
}
/*
- * personality(). We don't really support Linux personalities, but we have to
- * emulate enough (or ahem, lie) to show that we support the basic personality.
- * We also allow certain (relatively) harmless bits of the personality to be
- * "set" -- keeping track of whatever lie we're telling so we don't get caught
- * out too easily.
- */
-#define LX_PER_LINUX 0x0
-#define LX_PER_MASK 0xff
-
-/*
- * These are for what Linux calls "bug emulation".
- */
-#define LX_PER_UNAME26 0x0020000
-#define LX_PER_ADDR_NO_RANDOMIZE 0x0040000
-#define LX_PER_FDPIC_FUNCPTRS 0x0080000
-#define LX_PER_MMAP_PAGE_ZERO 0x0100000
-#define LX_PER_ADDR_COMPAT_LAYOUT 0x0200000
-#define LX_PER_READ_IMPLIES_EXEC 0x0400000
-#define LX_PER_ADDR_LIMIT_32BIT 0x0800000
-#define LX_PER_SHORT_INODE 0x1000000
-#define LX_PER_WHOLE_SECONDS 0x2000000
-#define LX_PER_STICKY_TIMEOUTS 0x4000000
-#define LX_PER_ADDR_LIMIT_3GB 0x8000000
-
-long
-lx_personality(uintptr_t p1)
-{
- static int current = LX_PER_LINUX;
- int per = (int)p1;
-
- switch (per) {
- case -1:
- /* Request current personality */
- return (current);
- case LX_PER_LINUX:
- current = per;
- return (0);
- default:
- if (per & LX_PER_MASK)
- return (-EINVAL);
-
- /*
- * We allow a subset of the legacy emulation personality
- * attributes to be "turned on" -- which we put in quotes
- * because we don't actually change our behavior based on
- * them. (Note that we silently ignore the others.)
- */
- current = per & (LX_PER_ADDR_LIMIT_3GB |
- LX_PER_ADDR_NO_RANDOMIZE | LX_PER_ADDR_COMPAT_LAYOUT);
-
- return (0);
- }
-}
-
-/*
* mknod() - Since we don't have the SYS_CONFIG privilege within a zone, the
* only mode we have to support is S_IFIFO. We also have to distinguish between
* an invalid type and insufficient privileges.
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
index 312e040059..5c9c0d9d7f 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
@@ -148,7 +148,6 @@ extern long lx_reboot(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern long lx_getgroups16(uintptr_t, uintptr_t);
extern long lx_setgroups(uintptr_t, uintptr_t);
extern long lx_setgroups16(uintptr_t, uintptr_t);
-extern long lx_personality(uintptr_t);
extern long lx_query_module(uintptr_t, uintptr_t, uintptr_t, uintptr_t,
uintptr_t);
diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c
index aed3585f55..bd41ad80d5 100644
--- a/usr/src/uts/common/brand/lx/os/lx_brand.c
+++ b/usr/src/uts/common/brand/lx/os/lx_brand.c
@@ -1834,6 +1834,16 @@ lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
return (EINVAL);
}
+ case B_GET_PERSONALITY: {
+ unsigned int result;
+
+ mutex_enter(&p->p_lock);
+ pd = ptolxproc(p);
+ result = pd->l_personality;
+ mutex_exit(&p->p_lock);
+ return (result);
+ }
+
}
return (EINVAL);
@@ -1984,26 +1994,26 @@ lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
struct intpdata *idata, int level, long *execsz, int setid,
caddr_t exec_file, struct cred *cred, int *brand_action)
{
- int error;
+ int error, i;
vnode_t *nvp;
Ehdr ehdr;
Addr uphdr_vaddr;
intptr_t voffset;
char *interp = NULL;
uintptr_t ldaddr = NULL;
- int i;
proc_t *p = ttoproc(curthread);
klwp_t *lwp = ttolwp(curthread);
- struct execenv env;
- struct execenv origenv;
+ lx_proc_data_t *lxpd = ptolxproc(p);
+ struct execenv env, origenv;
stack_t orig_sigaltstack;
struct user *up = PTOU(ttoproc(curthread));
lx_elf_data_t edp;
char *lib_path = LX_LIB_PATH;
boolean_t execstk = B_TRUE;
+ unsigned int personality;
- ASSERT(ttoproc(curthread)->p_brand == &lx_brand);
- ASSERT(ttoproc(curthread)->p_brand_data != NULL);
+ ASSERT(p->p_brand == &lx_brand);
+ ASSERT(lxpd != NULL);
/*
* Start with a separate struct for ELF data instead of inheriting
@@ -2103,12 +2113,19 @@ lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
#endif
/*
+ * Revert the base personality while maintaining any existing flags.
+ */
+ personality = LX_PER_LINUX | (lxpd->l_personality & ~LX_PER_MASK);
+
+ /*
* Linux defaults to an executable stack unless the aformentioned
- * PT_GNU_STACK entry in the elf header dictates otherwise.
+ * PT_GNU_STACK entry in the elf header dictates otherwise. Enabling
+ * the READ_IMPLIES_EXEC personality flag is also implied in this case.
*/
if (execstk) {
args->stk_prot |= PROT_EXEC;
args->stk_prot_override = B_TRUE;
+ personality |= LX_PER_READ_IMPLIES_EXEC;
}
/*
@@ -2420,9 +2437,11 @@ lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
}
/*
- * Record the brand ELF data now that the exec is a success.
+ * Record the brand ELF data and new personality now that the exec has
+ * proceeded successfully.
*/
- bcopy(&edp, &ttolxproc(curthread)->l_elf_data, sizeof (edp));
+ bcopy(&edp, &lxpd->l_elf_data, sizeof (edp));
+ lxpd->l_personality = personality;
return (0);
}
diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c
index 1423ca15aa..5e0f03cc5c 100644
--- a/usr/src/uts/common/brand/lx/os/lx_syscall.c
+++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c
@@ -752,7 +752,7 @@ lx_sysent_t lx_sysent32[] = {
{"fchdir", NULL, 0, 1}, /* 133 */
{"bdflush", NULL, NOSYS_KERNEL, 0}, /* 134 */
{"sysfs", NULL, 0, 3}, /* 135 */
- {"personality", NULL, 0, 1}, /* 136 */
+ {"personality", lx_personality, 0, 1}, /* 136 */
{"afs_syscall", NULL, NOSYS_KERNEL, 0}, /* 137 */
{"setfsuid16", NULL, 0, 1}, /* 138 */
{"setfsgid16", NULL, 0, 1}, /* 139 */
@@ -1122,7 +1122,7 @@ lx_sysent_t lx_sysent64[] = {
{"utime", NULL, 0, 2}, /* 132 */
{"mknod", NULL, 0, 3}, /* 133 */
{"uselib", NULL, NOSYS_KERNEL, 0}, /* 134 */
- {"personality", NULL, 0, 1}, /* 135 */
+ {"personality", lx_personality, 0, 1}, /* 135 */
{"ustat", NULL, NOSYS_OBSOLETE, 2}, /* 136 */
{"statfs", NULL, 0, 2}, /* 137 */
{"fstatfs", NULL, 0, 2}, /* 138 */
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_proc.h b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
index cd4d440450..471d7c0fdd 100644
--- a/usr/src/uts/common/brand/lx/procfs/lx_proc.h
+++ b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
@@ -123,6 +123,7 @@ typedef enum lxpr_nodetype {
LXPR_PID_MEM, /* /proc/<pid>/mem */
LXPR_PID_MOUNTINFO, /* /proc/<pid>/mountinfo */
LXPR_PID_OOM_SCR_ADJ, /* /proc/<pid>/oom_score_adj */
+ LXPR_PID_PERSONALITY, /* /proc/<pid>/personality */
LXPR_PID_ROOTDIR, /* /proc/<pid>/root */
LXPR_PID_STAT, /* /proc/<pid>/stat */
LXPR_PID_STATM, /* /proc/<pid>/statm */
@@ -144,6 +145,7 @@ typedef enum lxpr_nodetype {
LXPR_PID_TID_MEM, /* /proc/<pid>/task/<tid>/mem */
LXPR_PID_TID_MOUNTINFO, /* /proc/<pid>/task/<tid>/mountinfo */
LXPR_PID_TID_OOM_SCR_ADJ, /* /proc/<pid>/task/<tid>/oom_score_adj */
+ LXPR_PID_TID_PERSONALITY, /* /proc/<pid>/task/<tid>/personality */
LXPR_PID_TID_ROOTDIR, /* /proc/<pid>/task/<tid>/root */
LXPR_PID_TID_STAT, /* /proc/<pid>/task/<tid>/stat */
LXPR_PID_TID_STATM, /* /proc/<pid>/task/<tid>/statm */
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
index 106c75e532..2040aadade 100644
--- a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
@@ -177,6 +177,7 @@ static void lxpr_read_pid_limits(lxpr_node_t *, lxpr_uiobuf_t *);
static void lxpr_read_pid_maps(lxpr_node_t *, lxpr_uiobuf_t *);
static void lxpr_read_pid_mountinfo(lxpr_node_t *, lxpr_uiobuf_t *);
static void lxpr_read_pid_oom_scr_adj(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_personality(lxpr_node_t *, lxpr_uiobuf_t *);
static void lxpr_read_pid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
static void lxpr_read_pid_statm(lxpr_node_t *, lxpr_uiobuf_t *);
static void lxpr_read_pid_status(lxpr_node_t *, lxpr_uiobuf_t *);
@@ -332,6 +333,7 @@ static lxpr_dirent_t piddir[] = {
{ LXPR_PID_MEM, "mem" },
{ LXPR_PID_MOUNTINFO, "mountinfo" },
{ LXPR_PID_OOM_SCR_ADJ, "oom_score_adj" },
+ { LXPR_PID_PERSONALITY, "personality" },
{ LXPR_PID_ROOTDIR, "root" },
{ LXPR_PID_STAT, "stat" },
{ LXPR_PID_STATM, "statm" },
@@ -359,6 +361,7 @@ static lxpr_dirent_t tiddir[] = {
{ LXPR_PID_MEM, "mem" },
{ LXPR_PID_MOUNTINFO, "mountinfo" },
{ LXPR_PID_TID_OOM_SCR_ADJ, "oom_score_adj" },
+ { LXPR_PID_PERSONALITY, "personality" },
{ LXPR_PID_ROOTDIR, "root" },
{ LXPR_PID_TID_STAT, "stat" },
{ LXPR_PID_STATM, "statm" },
@@ -633,6 +636,7 @@ static void (*lxpr_read_function[LXPR_NFILES])() = {
lxpr_read_empty, /* /proc/<pid>/mem */
lxpr_read_pid_mountinfo, /* /proc/<pid>/mountinfo */
lxpr_read_pid_oom_scr_adj, /* /proc/<pid>/oom_score_adj */
+ lxpr_read_pid_personality, /* /proc/<pid>/personality */
lxpr_read_invalid, /* /proc/<pid>/root */
lxpr_read_pid_stat, /* /proc/<pid>/stat */
lxpr_read_pid_statm, /* /proc/<pid>/statm */
@@ -654,6 +658,7 @@ static void (*lxpr_read_function[LXPR_NFILES])() = {
lxpr_read_empty, /* /proc/<pid>/task/<tid>/mem */
lxpr_read_pid_mountinfo, /* /proc/<pid>/task/<tid>/mountinfo */
lxpr_read_pid_oom_scr_adj, /* /proc/<pid>/task/<tid>/oom_scr_adj */
+ lxpr_read_pid_personality, /* /proc/<pid>/task/<tid>/personality */
lxpr_read_invalid, /* /proc/<pid>/task/<tid>/root */
lxpr_read_pid_tid_stat, /* /proc/<pid>/task/<tid>/stat */
lxpr_read_pid_statm, /* /proc/<pid>/task/<tid>/statm */
@@ -755,6 +760,7 @@ static vnode_t *(*lxpr_lookup_function[LXPR_NFILES])() = {
lxpr_lookup_not_a_dir, /* /proc/<pid>/mem */
lxpr_lookup_not_a_dir, /* /proc/<pid>/mountinfo */
lxpr_lookup_not_a_dir, /* /proc/<pid>/oom_score_adj */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/personality */
lxpr_lookup_not_a_dir, /* /proc/<pid>/root */
lxpr_lookup_not_a_dir, /* /proc/<pid>/stat */
lxpr_lookup_not_a_dir, /* /proc/<pid>/statm */
@@ -776,6 +782,7 @@ static vnode_t *(*lxpr_lookup_function[LXPR_NFILES])() = {
lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/mem */
lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/mountinfo */
lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/oom_scr_adj */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/personality */
lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/root */
lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/stat */
lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/statm */
@@ -877,6 +884,7 @@ static int (*lxpr_readdir_function[LXPR_NFILES])() = {
lxpr_readdir_not_a_dir, /* /proc/<pid>/mem */
lxpr_readdir_not_a_dir, /* /proc/<pid>/mountinfo */
lxpr_readdir_not_a_dir, /* /proc/<pid>/oom_score_adj */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/personality */
lxpr_readdir_not_a_dir, /* /proc/<pid>/root */
lxpr_readdir_not_a_dir, /* /proc/<pid>/stat */
lxpr_readdir_not_a_dir, /* /proc/<pid>/statm */
@@ -898,6 +906,7 @@ static int (*lxpr_readdir_function[LXPR_NFILES])() = {
lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/mem */
lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/mountinfo */
lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid/oom_scr_adj */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid/personality */
lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/root */
lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/stat */
lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/statm */
@@ -1816,6 +1825,33 @@ lxpr_read_pid_oom_scr_adj(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
lxpr_uiobuf_printf(uiobuf, "0\n");
}
+/*
+ * lxpr_read_pid_personality(): read personality for process
+ */
+static void
+lxpr_read_pid_personality(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *lxpd;
+ unsigned int personality;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_PERSONALITY);
+
+ p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ if ((lxpd = ptolxproc(p)) != NULL) {
+ personality = lxpd->l_personality;
+ } else {
+ /* Report native processes as having the SunOS personality */
+ personality = LX_PER_SUNOS;
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf, "%08x\n", personality);
+}
/*
* lxpr_read_pid_statm(): memory status file
diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h
index 01d47458c3..a7d87f70d8 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_brand.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h
@@ -104,6 +104,7 @@ extern "C" {
#define B_SIGEV_THREAD_ID 148
#define B_OVERRIDE_KERN_VER 149
#define B_NOTIFY_VDSO_LOC 150
+#define B_GET_PERSONALITY 151
#ifndef _ASM
/*
@@ -310,11 +311,33 @@ typedef struct lx_proc_data {
/* Override zone-wide settings for uname release and version */
char l_uname_release[LX_KERN_RELEASE_MAX];
char l_uname_version[LX_KERN_VERSION_MAX];
+
+ /* Linux process personality */
+ unsigned int l_personality;
} lx_proc_data_t;
#endif /* _KERNEL */
/*
+ * Linux process personality(2) flags stored in l_personality
+ */
+#define LX_PER_UNAME26 0x0020000
+#define LX_PER_ADDR_NO_RANDOMIZE 0x0040000
+#define LX_PER_FDPIC_FUNCPTRS 0x0080000
+#define LX_PER_MMAP_PAGE_ZERO 0x0100000
+#define LX_PER_ADDR_COMPAT_LAYOUT 0x0200000
+#define LX_PER_READ_IMPLIES_EXEC 0x0400000
+#define LX_PER_ADDR_LIMIT_32BIT 0x0800000
+#define LX_PER_SHORT_INODE 0x1000000
+#define LX_PER_WHOLE_SECONDS 0x2000000
+#define LX_PER_STICKY_TIMEOUTS 0x4000000
+#define LX_PER_ADDR_LIMIT_3GB 0x8000000
+
+#define LX_PER_LINUX 0x00
+#define LX_PER_SUNOS (0x06 | LX_PER_STICKY_TIMEOUTS)
+#define LX_PER_MASK 0xff
+
+/*
* A data type big enough to bitmap all Linux possible cpus.
* The bitmap size is defined as 1024 cpus in the Linux 2.4 and 2.6 man pages
* for sched_getaffinity() and sched_getaffinity().
diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
index 60ad1e7ef4..f932c70bb9 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
@@ -104,6 +104,7 @@ extern long lx_nanosleep();
extern long lx_oldgetrlimit();
extern long lx_open();
extern long lx_openat();
+extern long lx_personality();
extern long lx_pipe();
extern long lx_pipe2();
extern long lx_poll();
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_personality.c b/usr/src/uts/common/brand/lx/syscall/lx_personality.c
new file mode 100644
index 0000000000..e7aa945b50
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_personality.c
@@ -0,0 +1,112 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+
+
+/*
+ * These flags are for what Linux calls "bug emulation".
+ * (Descriptions from the personality(2) Linux man page.)
+ *
+ * Flags which are currently actionable in LX:
+ * - READ_IMPLIES_EXEC (since Linux 2.6.8)
+ * With this flag set, PROT_READ implies PROT_EXEC for mmap(2).
+ *
+ * Flags which are current accepted but ignored:
+ * - UNAME26 (since Linux 3.1)
+ * Have uname(2) report a 2.6.40+ version number rather than a 3.x version
+ * number. Added as a stopgap measure to support broken applications that
+ * could not handle the kernel version- numbering switch from 2.6.x to 3.x.
+ *
+ * - ADDR_NO_RANDOMIZE (since Linux 2.6.12)
+ * With this flag set, disable address-space-layout randomization.
+ *
+ * - FDPIC_FUNCPTRS (since Linux 2.6.11)
+ * User-space function pointers to signal handlers point (on certain
+ * architectures) to descriptors.
+ *
+ * - MMAP_PAGE_ZERO (since Linux 2.4.0)
+ * Map page 0 as read-only (to support binaries that depend on this SVr4
+ * behavior).
+ *
+ * - ADDR_COMPAT_LAYOUT (since Linux 2.6.9)
+ * With this flag set, provide legacy virtual address space layout.
+ *
+ * - ADDR_LIMIT_32BIT (since Linux 2.2)
+ * Limit the address space to 32 bits.
+ *
+ * - SHORT_INODE (since Linux 2.4.0)
+ * No effects(?).
+ *
+ * - WHOLE_SECONDS (since Linux 1.2.0)
+ * No effects(?).
+ *
+ * - STICKY_TIMEOUTS (since Linux 1.2.0)
+ * With this flag set, select(2), pselect(2), and ppoll(2) do not modify the
+ * returned timeout argument when interrupted by a signal handler.
+ *
+ * - ADDR_LIMIT_3GB (since Linux 2.4.0)
+ * With this flag set, use 0xc0000000 as the offset at which to search a
+ * virtual memory chunk on mmap(2); otherwise use 0xffffe000.
+ */
+
+#define LX_PER_GET 0xffffffff
+
+long
+lx_personality(unsigned int arg)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ unsigned int result = 0;
+
+ mutex_enter(&curproc->p_lock);
+ result = lxpd->l_personality;
+
+ if (arg == LX_PER_GET) {
+ mutex_exit(&curproc->p_lock);
+ return (result);
+ }
+
+ /*
+ * Prevent changes to the personality if the process is undergoing an
+ * exec. This will allow elfexec and friends to manipulate the
+ * personality without hinderance.
+ */
+ if ((curproc->p_flag & P_PR_EXEC) != 0) {
+ mutex_exit(&curproc->p_lock);
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Keep tabs when a non-Linux personality is set. This is silently
+ * allowed to succeed, even though the emulation required is almost
+ * certainly missing.
+ */
+ if ((arg & LX_PER_MASK) != LX_PER_LINUX) {
+ char buf[64];
+
+ (void) snprintf(buf, sizeof (buf), "invalid personality: %02X",
+ arg & LX_PER_MASK);
+ lx_unsupported(buf);
+ }
+
+ lxpd->l_personality = arg;
+ mutex_exit(&curproc->p_lock);
+ return (result);
+}
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index b730774ac7..430445e3c5 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -317,6 +317,7 @@ LX_BRAND_OBJS = \
lx_mkdir.o \
lx_modify_ldt.o \
lx_open.o \
+ lx_personality.o \
lx_pid.o \
lx_pipe.o \
lx_poll.o \