blob: 83d4382f56998d8bcde22b4d9cc4822360d41afa [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Handling of different ABIs (personalities).
3 *
4 * We group personalities into execution domains which have their
5 * own handlers for kernel entry points, signal mapping, etc...
6 *
7 * 2001-05-06 Complete rewrite, Christoph Hellwig (hch@infradead.org)
8 */
9
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/init.h>
11#include <linux/kernel.h>
12#include <linux/kmod.h>
13#include <linux/module.h>
14#include <linux/personality.h>
Alexey Dobriyan6e627752008-10-04 14:28:09 +040015#include <linux/proc_fs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/sched.h>
Alexey Dobriyan6e627752008-10-04 14:28:09 +040017#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/syscalls.h>
19#include <linux/sysctl.h>
20#include <linux/types.h>
Al Viro5ad4e532009-03-29 19:50:06 -040021#include <linux/fs_struct.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23
24static void default_handler(int, struct pt_regs *);
25
26static struct exec_domain *exec_domains = &default_exec_domain;
27static DEFINE_RWLOCK(exec_domains_lock);
28
29
Oleg Nesterov485d5272010-06-04 14:14:58 -070030static unsigned long ident_map[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070031 0, 1, 2, 3, 4, 5, 6, 7,
32 8, 9, 10, 11, 12, 13, 14, 15,
33 16, 17, 18, 19, 20, 21, 22, 23,
34 24, 25, 26, 27, 28, 29, 30, 31
35};
36
37struct exec_domain default_exec_domain = {
38 .name = "Linux", /* name */
39 .handler = default_handler, /* lcall7 causes a seg fault. */
Fabian Frederickb9e5db62014-06-04 16:11:20 -070040 .pers_low = 0, /* PER_LINUX personality. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 .pers_high = 0, /* PER_LINUX personality. */
42 .signal_map = ident_map, /* Identity map signals. */
43 .signal_invmap = ident_map, /* - both ways. */
44};
45
46
47static void
48default_handler(int segment, struct pt_regs *regp)
49{
50 set_personality(0);
51
52 if (current_thread_info()->exec_domain->handler != default_handler)
53 current_thread_info()->exec_domain->handler(segment, regp);
54 else
55 send_sig(SIGSEGV, current, 1);
56}
57
58static struct exec_domain *
Oleg Nesterov485d5272010-06-04 14:14:58 -070059lookup_exec_domain(unsigned int personality)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060{
Oleg Nesterov485d5272010-06-04 14:14:58 -070061 unsigned int pers = personality(personality);
62 struct exec_domain *ep;
Daniel Walker62769dc2007-10-18 03:06:10 -070063
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 read_lock(&exec_domains_lock);
65 for (ep = exec_domains; ep; ep = ep->next) {
66 if (pers >= ep->pers_low && pers <= ep->pers_high)
67 if (try_module_get(ep->module))
68 goto out;
69 }
70
Johannes Berga1ef5ad2008-07-08 19:00:17 +020071#ifdef CONFIG_MODULES
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 read_unlock(&exec_domains_lock);
Oleg Nesterov485d5272010-06-04 14:14:58 -070073 request_module("personality-%d", pers);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 read_lock(&exec_domains_lock);
75
76 for (ep = exec_domains; ep; ep = ep->next) {
77 if (pers >= ep->pers_low && pers <= ep->pers_high)
78 if (try_module_get(ep->module))
79 goto out;
80 }
81#endif
82
83 ep = &default_exec_domain;
84out:
85 read_unlock(&exec_domains_lock);
Fabian Frederickb9e5db62014-06-04 16:11:20 -070086 return ep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087}
88
89int
90register_exec_domain(struct exec_domain *ep)
91{
92 struct exec_domain *tmp;
93 int err = -EBUSY;
94
95 if (ep == NULL)
96 return -EINVAL;
97
98 if (ep->next != NULL)
99 return -EBUSY;
100
101 write_lock(&exec_domains_lock);
102 for (tmp = exec_domains; tmp; tmp = tmp->next) {
103 if (tmp == ep)
104 goto out;
105 }
106
107 ep->next = exec_domains;
108 exec_domains = ep;
109 err = 0;
110
111out:
112 write_unlock(&exec_domains_lock);
Fabian Frederickb9e5db62014-06-04 16:11:20 -0700113 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114}
Fabian Frederickb9e5db62014-06-04 16:11:20 -0700115EXPORT_SYMBOL(register_exec_domain);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
117int
118unregister_exec_domain(struct exec_domain *ep)
119{
120 struct exec_domain **epp;
121
122 epp = &exec_domains;
123 write_lock(&exec_domains_lock);
124 for (epp = &exec_domains; *epp; epp = &(*epp)->next) {
125 if (ep == *epp)
126 goto unregister;
127 }
128 write_unlock(&exec_domains_lock);
129 return -EINVAL;
130
131unregister:
132 *epp = ep->next;
133 ep->next = NULL;
134 write_unlock(&exec_domains_lock);
135 return 0;
136}
Fabian Frederickb9e5db62014-06-04 16:11:20 -0700137EXPORT_SYMBOL(unregister_exec_domain);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700139int __set_personality(unsigned int personality)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140{
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700141 struct exec_domain *oep = current_thread_info()->exec_domain;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700143 current_thread_info()->exec_domain = lookup_exec_domain(personality);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 current->personality = personality;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 module_put(oep->module);
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 return 0;
148}
Fabian Frederickb9e5db62014-06-04 16:11:20 -0700149EXPORT_SYMBOL(__set_personality);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400151#ifdef CONFIG_PROC_FS
152static int execdomains_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
154 struct exec_domain *ep;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
156 read_lock(&exec_domains_lock);
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400157 for (ep = exec_domains; ep; ep = ep->next)
158 seq_printf(m, "%d-%d\t%-16s\t[%s]\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 ep->pers_low, ep->pers_high, ep->name,
160 module_name(ep->module));
161 read_unlock(&exec_domains_lock);
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400162 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163}
164
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400165static int execdomains_proc_open(struct inode *inode, struct file *file)
166{
167 return single_open(file, execdomains_proc_show, NULL);
168}
169
170static const struct file_operations execdomains_proc_fops = {
171 .open = execdomains_proc_open,
172 .read = seq_read,
173 .llseek = seq_lseek,
174 .release = single_release,
175};
176
177static int __init proc_execdomains_init(void)
178{
179 proc_create("execdomains", 0, NULL, &execdomains_proc_fops);
180 return 0;
181}
182module_init(proc_execdomains_init);
183#endif
184
Oleg Nesterov485d5272010-06-04 14:14:58 -0700185SYSCALL_DEFINE1(personality, unsigned int, personality)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186{
Oleg Nesterov485d5272010-06-04 14:14:58 -0700187 unsigned int old = current->personality;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700189 if (personality != 0xffffffff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 set_personality(personality);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
Oleg Nesterov485d5272010-06-04 14:14:58 -0700192 return old;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193}