summaryrefslogtreecommitdiffstats
path: root/kernel/exec_domain.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/exec_domain.c')
-rw-r--r--kernel/exec_domain.c209
1 files changed, 209 insertions, 0 deletions
diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c
new file mode 100644
index 000000000000..867d6dbeb574
--- /dev/null
+++ b/kernel/exec_domain.c
@@ -0,0 +1,209 @@
+/*
+ * Handling of different ABIs (personalities).
+ *
+ * We group personalities into execution domains which have their
+ * own handlers for kernel entry points, signal mapping, etc...
+ *
+ * 2001-05-06 Complete rewrite, Christoph Hellwig (hch@infradead.org)
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/module.h>
+#include <linux/personality.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/sysctl.h>
+#include <linux/types.h>
+
+
+static void default_handler(int, struct pt_regs *);
+
+static struct exec_domain *exec_domains = &default_exec_domain;
+static DEFINE_RWLOCK(exec_domains_lock);
+
+
+static u_long ident_map[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31
+};
+
+struct exec_domain default_exec_domain = {
+ .name = "Linux", /* name */
+ .handler = default_handler, /* lcall7 causes a seg fault. */
+ .pers_low = 0, /* PER_LINUX personality. */
+ .pers_high = 0, /* PER_LINUX personality. */
+ .signal_map = ident_map, /* Identity map signals. */
+ .signal_invmap = ident_map, /* - both ways. */
+};
+
+
+static void
+default_handler(int segment, struct pt_regs *regp)
+{
+ set_personality(0);
+
+ if (current_thread_info()->exec_domain->handler != default_handler)
+ current_thread_info()->exec_domain->handler(segment, regp);
+ else
+ send_sig(SIGSEGV, current, 1);
+}
+
+static struct exec_domain *
+lookup_exec_domain(u_long personality)
+{
+ struct exec_domain * ep;
+ u_long pers = personality(personality);
+
+ read_lock(&exec_domains_lock);
+ for (ep = exec_domains; ep; ep = ep->next) {
+ if (pers >= ep->pers_low && pers <= ep->pers_high)
+ if (try_module_get(ep->module))
+ goto out;
+ }
+
+#ifdef CONFIG_KMOD
+ read_unlock(&exec_domains_lock);
+ request_module("personality-%ld", pers);
+ read_lock(&exec_domains_lock);
+
+ for (ep = exec_domains; ep; ep = ep->next) {
+ if (pers >= ep->pers_low && pers <= ep->pers_high)
+ if (try_module_get(ep->module))
+ goto out;
+ }
+#endif
+
+ ep = &default_exec_domain;
+out:
+ read_unlock(&exec_domains_lock);
+ return (ep);
+}
+
+int
+register_exec_domain(struct exec_domain *ep)
+{
+ struct exec_domain *tmp;
+ int err = -EBUSY;
+
+ if (ep == NULL)
+ return -EINVAL;
+
+ if (ep->next != NULL)
+ return -EBUSY;
+
+ write_lock(&exec_domains_lock);
+ for (tmp = exec_domains; tmp; tmp = tmp->next) {
+ if (tmp == ep)
+ goto out;
+ }
+
+ ep->next = exec_domains;
+ exec_domains = ep;
+ err = 0;
+
+out:
+ write_unlock(&exec_domains_lock);
+ return (err);
+}
+
+int
+unregister_exec_domain(struct exec_domain *ep)
+{
+ struct exec_domain **epp;
+
+ epp = &exec_domains;
+ write_lock(&exec_domains_lock);
+ for (epp = &exec_domains; *epp; epp = &(*epp)->next) {
+ if (ep == *epp)
+ goto unregister;
+ }
+ write_unlock(&exec_domains_lock);
+ return -EINVAL;
+
+unregister:
+ *epp = ep->next;
+ ep->next = NULL;
+ write_unlock(&exec_domains_lock);
+ return 0;
+}
+
+int
+__set_personality(u_long personality)
+{
+ struct exec_domain *ep, *oep;
+
+ ep = lookup_exec_domain(personality);
+ if (ep == current_thread_info()->exec_domain) {
+ current->personality = personality;
+ return 0;
+ }
+
+ if (atomic_read(&current->fs->count) != 1) {
+ struct fs_struct *fsp, *ofsp;
+
+ fsp = copy_fs_struct(current->fs);
+ if (fsp == NULL) {
+ module_put(ep->module);
+ return -ENOMEM;
+ }
+
+ task_lock(current);
+ ofsp = current->fs;
+ current->fs = fsp;
+ task_unlock(current);
+
+ put_fs_struct(ofsp);
+ }
+
+ /*
+ * At that point we are guaranteed to be the sole owner of
+ * current->fs.
+ */
+
+ current->personality = personality;
+ oep = current_thread_info()->exec_domain;
+ current_thread_info()->exec_domain = ep;
+ set_fs_altroot();
+
+ module_put(oep->module);
+ return 0;
+}
+
+int
+get_exec_domain_list(char *page)
+{
+ struct exec_domain *ep;
+ int len = 0;
+
+ read_lock(&exec_domains_lock);
+ for (ep = exec_domains; ep && len < PAGE_SIZE - 80; ep = ep->next)
+ len += sprintf(page + len, "%d-%d\t%-16s\t[%s]\n",
+ ep->pers_low, ep->pers_high, ep->name,
+ module_name(ep->module));
+ read_unlock(&exec_domains_lock);
+ return (len);
+}
+
+asmlinkage long
+sys_personality(u_long personality)
+{
+ u_long old = current->personality;
+
+ if (personality != 0xffffffff) {
+ set_personality(personality);
+ if (current->personality != personality)
+ return -EINVAL;
+ }
+
+ return (long)old;
+}
+
+
+EXPORT_SYMBOL(register_exec_domain);
+EXPORT_SYMBOL(unregister_exec_domain);
+EXPORT_SYMBOL(__set_personality);