Merge branches 'core/futexes', 'core/locking', 'core/rcu' and 'linus' into core/urgent
This commit is contained in:
@@ -159,11 +159,8 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
|
||||
return __audit_signal_info(sig, t);
|
||||
return 0;
|
||||
}
|
||||
extern enum audit_state audit_filter_inodes(struct task_struct *,
|
||||
struct audit_context *);
|
||||
extern void audit_set_auditable(struct audit_context *);
|
||||
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
|
||||
#else
|
||||
#define audit_signal_info(s,t) AUDIT_DISABLED
|
||||
#define audit_filter_inodes(t,c) AUDIT_DISABLED
|
||||
#define audit_set_auditable(c)
|
||||
#endif
|
||||
|
@@ -450,6 +450,7 @@ static void kill_rules(struct audit_tree *tree)
|
||||
audit_log_end(ab);
|
||||
rule->tree = NULL;
|
||||
list_del_rcu(&entry->list);
|
||||
list_del(&entry->rule.list);
|
||||
call_rcu(&entry->rcu, audit_free_rule_rcu);
|
||||
}
|
||||
}
|
||||
@@ -617,7 +618,7 @@ int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op)
|
||||
|
||||
if (pathname[0] != '/' ||
|
||||
rule->listnr != AUDIT_FILTER_EXIT ||
|
||||
op & ~AUDIT_EQUAL ||
|
||||
op != Audit_equal ||
|
||||
rule->inode_f || rule->watch || rule->tree)
|
||||
return -EINVAL;
|
||||
rule->tree = alloc_tree(pathname);
|
||||
|
@@ -86,6 +86,14 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
|
||||
#error Fix audit_filter_list initialiser
|
||||
#endif
|
||||
};
|
||||
static struct list_head audit_rules_list[AUDIT_NR_FILTERS] = {
|
||||
LIST_HEAD_INIT(audit_rules_list[0]),
|
||||
LIST_HEAD_INIT(audit_rules_list[1]),
|
||||
LIST_HEAD_INIT(audit_rules_list[2]),
|
||||
LIST_HEAD_INIT(audit_rules_list[3]),
|
||||
LIST_HEAD_INIT(audit_rules_list[4]),
|
||||
LIST_HEAD_INIT(audit_rules_list[5]),
|
||||
};
|
||||
|
||||
DEFINE_MUTEX(audit_filter_mutex);
|
||||
|
||||
@@ -244,7 +252,8 @@ static inline int audit_to_inode(struct audit_krule *krule,
|
||||
struct audit_field *f)
|
||||
{
|
||||
if (krule->listnr != AUDIT_FILTER_EXIT ||
|
||||
krule->watch || krule->inode_f || krule->tree)
|
||||
krule->watch || krule->inode_f || krule->tree ||
|
||||
(f->op != Audit_equal && f->op != Audit_not_equal))
|
||||
return -EINVAL;
|
||||
|
||||
krule->inode_f = f;
|
||||
@@ -262,7 +271,7 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len,
|
||||
|
||||
if (path[0] != '/' || path[len-1] == '/' ||
|
||||
krule->listnr != AUDIT_FILTER_EXIT ||
|
||||
op & ~AUDIT_EQUAL ||
|
||||
op != Audit_equal ||
|
||||
krule->inode_f || krule->watch || krule->tree)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -412,12 +421,32 @@ exit_err:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static u32 audit_ops[] =
|
||||
{
|
||||
[Audit_equal] = AUDIT_EQUAL,
|
||||
[Audit_not_equal] = AUDIT_NOT_EQUAL,
|
||||
[Audit_bitmask] = AUDIT_BIT_MASK,
|
||||
[Audit_bittest] = AUDIT_BIT_TEST,
|
||||
[Audit_lt] = AUDIT_LESS_THAN,
|
||||
[Audit_gt] = AUDIT_GREATER_THAN,
|
||||
[Audit_le] = AUDIT_LESS_THAN_OR_EQUAL,
|
||||
[Audit_ge] = AUDIT_GREATER_THAN_OR_EQUAL,
|
||||
};
|
||||
|
||||
static u32 audit_to_op(u32 op)
|
||||
{
|
||||
u32 n;
|
||||
for (n = Audit_equal; n < Audit_bad && audit_ops[n] != op; n++)
|
||||
;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/* Translate struct audit_rule to kernel's rule respresentation.
|
||||
* Exists for backward compatibility with userspace. */
|
||||
static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||
{
|
||||
struct audit_entry *entry;
|
||||
struct audit_field *ino_f;
|
||||
int err = 0;
|
||||
int i;
|
||||
|
||||
@@ -427,12 +456,28 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||
|
||||
for (i = 0; i < rule->field_count; i++) {
|
||||
struct audit_field *f = &entry->rule.fields[i];
|
||||
u32 n;
|
||||
|
||||
n = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||
|
||||
/* Support for legacy operators where
|
||||
* AUDIT_NEGATE bit signifies != and otherwise assumes == */
|
||||
if (n & AUDIT_NEGATE)
|
||||
f->op = Audit_not_equal;
|
||||
else if (!n)
|
||||
f->op = Audit_equal;
|
||||
else
|
||||
f->op = audit_to_op(n);
|
||||
|
||||
entry->rule.vers_ops = (n & AUDIT_OPERATORS) ? 2 : 1;
|
||||
|
||||
f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||
f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
|
||||
f->val = rule->values[i];
|
||||
|
||||
err = -EINVAL;
|
||||
if (f->op == Audit_bad)
|
||||
goto exit_free;
|
||||
|
||||
switch(f->type) {
|
||||
default:
|
||||
goto exit_free;
|
||||
@@ -454,11 +499,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||
case AUDIT_EXIT:
|
||||
case AUDIT_SUCCESS:
|
||||
/* bit ops are only useful on syscall args */
|
||||
if (f->op == AUDIT_BIT_MASK ||
|
||||
f->op == AUDIT_BIT_TEST) {
|
||||
err = -EINVAL;
|
||||
if (f->op == Audit_bitmask || f->op == Audit_bittest)
|
||||
goto exit_free;
|
||||
}
|
||||
break;
|
||||
case AUDIT_ARG0:
|
||||
case AUDIT_ARG1:
|
||||
@@ -467,11 +509,8 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||
break;
|
||||
/* arch is only allowed to be = or != */
|
||||
case AUDIT_ARCH:
|
||||
if ((f->op != AUDIT_NOT_EQUAL) && (f->op != AUDIT_EQUAL)
|
||||
&& (f->op != AUDIT_NEGATE) && (f->op)) {
|
||||
err = -EINVAL;
|
||||
if (f->op != Audit_not_equal && f->op != Audit_equal)
|
||||
goto exit_free;
|
||||
}
|
||||
entry->rule.arch_f = f;
|
||||
break;
|
||||
case AUDIT_PERM:
|
||||
@@ -488,33 +527,10 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
|
||||
goto exit_free;
|
||||
break;
|
||||
}
|
||||
|
||||
entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
|
||||
|
||||
/* Support for legacy operators where
|
||||
* AUDIT_NEGATE bit signifies != and otherwise assumes == */
|
||||
if (f->op & AUDIT_NEGATE)
|
||||
f->op = AUDIT_NOT_EQUAL;
|
||||
else if (!f->op)
|
||||
f->op = AUDIT_EQUAL;
|
||||
else if (f->op == AUDIT_OPERATORS) {
|
||||
err = -EINVAL;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
|
||||
ino_f = entry->rule.inode_f;
|
||||
if (ino_f) {
|
||||
switch(ino_f->op) {
|
||||
case AUDIT_NOT_EQUAL:
|
||||
entry->rule.inode_f = NULL;
|
||||
case AUDIT_EQUAL:
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal)
|
||||
entry->rule.inode_f = NULL;
|
||||
|
||||
exit_nofree:
|
||||
return entry;
|
||||
@@ -530,7 +546,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
||||
{
|
||||
int err = 0;
|
||||
struct audit_entry *entry;
|
||||
struct audit_field *ino_f;
|
||||
void *bufp;
|
||||
size_t remain = datasz - sizeof(struct audit_rule_data);
|
||||
int i;
|
||||
@@ -546,11 +561,11 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
||||
struct audit_field *f = &entry->rule.fields[i];
|
||||
|
||||
err = -EINVAL;
|
||||
if (!(data->fieldflags[i] & AUDIT_OPERATORS) ||
|
||||
data->fieldflags[i] & ~AUDIT_OPERATORS)
|
||||
|
||||
f->op = audit_to_op(data->fieldflags[i]);
|
||||
if (f->op == Audit_bad)
|
||||
goto exit_free;
|
||||
|
||||
f->op = data->fieldflags[i] & AUDIT_OPERATORS;
|
||||
f->type = data->fields[i];
|
||||
f->val = data->values[i];
|
||||
f->lsm_str = NULL;
|
||||
@@ -662,18 +677,8 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
|
||||
}
|
||||
}
|
||||
|
||||
ino_f = entry->rule.inode_f;
|
||||
if (ino_f) {
|
||||
switch(ino_f->op) {
|
||||
case AUDIT_NOT_EQUAL:
|
||||
entry->rule.inode_f = NULL;
|
||||
case AUDIT_EQUAL:
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto exit_free;
|
||||
}
|
||||
}
|
||||
if (entry->rule.inode_f && entry->rule.inode_f->op == Audit_not_equal)
|
||||
entry->rule.inode_f = NULL;
|
||||
|
||||
exit_nofree:
|
||||
return entry;
|
||||
@@ -713,10 +718,10 @@ static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
|
||||
rule->fields[i] = krule->fields[i].type;
|
||||
|
||||
if (krule->vers_ops == 1) {
|
||||
if (krule->fields[i].op & AUDIT_NOT_EQUAL)
|
||||
if (krule->fields[i].op == Audit_not_equal)
|
||||
rule->fields[i] |= AUDIT_NEGATE;
|
||||
} else {
|
||||
rule->fields[i] |= krule->fields[i].op;
|
||||
rule->fields[i] |= audit_ops[krule->fields[i].op];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
|
||||
@@ -744,7 +749,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
|
||||
struct audit_field *f = &krule->fields[i];
|
||||
|
||||
data->fields[i] = f->type;
|
||||
data->fieldflags[i] = f->op;
|
||||
data->fieldflags[i] = audit_ops[f->op];
|
||||
switch(f->type) {
|
||||
case AUDIT_SUBJ_USER:
|
||||
case AUDIT_SUBJ_ROLE:
|
||||
@@ -919,6 +924,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
|
||||
new->action = old->action;
|
||||
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
|
||||
new->mask[i] = old->mask[i];
|
||||
new->prio = old->prio;
|
||||
new->buflen = old->buflen;
|
||||
new->inode_f = old->inode_f;
|
||||
new->watch = NULL;
|
||||
@@ -987,9 +993,8 @@ static void audit_update_watch(struct audit_parent *parent,
|
||||
|
||||
/* If the update involves invalidating rules, do the inode-based
|
||||
* filtering now, so we don't omit records. */
|
||||
if (invalidating && current->audit_context &&
|
||||
audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT)
|
||||
audit_set_auditable(current->audit_context);
|
||||
if (invalidating && current->audit_context)
|
||||
audit_filter_inodes(current, current->audit_context);
|
||||
|
||||
nwatch = audit_dupe_watch(owatch);
|
||||
if (IS_ERR(nwatch)) {
|
||||
@@ -1007,12 +1012,15 @@ static void audit_update_watch(struct audit_parent *parent,
|
||||
list_del_rcu(&oentry->list);
|
||||
|
||||
nentry = audit_dupe_rule(&oentry->rule, nwatch);
|
||||
if (IS_ERR(nentry))
|
||||
if (IS_ERR(nentry)) {
|
||||
list_del(&oentry->rule.list);
|
||||
audit_panic("error updating watch, removing");
|
||||
else {
|
||||
} else {
|
||||
int h = audit_hash_ino((u32)ino);
|
||||
list_add(&nentry->rule.rlist, &nwatch->rules);
|
||||
list_add_rcu(&nentry->list, &audit_inode_hash[h]);
|
||||
list_replace(&oentry->rule.list,
|
||||
&nentry->rule.list);
|
||||
}
|
||||
|
||||
call_rcu(&oentry->rcu, audit_free_rule_rcu);
|
||||
@@ -1077,6 +1085,7 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
|
||||
audit_log_end(ab);
|
||||
}
|
||||
list_del(&r->rlist);
|
||||
list_del(&r->list);
|
||||
list_del_rcu(&e->list);
|
||||
call_rcu(&e->rcu, audit_free_rule_rcu);
|
||||
}
|
||||
@@ -1102,12 +1111,16 @@ static void audit_inotify_unregister(struct list_head *in_list)
|
||||
/* Find an existing audit rule.
|
||||
* Caller must hold audit_filter_mutex to prevent stale rule data. */
|
||||
static struct audit_entry *audit_find_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
struct list_head **p)
|
||||
{
|
||||
struct audit_entry *e, *found = NULL;
|
||||
struct list_head *list;
|
||||
int h;
|
||||
|
||||
if (entry->rule.watch) {
|
||||
if (entry->rule.inode_f) {
|
||||
h = audit_hash_ino(entry->rule.inode_f->val);
|
||||
*p = list = &audit_inode_hash[h];
|
||||
} else if (entry->rule.watch) {
|
||||
/* we don't know the inode number, so must walk entire hash */
|
||||
for (h = 0; h < AUDIT_INODE_BUCKETS; h++) {
|
||||
list = &audit_inode_hash[h];
|
||||
@@ -1118,6 +1131,8 @@ static struct audit_entry *audit_find_rule(struct audit_entry *entry,
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
} else {
|
||||
*p = list = &audit_filter_list[entry->rule.listnr];
|
||||
}
|
||||
|
||||
list_for_each_entry(e, list, list)
|
||||
@@ -1258,15 +1273,17 @@ static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 prio_low = ~0ULL/2;
|
||||
static u64 prio_high = ~0ULL/2 - 1;
|
||||
|
||||
/* Add rule to given filterlist if not a duplicate. */
|
||||
static inline int audit_add_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
static inline int audit_add_rule(struct audit_entry *entry)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
struct audit_field *inode_f = entry->rule.inode_f;
|
||||
struct audit_watch *watch = entry->rule.watch;
|
||||
struct audit_tree *tree = entry->rule.tree;
|
||||
struct nameidata *ndp = NULL, *ndw = NULL;
|
||||
struct list_head *list;
|
||||
int h, err;
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
int dont_count = 0;
|
||||
@@ -1277,13 +1294,8 @@ static inline int audit_add_rule(struct audit_entry *entry,
|
||||
dont_count = 1;
|
||||
#endif
|
||||
|
||||
if (inode_f) {
|
||||
h = audit_hash_ino(inode_f->val);
|
||||
list = &audit_inode_hash[h];
|
||||
}
|
||||
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
e = audit_find_rule(entry, list);
|
||||
e = audit_find_rule(entry, &list);
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
if (e) {
|
||||
err = -EEXIST;
|
||||
@@ -1319,10 +1331,22 @@ static inline int audit_add_rule(struct audit_entry *entry,
|
||||
}
|
||||
}
|
||||
|
||||
entry->rule.prio = ~0ULL;
|
||||
if (entry->rule.listnr == AUDIT_FILTER_EXIT) {
|
||||
if (entry->rule.flags & AUDIT_FILTER_PREPEND)
|
||||
entry->rule.prio = ++prio_high;
|
||||
else
|
||||
entry->rule.prio = --prio_low;
|
||||
}
|
||||
|
||||
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
|
||||
list_add(&entry->rule.list,
|
||||
&audit_rules_list[entry->rule.listnr]);
|
||||
list_add_rcu(&entry->list, list);
|
||||
entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
|
||||
} else {
|
||||
list_add_tail(&entry->rule.list,
|
||||
&audit_rules_list[entry->rule.listnr]);
|
||||
list_add_tail_rcu(&entry->list, list);
|
||||
}
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
@@ -1345,15 +1369,14 @@ error:
|
||||
}
|
||||
|
||||
/* Remove an existing rule from filterlist. */
|
||||
static inline int audit_del_rule(struct audit_entry *entry,
|
||||
struct list_head *list)
|
||||
static inline int audit_del_rule(struct audit_entry *entry)
|
||||
{
|
||||
struct audit_entry *e;
|
||||
struct audit_field *inode_f = entry->rule.inode_f;
|
||||
struct audit_watch *watch, *tmp_watch = entry->rule.watch;
|
||||
struct audit_tree *tree = entry->rule.tree;
|
||||
struct list_head *list;
|
||||
LIST_HEAD(inotify_list);
|
||||
int h, ret = 0;
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
int dont_count = 0;
|
||||
|
||||
@@ -1363,13 +1386,8 @@ static inline int audit_del_rule(struct audit_entry *entry,
|
||||
dont_count = 1;
|
||||
#endif
|
||||
|
||||
if (inode_f) {
|
||||
h = audit_hash_ino(inode_f->val);
|
||||
list = &audit_inode_hash[h];
|
||||
}
|
||||
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
e = audit_find_rule(entry, list);
|
||||
e = audit_find_rule(entry, &list);
|
||||
if (!e) {
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
ret = -ENOENT;
|
||||
@@ -1404,6 +1422,7 @@ static inline int audit_del_rule(struct audit_entry *entry,
|
||||
audit_remove_tree_rule(&e->rule);
|
||||
|
||||
list_del_rcu(&e->list);
|
||||
list_del(&e->rule.list);
|
||||
call_rcu(&e->rcu, audit_free_rule_rcu);
|
||||
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
@@ -1432,30 +1451,16 @@ out:
|
||||
static void audit_list(int pid, int seq, struct sk_buff_head *q)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct audit_entry *entry;
|
||||
struct audit_krule *r;
|
||||
int i;
|
||||
|
||||
/* This is a blocking read, so use audit_filter_mutex instead of rcu
|
||||
* iterator to sync with list writers. */
|
||||
for (i=0; i<AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry(entry, &audit_filter_list[i], list) {
|
||||
list_for_each_entry(r, &audit_rules_list[i], list) {
|
||||
struct audit_rule *rule;
|
||||
|
||||
rule = audit_krule_to_rule(&entry->rule);
|
||||
if (unlikely(!rule))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
rule, sizeof(*rule));
|
||||
if (skb)
|
||||
skb_queue_tail(q, skb);
|
||||
kfree(rule);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < AUDIT_INODE_BUCKETS; i++) {
|
||||
list_for_each_entry(entry, &audit_inode_hash[i], list) {
|
||||
struct audit_rule *rule;
|
||||
|
||||
rule = audit_krule_to_rule(&entry->rule);
|
||||
rule = audit_krule_to_rule(r);
|
||||
if (unlikely(!rule))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
|
||||
@@ -1474,30 +1479,16 @@ static void audit_list(int pid, int seq, struct sk_buff_head *q)
|
||||
static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct audit_entry *e;
|
||||
struct audit_krule *r;
|
||||
int i;
|
||||
|
||||
/* This is a blocking read, so use audit_filter_mutex instead of rcu
|
||||
* iterator to sync with list writers. */
|
||||
for (i=0; i<AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry(e, &audit_filter_list[i], list) {
|
||||
list_for_each_entry(r, &audit_rules_list[i], list) {
|
||||
struct audit_rule_data *data;
|
||||
|
||||
data = audit_krule_to_data(&e->rule);
|
||||
if (unlikely(!data))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
|
||||
data, sizeof(*data) + data->buflen);
|
||||
if (skb)
|
||||
skb_queue_tail(q, skb);
|
||||
kfree(data);
|
||||
}
|
||||
}
|
||||
for (i=0; i< AUDIT_INODE_BUCKETS; i++) {
|
||||
list_for_each_entry(e, &audit_inode_hash[i], list) {
|
||||
struct audit_rule_data *data;
|
||||
|
||||
data = audit_krule_to_data(&e->rule);
|
||||
data = audit_krule_to_data(r);
|
||||
if (unlikely(!data))
|
||||
break;
|
||||
skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
|
||||
@@ -1603,8 +1594,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
err = audit_add_rule(entry,
|
||||
&audit_filter_list[entry->rule.listnr]);
|
||||
err = audit_add_rule(entry);
|
||||
audit_log_rule_change(loginuid, sessionid, sid, "add",
|
||||
&entry->rule, !err);
|
||||
|
||||
@@ -1620,8 +1610,7 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
err = audit_del_rule(entry,
|
||||
&audit_filter_list[entry->rule.listnr]);
|
||||
err = audit_del_rule(entry);
|
||||
audit_log_rule_change(loginuid, sessionid, sid, "remove",
|
||||
&entry->rule, !err);
|
||||
|
||||
@@ -1634,28 +1623,29 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
|
||||
return err;
|
||||
}
|
||||
|
||||
int audit_comparator(const u32 left, const u32 op, const u32 right)
|
||||
int audit_comparator(u32 left, u32 op, u32 right)
|
||||
{
|
||||
switch (op) {
|
||||
case AUDIT_EQUAL:
|
||||
case Audit_equal:
|
||||
return (left == right);
|
||||
case AUDIT_NOT_EQUAL:
|
||||
case Audit_not_equal:
|
||||
return (left != right);
|
||||
case AUDIT_LESS_THAN:
|
||||
case Audit_lt:
|
||||
return (left < right);
|
||||
case AUDIT_LESS_THAN_OR_EQUAL:
|
||||
case Audit_le:
|
||||
return (left <= right);
|
||||
case AUDIT_GREATER_THAN:
|
||||
case Audit_gt:
|
||||
return (left > right);
|
||||
case AUDIT_GREATER_THAN_OR_EQUAL:
|
||||
case Audit_ge:
|
||||
return (left >= right);
|
||||
case AUDIT_BIT_MASK:
|
||||
case Audit_bitmask:
|
||||
return (left & right);
|
||||
case AUDIT_BIT_TEST:
|
||||
case Audit_bittest:
|
||||
return ((left & right) == right);
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compare given dentry name with last component in given path,
|
||||
@@ -1778,6 +1768,43 @@ unlock_and_return:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int update_lsm_rule(struct audit_krule *r)
|
||||
{
|
||||
struct audit_entry *entry = container_of(r, struct audit_entry, rule);
|
||||
struct audit_entry *nentry;
|
||||
struct audit_watch *watch;
|
||||
struct audit_tree *tree;
|
||||
int err = 0;
|
||||
|
||||
if (!security_audit_rule_known(r))
|
||||
return 0;
|
||||
|
||||
watch = r->watch;
|
||||
tree = r->tree;
|
||||
nentry = audit_dupe_rule(r, watch);
|
||||
if (IS_ERR(nentry)) {
|
||||
/* save the first error encountered for the
|
||||
* return value */
|
||||
err = PTR_ERR(nentry);
|
||||
audit_panic("error updating LSM filters");
|
||||
if (watch)
|
||||
list_del(&r->rlist);
|
||||
list_del_rcu(&entry->list);
|
||||
list_del(&r->list);
|
||||
} else {
|
||||
if (watch) {
|
||||
list_add(&nentry->rule.rlist, &watch->rules);
|
||||
list_del(&r->rlist);
|
||||
} else if (tree)
|
||||
list_replace_init(&r->rlist, &nentry->rule.rlist);
|
||||
list_replace_rcu(&entry->list, &nentry->list);
|
||||
list_replace(&r->list, &nentry->rule.list);
|
||||
}
|
||||
call_rcu(&entry->rcu, audit_free_rule_rcu);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function will re-initialize the lsm_rule field of all applicable rules.
|
||||
* It will traverse the filter lists serarching for rules that contain LSM
|
||||
* specific filter fields. When such a rule is found, it is copied, the
|
||||
@@ -1785,45 +1812,19 @@ unlock_and_return:
|
||||
* updated rule. */
|
||||
int audit_update_lsm_rules(void)
|
||||
{
|
||||
struct audit_entry *entry, *n, *nentry;
|
||||
struct audit_watch *watch;
|
||||
struct audit_tree *tree;
|
||||
struct audit_krule *r, *n;
|
||||
int i, err = 0;
|
||||
|
||||
/* audit_filter_mutex synchronizes the writers */
|
||||
mutex_lock(&audit_filter_mutex);
|
||||
|
||||
for (i = 0; i < AUDIT_NR_FILTERS; i++) {
|
||||
list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
|
||||
if (!security_audit_rule_known(&entry->rule))
|
||||
continue;
|
||||
|
||||
watch = entry->rule.watch;
|
||||
tree = entry->rule.tree;
|
||||
nentry = audit_dupe_rule(&entry->rule, watch);
|
||||
if (IS_ERR(nentry)) {
|
||||
/* save the first error encountered for the
|
||||
* return value */
|
||||
if (!err)
|
||||
err = PTR_ERR(nentry);
|
||||
audit_panic("error updating LSM filters");
|
||||
if (watch)
|
||||
list_del(&entry->rule.rlist);
|
||||
list_del_rcu(&entry->list);
|
||||
} else {
|
||||
if (watch) {
|
||||
list_add(&nentry->rule.rlist,
|
||||
&watch->rules);
|
||||
list_del(&entry->rule.rlist);
|
||||
} else if (tree)
|
||||
list_replace_init(&entry->rule.rlist,
|
||||
&nentry->rule.rlist);
|
||||
list_replace_rcu(&entry->list, &nentry->list);
|
||||
}
|
||||
call_rcu(&entry->rcu, audit_free_rule_rcu);
|
||||
list_for_each_entry_safe(r, n, &audit_rules_list[i], list) {
|
||||
int res = update_lsm_rule(r);
|
||||
if (!err)
|
||||
err = res;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&audit_filter_mutex);
|
||||
|
||||
return err;
|
||||
|
739
kernel/auditsc.c
739
kernel/auditsc.c
File diff suppressed because it is too large
Load Diff
@@ -280,9 +280,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = audit_log_capset(pid, new, current_cred());
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
audit_log_capset(pid, new, current_cred());
|
||||
|
||||
return commit_creds(new);
|
||||
|
||||
|
@@ -573,7 +573,6 @@ static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb)
|
||||
inode->i_mode = mode;
|
||||
inode->i_uid = current_fsuid();
|
||||
inode->i_gid = current_fsgid();
|
||||
inode->i_blocks = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
||||
inode->i_mapping->backing_dev_info = &cgroup_backing_dev_info;
|
||||
}
|
||||
@@ -2945,7 +2944,11 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys,
|
||||
parent = task_cgroup(tsk, subsys->subsys_id);
|
||||
|
||||
/* Pin the hierarchy */
|
||||
atomic_inc(&parent->root->sb->s_active);
|
||||
if (!atomic_inc_not_zero(&parent->root->sb->s_active)) {
|
||||
/* We race with the final deactivate_super() */
|
||||
mutex_unlock(&cgroup_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep the cgroup alive */
|
||||
get_css_set(cg);
|
||||
|
@@ -269,8 +269,11 @@ out_release:
|
||||
|
||||
int __ref cpu_down(unsigned int cpu)
|
||||
{
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
err = stop_machine_create();
|
||||
if (err)
|
||||
return err;
|
||||
cpu_maps_update_begin();
|
||||
|
||||
if (cpu_hotplug_disabled) {
|
||||
@@ -297,6 +300,7 @@ int __ref cpu_down(unsigned int cpu)
|
||||
|
||||
out:
|
||||
cpu_maps_update_done();
|
||||
stop_machine_destroy();
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(cpu_down);
|
||||
|
@@ -170,8 +170,11 @@ static void get_futex_key_refs(union futex_key *key)
|
||||
*/
|
||||
static void drop_futex_key_refs(union futex_key *key)
|
||||
{
|
||||
if (!key->both.ptr)
|
||||
if (!key->both.ptr) {
|
||||
/* If we're here then we tried to put a key we failed to get */
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) {
|
||||
case FUT_OFF_INODE:
|
||||
@@ -730,8 +733,8 @@ static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset)
|
||||
}
|
||||
|
||||
spin_unlock(&hb->lock);
|
||||
out:
|
||||
put_futex_key(fshared, &key);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -755,7 +758,7 @@ retryfull:
|
||||
goto out;
|
||||
ret = get_futex_key(uaddr2, fshared, &key2);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
goto out_put_key1;
|
||||
|
||||
hb1 = hash_futex(&key1);
|
||||
hb2 = hash_futex(&key2);
|
||||
@@ -777,12 +780,12 @@ retry:
|
||||
* but we might get them from range checking
|
||||
*/
|
||||
ret = op_ret;
|
||||
goto out;
|
||||
goto out_put_keys;
|
||||
#endif
|
||||
|
||||
if (unlikely(op_ret != -EFAULT)) {
|
||||
ret = op_ret;
|
||||
goto out;
|
||||
goto out_put_keys;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -796,7 +799,7 @@ retry:
|
||||
ret = futex_handle_fault((unsigned long)uaddr2,
|
||||
attempt);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_put_keys;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
@@ -834,10 +837,11 @@ retry:
|
||||
spin_unlock(&hb1->lock);
|
||||
if (hb1 != hb2)
|
||||
spin_unlock(&hb2->lock);
|
||||
out:
|
||||
out_put_keys:
|
||||
put_futex_key(fshared, &key2);
|
||||
out_put_key1:
|
||||
put_futex_key(fshared, &key1);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -854,13 +858,13 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
|
||||
struct futex_q *this, *next;
|
||||
int ret, drop_count = 0;
|
||||
|
||||
retry:
|
||||
retry:
|
||||
ret = get_futex_key(uaddr1, fshared, &key1);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
ret = get_futex_key(uaddr2, fshared, &key2);
|
||||
if (unlikely(ret != 0))
|
||||
goto out;
|
||||
goto out_put_key1;
|
||||
|
||||
hb1 = hash_futex(&key1);
|
||||
hb2 = hash_futex(&key2);
|
||||
@@ -882,7 +886,7 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2,
|
||||
if (!ret)
|
||||
goto retry;
|
||||
|
||||
return ret;
|
||||
goto out_put_keys;
|
||||
}
|
||||
if (curval != *cmpval) {
|
||||
ret = -EAGAIN;
|
||||
@@ -927,9 +931,11 @@ out_unlock:
|
||||
while (--drop_count >= 0)
|
||||
drop_futex_key_refs(&key1);
|
||||
|
||||
out:
|
||||
out_put_keys:
|
||||
put_futex_key(fshared, &key2);
|
||||
out_put_key1:
|
||||
put_futex_key(fshared, &key1);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -990,7 +996,7 @@ static int unqueue_me(struct futex_q *q)
|
||||
int ret = 0;
|
||||
|
||||
/* In the common case we don't take the spinlock, which is nice. */
|
||||
retry:
|
||||
retry:
|
||||
lock_ptr = q->lock_ptr;
|
||||
barrier();
|
||||
if (lock_ptr != NULL) {
|
||||
@@ -1172,11 +1178,11 @@ static int futex_wait(u32 __user *uaddr, int fshared,
|
||||
|
||||
q.pi_state = NULL;
|
||||
q.bitset = bitset;
|
||||
retry:
|
||||
retry:
|
||||
q.key = FUTEX_KEY_INIT;
|
||||
ret = get_futex_key(uaddr, fshared, &q.key);
|
||||
if (unlikely(ret != 0))
|
||||
goto out_release_sem;
|
||||
goto out;
|
||||
|
||||
hb = queue_lock(&q);
|
||||
|
||||
@@ -1204,6 +1210,7 @@ static int futex_wait(u32 __user *uaddr, int fshared,
|
||||
|
||||
if (unlikely(ret)) {
|
||||
queue_unlock(&q, hb);
|
||||
put_futex_key(fshared, &q.key);
|
||||
|
||||
ret = get_user(uval, uaddr);
|
||||
|
||||
@@ -1213,7 +1220,7 @@ static int futex_wait(u32 __user *uaddr, int fshared,
|
||||
}
|
||||
ret = -EWOULDBLOCK;
|
||||
if (uval != val)
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
|
||||
/* Only actually queue if *uaddr contained val. */
|
||||
queue_me(&q, hb);
|
||||
@@ -1305,11 +1312,11 @@ static int futex_wait(u32 __user *uaddr, int fshared,
|
||||
return -ERESTART_RESTARTBLOCK;
|
||||
}
|
||||
|
||||
out_unlock_release_sem:
|
||||
out_unlock_put_key:
|
||||
queue_unlock(&q, hb);
|
||||
|
||||
out_release_sem:
|
||||
put_futex_key(fshared, &q.key);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1358,16 +1365,16 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
}
|
||||
|
||||
q.pi_state = NULL;
|
||||
retry:
|
||||
retry:
|
||||
q.key = FUTEX_KEY_INIT;
|
||||
ret = get_futex_key(uaddr, fshared, &q.key);
|
||||
if (unlikely(ret != 0))
|
||||
goto out_release_sem;
|
||||
goto out;
|
||||
|
||||
retry_unlocked:
|
||||
retry_unlocked:
|
||||
hb = queue_lock(&q);
|
||||
|
||||
retry_locked:
|
||||
retry_locked:
|
||||
ret = lock_taken = 0;
|
||||
|
||||
/*
|
||||
@@ -1388,14 +1395,14 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
*/
|
||||
if (unlikely((curval & FUTEX_TID_MASK) == task_pid_vnr(current))) {
|
||||
ret = -EDEADLK;
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Surprise - we got the lock. Just return to userspace:
|
||||
*/
|
||||
if (unlikely(!curval))
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
|
||||
uval = curval;
|
||||
|
||||
@@ -1431,7 +1438,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
* We took the lock due to owner died take over.
|
||||
*/
|
||||
if (unlikely(lock_taken))
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
|
||||
/*
|
||||
* We dont have the lock. Look up the PI state (or create it if
|
||||
@@ -1470,7 +1477,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
goto retry_locked;
|
||||
}
|
||||
default:
|
||||
goto out_unlock_release_sem;
|
||||
goto out_unlock_put_key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1561,16 +1568,17 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
destroy_hrtimer_on_stack(&to->timer);
|
||||
return ret != -EINTR ? ret : -ERESTARTNOINTR;
|
||||
|
||||
out_unlock_release_sem:
|
||||
out_unlock_put_key:
|
||||
queue_unlock(&q, hb);
|
||||
|
||||
out_release_sem:
|
||||
out_put_key:
|
||||
put_futex_key(fshared, &q.key);
|
||||
out:
|
||||
if (to)
|
||||
destroy_hrtimer_on_stack(&to->timer);
|
||||
return ret;
|
||||
|
||||
uaddr_faulted:
|
||||
uaddr_faulted:
|
||||
/*
|
||||
* We have to r/w *(int __user *)uaddr, and we have to modify it
|
||||
* atomically. Therefore, if we continue to fault after get_user()
|
||||
@@ -1583,7 +1591,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared,
|
||||
if (attempt++) {
|
||||
ret = futex_handle_fault((unsigned long)uaddr, attempt);
|
||||
if (ret)
|
||||
goto out_release_sem;
|
||||
goto out_put_key;
|
||||
goto retry_unlocked;
|
||||
}
|
||||
|
||||
@@ -1675,9 +1683,9 @@ retry_unlocked:
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&hb->lock);
|
||||
out:
|
||||
put_futex_key(fshared, &key);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
||||
pi_faulted:
|
||||
|
@@ -757,8 +757,16 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
|
||||
return -EFAULT;
|
||||
name[MODULE_NAME_LEN-1] = '\0';
|
||||
|
||||
if (mutex_lock_interruptible(&module_mutex) != 0)
|
||||
return -EINTR;
|
||||
/* Create stop_machine threads since free_module relies on
|
||||
* a non-failing stop_machine call. */
|
||||
ret = stop_machine_create();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mutex_lock_interruptible(&module_mutex) != 0) {
|
||||
ret = -EINTR;
|
||||
goto out_stop;
|
||||
}
|
||||
|
||||
mod = find_module(name);
|
||||
if (!mod) {
|
||||
@@ -817,10 +825,12 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
|
||||
|
||||
out:
|
||||
mutex_unlock(&module_mutex);
|
||||
out_stop:
|
||||
stop_machine_destroy();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
static inline void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
{
|
||||
struct module_use *use;
|
||||
int printed_something = 0;
|
||||
@@ -893,7 +903,7 @@ void module_put(struct module *module)
|
||||
EXPORT_SYMBOL(module_put);
|
||||
|
||||
#else /* !CONFIG_MODULE_UNLOAD */
|
||||
static void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
static inline void print_unload_info(struct seq_file *m, struct module *mod)
|
||||
{
|
||||
/* We don't know the usage count, or what modules are using. */
|
||||
seq_printf(m, " - -");
|
||||
@@ -1578,11 +1588,21 @@ static int simplify_symbols(Elf_Shdr *sechdrs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Additional bytes needed by arch in front of individual sections */
|
||||
unsigned int __weak arch_mod_section_prepend(struct module *mod,
|
||||
unsigned int section)
|
||||
{
|
||||
/* default implementation just returns zero */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update size with this section: return offset. */
|
||||
static long get_offset(unsigned int *size, Elf_Shdr *sechdr)
|
||||
static long get_offset(struct module *mod, unsigned int *size,
|
||||
Elf_Shdr *sechdr, unsigned int section)
|
||||
{
|
||||
long ret;
|
||||
|
||||
*size += arch_mod_section_prepend(mod, section);
|
||||
ret = ALIGN(*size, sechdr->sh_addralign ?: 1);
|
||||
*size = ret + sechdr->sh_size;
|
||||
return ret;
|
||||
@@ -1622,7 +1642,7 @@ static void layout_sections(struct module *mod,
|
||||
|| strncmp(secstrings + s->sh_name,
|
||||
".init", 5) == 0)
|
||||
continue;
|
||||
s->sh_entsize = get_offset(&mod->core_size, s);
|
||||
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
|
||||
DEBUGP("\t%s\n", secstrings + s->sh_name);
|
||||
}
|
||||
if (m == 0)
|
||||
@@ -1640,7 +1660,7 @@ static void layout_sections(struct module *mod,
|
||||
|| strncmp(secstrings + s->sh_name,
|
||||
".init", 5) != 0)
|
||||
continue;
|
||||
s->sh_entsize = (get_offset(&mod->init_size, s)
|
||||
s->sh_entsize = (get_offset(mod, &mod->init_size, s, i)
|
||||
| INIT_OFFSET_MASK);
|
||||
DEBUGP("\t%s\n", secstrings + s->sh_name);
|
||||
}
|
||||
@@ -1725,15 +1745,15 @@ static const struct kernel_symbol *lookup_symbol(const char *name,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int is_exported(const char *name, const struct module *mod)
|
||||
static int is_exported(const char *name, unsigned long value,
|
||||
const struct module *mod)
|
||||
{
|
||||
if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab))
|
||||
return 1;
|
||||
const struct kernel_symbol *ks;
|
||||
if (!mod)
|
||||
ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab);
|
||||
else
|
||||
if (mod && lookup_symbol(name, mod->syms, mod->syms + mod->num_syms))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms);
|
||||
return ks != NULL && ks->value == value;
|
||||
}
|
||||
|
||||
/* As per nm */
|
||||
@@ -1865,6 +1885,13 @@ static noinline struct module *load_module(void __user *umod,
|
||||
/* vmalloc barfs on "unusual" numbers. Check here */
|
||||
if (len > 64 * 1024 * 1024 || (hdr = vmalloc(len)) == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Create stop_machine threads since the error path relies on
|
||||
* a non-failing stop_machine call. */
|
||||
err = stop_machine_create();
|
||||
if (err)
|
||||
goto free_hdr;
|
||||
|
||||
if (copy_from_user(hdr, umod, len) != 0) {
|
||||
err = -EFAULT;
|
||||
goto free_hdr;
|
||||
@@ -2248,6 +2275,7 @@ static noinline struct module *load_module(void __user *umod,
|
||||
/* Get rid of temporary copy */
|
||||
vfree(hdr);
|
||||
|
||||
stop_machine_destroy();
|
||||
/* Done! */
|
||||
return mod;
|
||||
|
||||
@@ -2270,6 +2298,7 @@ static noinline struct module *load_module(void __user *umod,
|
||||
kfree(args);
|
||||
free_hdr:
|
||||
vfree(hdr);
|
||||
stop_machine_destroy();
|
||||
return ERR_PTR(err);
|
||||
|
||||
truncated:
|
||||
@@ -2504,7 +2533,7 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
|
||||
strlcpy(name, mod->strtab + mod->symtab[symnum].st_name,
|
||||
KSYM_NAME_LEN);
|
||||
strlcpy(module_name, mod->name, MODULE_NAME_LEN);
|
||||
*exported = is_exported(name, mod);
|
||||
*exported = is_exported(name, *value, mod);
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
@@ -38,7 +38,10 @@ struct stop_machine_data {
|
||||
static unsigned int num_threads;
|
||||
static atomic_t thread_ack;
|
||||
static DEFINE_MUTEX(lock);
|
||||
|
||||
/* setup_lock protects refcount, stop_machine_wq and stop_machine_work. */
|
||||
static DEFINE_MUTEX(setup_lock);
|
||||
/* Users of stop_machine. */
|
||||
static int refcount;
|
||||
static struct workqueue_struct *stop_machine_wq;
|
||||
static struct stop_machine_data active, idle;
|
||||
static const cpumask_t *active_cpus;
|
||||
@@ -109,6 +112,43 @@ static int chill(void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stop_machine_create(void)
|
||||
{
|
||||
mutex_lock(&setup_lock);
|
||||
if (refcount)
|
||||
goto done;
|
||||
stop_machine_wq = create_rt_workqueue("kstop");
|
||||
if (!stop_machine_wq)
|
||||
goto err_out;
|
||||
stop_machine_work = alloc_percpu(struct work_struct);
|
||||
if (!stop_machine_work)
|
||||
goto err_out;
|
||||
done:
|
||||
refcount++;
|
||||
mutex_unlock(&setup_lock);
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
if (stop_machine_wq)
|
||||
destroy_workqueue(stop_machine_wq);
|
||||
mutex_unlock(&setup_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stop_machine_create);
|
||||
|
||||
void stop_machine_destroy(void)
|
||||
{
|
||||
mutex_lock(&setup_lock);
|
||||
refcount--;
|
||||
if (refcount)
|
||||
goto done;
|
||||
destroy_workqueue(stop_machine_wq);
|
||||
free_percpu(stop_machine_work);
|
||||
done:
|
||||
mutex_unlock(&setup_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stop_machine_destroy);
|
||||
|
||||
int __stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
|
||||
{
|
||||
struct work_struct *sm_work;
|
||||
@@ -146,19 +186,14 @@ int stop_machine(int (*fn)(void *), void *data, const struct cpumask *cpus)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = stop_machine_create();
|
||||
if (ret)
|
||||
return ret;
|
||||
/* No CPUs can come up or down during this. */
|
||||
get_online_cpus();
|
||||
ret = __stop_machine(fn, data, cpus);
|
||||
put_online_cpus();
|
||||
|
||||
stop_machine_destroy();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stop_machine);
|
||||
|
||||
static int __init stop_machine_init(void)
|
||||
{
|
||||
stop_machine_wq = create_rt_workqueue("kstop");
|
||||
stop_machine_work = alloc_percpu(struct work_struct);
|
||||
return 0;
|
||||
}
|
||||
core_initcall(stop_machine_init);
|
||||
|
Reference in New Issue
Block a user