summaryrefslogtreecommitdiffstats
path: root/kernel/exec_domain.c
blob: 9a202359a69e6f549a565df7c4fb162cd551b4c0 (plain)
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
#include <linux/mm.h>

static asmlinkage void no_lcall7(struct pt_regs * regs);


static unsigned 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 = {
	"Linux",	/* name */
	no_lcall7,	/* lcall7 causes a seg fault. */
	0, 0xff,	/* All personalities. */
	ident_map,	/* Identity map signals. */
	ident_map,	/*  - both ways. */
	NULL,		/* No usage counter. */
	NULL		/* Nothing after this in the list. */
};

static struct exec_domain *exec_domains = &default_exec_domain;


static asmlinkage void no_lcall7(struct pt_regs * regs)
{

  /*
   * This may have been a static linked SVr4 binary, so we would have the
   * personality set incorrectly.  Check to see whether SVr4 is available,
   * and use it, otherwise give the user a SEGV.
   */
	if (current->exec_domain && current->exec_domain->use_count)
		(*current->exec_domain->use_count)--;

	current->personality = PER_SVR4;
	current->exec_domain = lookup_exec_domain(current->personality);

	if (current->exec_domain && current->exec_domain->use_count)
		(*current->exec_domain->use_count)++;

	if (current->exec_domain && current->exec_domain->handler
	&& current->exec_domain->handler != no_lcall7) {
		current->exec_domain->handler(regs);
		return;
	}

	send_sig(SIGSEGV, current, 1);
}

struct exec_domain *lookup_exec_domain(unsigned long personality)
{
	unsigned long pers = personality & PER_MASK;
	struct exec_domain *it;

	for (it=exec_domains; it; it=it->next)
		if (pers >= it->pers_low
		&& pers <= it->pers_high)
			return it;

	/* Should never get this far. */
	printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers);
	return NULL;
}

int register_exec_domain(struct exec_domain *it)
{
	struct exec_domain *tmp;

	if (!it)
		return -EINVAL;
	if (it->next)
		return -EBUSY;
	for (tmp=exec_domains; tmp; tmp=tmp->next)
		if (tmp == it)
			return -EBUSY;
	it->next = exec_domains;
	exec_domains = it;
	return 0;
}

int unregister_exec_domain(struct exec_domain *it)
{
	struct exec_domain ** tmp;

	tmp = &exec_domains;
	while (*tmp) {
		if (it == *tmp) {
			*tmp = it->next;
			it->next = NULL;
			return 0;
		}
		tmp = &(*tmp)->next;
	}
	return -EINVAL;
}

asmlinkage int sys_personality(unsigned long personality)
{
	struct exec_domain *it;
	unsigned long old_personality;

	if (personality == 0xffffffff)
		return current->personality;

	it = lookup_exec_domain(personality);
	if (!it)
		return -EINVAL;

	old_personality = current->personality;
	if (current->exec_domain && current->exec_domain->use_count)
		(*current->exec_domain->use_count)--;
	current->personality = personality;
	current->exec_domain = it;
	if (current->exec_domain->use_count)
		(*current->exec_domain->use_count)++;

	return old_personality;
}