blob: dd62f8e714ca52d2fed7a7b14b03a6ee9a53d2f1 [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. */
40 .pers_low = 0, /* PER_LINUX personality. */
41 .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);
86 return (ep);
87}
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);
113 return (err);
114}
115
116int
117unregister_exec_domain(struct exec_domain *ep)
118{
119 struct exec_domain **epp;
120
121 epp = &exec_domains;
122 write_lock(&exec_domains_lock);
123 for (epp = &exec_domains; *epp; epp = &(*epp)->next) {
124 if (ep == *epp)
125 goto unregister;
126 }
127 write_unlock(&exec_domains_lock);
128 return -EINVAL;
129
130unregister:
131 *epp = ep->next;
132 ep->next = NULL;
133 write_unlock(&exec_domains_lock);
134 return 0;
135}
136
137int
Oleg Nesterov485d5272010-06-04 14:14:58 -0700138__set_personality(unsigned int personality)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139{
140 struct exec_domain *ep, *oep;
141
142 ep = lookup_exec_domain(personality);
143 if (ep == current_thread_info()->exec_domain) {
144 current->personality = personality;
Sergey Vlasov6a4d11c2006-03-24 03:18:38 -0800145 module_put(ep->module);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 return 0;
147 }
148
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 current->personality = personality;
150 oep = current_thread_info()->exec_domain;
151 current_thread_info()->exec_domain = ep;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 module_put(oep->module);
154 return 0;
155}
156
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400157#ifdef CONFIG_PROC_FS
158static int execdomains_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159{
160 struct exec_domain *ep;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
162 read_lock(&exec_domains_lock);
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400163 for (ep = exec_domains; ep; ep = ep->next)
164 seq_printf(m, "%d-%d\t%-16s\t[%s]\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 ep->pers_low, ep->pers_high, ep->name,
166 module_name(ep->module));
167 read_unlock(&exec_domains_lock);
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400168 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169}
170
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400171static int execdomains_proc_open(struct inode *inode, struct file *file)
172{
173 return single_open(file, execdomains_proc_show, NULL);
174}
175
176static const struct file_operations execdomains_proc_fops = {
177 .open = execdomains_proc_open,
178 .read = seq_read,
179 .llseek = seq_lseek,
180 .release = single_release,
181};
182
183static int __init proc_execdomains_init(void)
184{
185 proc_create("execdomains", 0, NULL, &execdomains_proc_fops);
186 return 0;
187}
188module_init(proc_execdomains_init);
189#endif
190
Oleg Nesterov485d5272010-06-04 14:14:58 -0700191SYSCALL_DEFINE1(personality, unsigned int, personality)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
Oleg Nesterov485d5272010-06-04 14:14:58 -0700193 unsigned int old = current->personality;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
195 if (personality != 0xffffffff) {
196 set_personality(personality);
197 if (current->personality != personality)
198 return -EINVAL;
199 }
200
Oleg Nesterov485d5272010-06-04 14:14:58 -0700201 return old;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202}
203
204
205EXPORT_SYMBOL(register_exec_domain);
206EXPORT_SYMBOL(unregister_exec_domain);
207EXPORT_SYMBOL(__set_personality);