blob: 36cc21da4dd024850fc70f11ccca1bc20e3cc5cf [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
John Stultz9c60b9e2015-11-17 08:35:54 -080071/*
72 * Disable the request_module here to avoid trying to
73 * load the personality-8 module, which doesn't exist,
74 * and results in selinux audit noise.
75 * Disabling this here avoids folks adding module_request
76 * to their sepolicy, which is maybe too generous
77 */
78#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 read_unlock(&exec_domains_lock);
Oleg Nesterov485d5272010-06-04 14:14:58 -070080 request_module("personality-%d", pers);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 read_lock(&exec_domains_lock);
82
83 for (ep = exec_domains; ep; ep = ep->next) {
84 if (pers >= ep->pers_low && pers <= ep->pers_high)
85 if (try_module_get(ep->module))
86 goto out;
87 }
88#endif
89
90 ep = &default_exec_domain;
91out:
92 read_unlock(&exec_domains_lock);
93 return (ep);
94}
95
96int
97register_exec_domain(struct exec_domain *ep)
98{
99 struct exec_domain *tmp;
100 int err = -EBUSY;
101
102 if (ep == NULL)
103 return -EINVAL;
104
105 if (ep->next != NULL)
106 return -EBUSY;
107
108 write_lock(&exec_domains_lock);
109 for (tmp = exec_domains; tmp; tmp = tmp->next) {
110 if (tmp == ep)
111 goto out;
112 }
113
114 ep->next = exec_domains;
115 exec_domains = ep;
116 err = 0;
117
118out:
119 write_unlock(&exec_domains_lock);
120 return (err);
121}
122
123int
124unregister_exec_domain(struct exec_domain *ep)
125{
126 struct exec_domain **epp;
127
128 epp = &exec_domains;
129 write_lock(&exec_domains_lock);
130 for (epp = &exec_domains; *epp; epp = &(*epp)->next) {
131 if (ep == *epp)
132 goto unregister;
133 }
134 write_unlock(&exec_domains_lock);
135 return -EINVAL;
136
137unregister:
138 *epp = ep->next;
139 ep->next = NULL;
140 write_unlock(&exec_domains_lock);
141 return 0;
142}
143
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700144int __set_personality(unsigned int personality)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700146 struct exec_domain *oep = current_thread_info()->exec_domain;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700148 current_thread_info()->exec_domain = lookup_exec_domain(personality);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 current->personality = personality;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 module_put(oep->module);
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700151
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 return 0;
153}
154
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400155#ifdef CONFIG_PROC_FS
156static int execdomains_proc_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157{
158 struct exec_domain *ep;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
160 read_lock(&exec_domains_lock);
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400161 for (ep = exec_domains; ep; ep = ep->next)
162 seq_printf(m, "%d-%d\t%-16s\t[%s]\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 ep->pers_low, ep->pers_high, ep->name,
164 module_name(ep->module));
165 read_unlock(&exec_domains_lock);
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400166 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167}
168
Alexey Dobriyan6e627752008-10-04 14:28:09 +0400169static int execdomains_proc_open(struct inode *inode, struct file *file)
170{
171 return single_open(file, execdomains_proc_show, NULL);
172}
173
174static const struct file_operations execdomains_proc_fops = {
175 .open = execdomains_proc_open,
176 .read = seq_read,
177 .llseek = seq_lseek,
178 .release = single_release,
179};
180
181static int __init proc_execdomains_init(void)
182{
183 proc_create("execdomains", 0, NULL, &execdomains_proc_fops);
184 return 0;
185}
186module_init(proc_execdomains_init);
187#endif
188
Oleg Nesterov485d5272010-06-04 14:14:58 -0700189SYSCALL_DEFINE1(personality, unsigned int, personality)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190{
Oleg Nesterov485d5272010-06-04 14:14:58 -0700191 unsigned int old = current->personality;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Oleg Nesterov2ee7c922010-08-09 17:20:30 -0700193 if (personality != 0xffffffff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 set_personality(personality);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
Oleg Nesterov485d5272010-06-04 14:14:58 -0700196 return old;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197}
198
199
200EXPORT_SYMBOL(register_exec_domain);
201EXPORT_SYMBOL(unregister_exec_domain);
202EXPORT_SYMBOL(__set_personality);