diff options
author | Paul Moore <paul.moore@hp.com> | 2008-04-25 15:03:39 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2008-04-27 19:36:27 -0400 |
commit | c9b7b9793764b171a118d049d4b721a7f5d8ac82 (patch) | |
tree | d9b0bf6c44a6672f6c3e08da340f6544056932e5 /security/selinux | |
parent | a639e7ca8e8282b75be2724a28bfc788aa3bb156 (diff) |
SELinux: Fix a RCU free problem with the netport cache
The netport cache doesn't free resources in a manner which is safe or orderly.
This patch fixes this by adding in a missing call to rcu_dereference() in
sel_netport_insert() as well as some general cleanup throughout the file.
Signed-off-by: Paul Moore <paul.moore@hp.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/netport.c | 40 |
1 files changed, 18 insertions, 22 deletions
diff --git a/security/selinux/netport.c b/security/selinux/netport.c index 68ede3c498ab..90b4cff7c350 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c | |||
@@ -114,8 +114,7 @@ static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum) | |||
114 | 114 | ||
115 | idx = sel_netport_hashfn(pnum); | 115 | idx = sel_netport_hashfn(pnum); |
116 | list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) | 116 | list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) |
117 | if (port->psec.port == pnum && | 117 | if (port->psec.port == pnum && port->psec.protocol == protocol) |
118 | port->psec.protocol == protocol) | ||
119 | return port; | 118 | return port; |
120 | 119 | ||
121 | return NULL; | 120 | return NULL; |
@@ -126,11 +125,10 @@ static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum) | |||
126 | * @port: the new port record | 125 | * @port: the new port record |
127 | * | 126 | * |
128 | * Description: | 127 | * Description: |
129 | * Add a new port record to the network address hash table. Returns zero on | 128 | * Add a new port record to the network address hash table. |
130 | * success, negative values on failure. | ||
131 | * | 129 | * |
132 | */ | 130 | */ |
133 | static int sel_netport_insert(struct sel_netport *port) | 131 | static void sel_netport_insert(struct sel_netport *port) |
134 | { | 132 | { |
135 | unsigned int idx; | 133 | unsigned int idx; |
136 | 134 | ||
@@ -140,13 +138,13 @@ static int sel_netport_insert(struct sel_netport *port) | |||
140 | list_add_rcu(&port->list, &sel_netport_hash[idx].list); | 138 | list_add_rcu(&port->list, &sel_netport_hash[idx].list); |
141 | if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { | 139 | if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { |
142 | struct sel_netport *tail; | 140 | struct sel_netport *tail; |
143 | tail = list_entry(port->list.prev, struct sel_netport, list); | 141 | tail = list_entry( |
144 | list_del_rcu(port->list.prev); | 142 | rcu_dereference(sel_netport_hash[idx].list.prev), |
143 | struct sel_netport, list); | ||
144 | list_del_rcu(&tail->list); | ||
145 | call_rcu(&tail->rcu, sel_netport_free); | 145 | call_rcu(&tail->rcu, sel_netport_free); |
146 | } else | 146 | } else |
147 | sel_netport_hash[idx].size++; | 147 | sel_netport_hash[idx].size++; |
148 | |||
149 | return 0; | ||
150 | } | 148 | } |
151 | 149 | ||
152 | /** | 150 | /** |
@@ -163,7 +161,7 @@ static int sel_netport_insert(struct sel_netport *port) | |||
163 | */ | 161 | */ |
164 | static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) | 162 | static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) |
165 | { | 163 | { |
166 | int ret; | 164 | int ret = -ENOMEM; |
167 | struct sel_netport *port; | 165 | struct sel_netport *port; |
168 | struct sel_netport *new = NULL; | 166 | struct sel_netport *new = NULL; |
169 | 167 | ||
@@ -171,23 +169,20 @@ static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) | |||
171 | port = sel_netport_find(protocol, pnum); | 169 | port = sel_netport_find(protocol, pnum); |
172 | if (port != NULL) { | 170 | if (port != NULL) { |
173 | *sid = port->psec.sid; | 171 | *sid = port->psec.sid; |
174 | ret = 0; | 172 | spin_unlock_bh(&sel_netport_lock); |
175 | goto out; | 173 | return 0; |
176 | } | 174 | } |
177 | new = kzalloc(sizeof(*new), GFP_ATOMIC); | 175 | new = kzalloc(sizeof(*new), GFP_ATOMIC); |
178 | if (new == NULL) { | 176 | if (new == NULL) |
179 | ret = -ENOMEM; | ||
180 | goto out; | 177 | goto out; |
181 | } | 178 | ret = security_port_sid(protocol, pnum, sid); |
182 | ret = security_port_sid(protocol, pnum, &new->psec.sid); | ||
183 | if (ret != 0) | 179 | if (ret != 0) |
184 | goto out; | 180 | goto out; |
181 | |||
185 | new->psec.port = pnum; | 182 | new->psec.port = pnum; |
186 | new->psec.protocol = protocol; | 183 | new->psec.protocol = protocol; |
187 | ret = sel_netport_insert(new); | 184 | new->psec.sid = *sid; |
188 | if (ret != 0) | 185 | sel_netport_insert(new); |
189 | goto out; | ||
190 | *sid = new->psec.sid; | ||
191 | 186 | ||
192 | out: | 187 | out: |
193 | spin_unlock_bh(&sel_netport_lock); | 188 | spin_unlock_bh(&sel_netport_lock); |
@@ -239,11 +234,12 @@ int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) | |||
239 | static void sel_netport_flush(void) | 234 | static void sel_netport_flush(void) |
240 | { | 235 | { |
241 | unsigned int idx; | 236 | unsigned int idx; |
242 | struct sel_netport *port; | 237 | struct sel_netport *port, *port_tmp; |
243 | 238 | ||
244 | spin_lock_bh(&sel_netport_lock); | 239 | spin_lock_bh(&sel_netport_lock); |
245 | for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) { | 240 | for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) { |
246 | list_for_each_entry(port, &sel_netport_hash[idx].list, list) { | 241 | list_for_each_entry_safe(port, port_tmp, |
242 | &sel_netport_hash[idx].list, list) { | ||
247 | list_del_rcu(&port->list); | 243 | list_del_rcu(&port->list); |
248 | call_rcu(&port->rcu, sel_netport_free); | 244 | call_rcu(&port->rcu, sel_netport_free); |
249 | } | 245 | } |