Commit c9b7b9793764b171a118d049d4b721a7f5d8ac82
Committed by
James Morris
1 parent
a639e7ca8e
Exists in
master
and in
20 other branches
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>
Showing 1 changed file with 18 additions and 22 deletions Side-by-side Diff
security/selinux/netport.c
... | ... | @@ -114,8 +114,7 @@ |
114 | 114 | |
115 | 115 | idx = sel_netport_hashfn(pnum); |
116 | 116 | list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list) |
117 | - if (port->psec.port == pnum && | |
118 | - port->psec.protocol == protocol) | |
117 | + if (port->psec.port == pnum && port->psec.protocol == protocol) | |
119 | 118 | return port; |
120 | 119 | |
121 | 120 | return NULL; |
122 | 121 | |
... | ... | @@ -126,11 +125,10 @@ |
126 | 125 | * @port: the new port record |
127 | 126 | * |
128 | 127 | * Description: |
129 | - * Add a new port record to the network address hash table. Returns zero on | |
130 | - * success, negative values on failure. | |
128 | + * Add a new port record to the network address hash table. | |
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 | 133 | unsigned int idx; |
136 | 134 | |
137 | 135 | |
... | ... | @@ -140,13 +138,13 @@ |
140 | 138 | list_add_rcu(&port->list, &sel_netport_hash[idx].list); |
141 | 139 | if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) { |
142 | 140 | struct sel_netport *tail; |
143 | - tail = list_entry(port->list.prev, struct sel_netport, list); | |
144 | - list_del_rcu(port->list.prev); | |
141 | + tail = list_entry( | |
142 | + rcu_dereference(sel_netport_hash[idx].list.prev), | |
143 | + struct sel_netport, list); | |
144 | + list_del_rcu(&tail->list); | |
145 | 145 | call_rcu(&tail->rcu, sel_netport_free); |
146 | 146 | } else |
147 | 147 | sel_netport_hash[idx].size++; |
148 | - | |
149 | - return 0; | |
150 | 148 | } |
151 | 149 | |
152 | 150 | /** |
... | ... | @@ -163,7 +161,7 @@ |
163 | 161 | */ |
164 | 162 | static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid) |
165 | 163 | { |
166 | - int ret; | |
164 | + int ret = -ENOMEM; | |
167 | 165 | struct sel_netport *port; |
168 | 166 | struct sel_netport *new = NULL; |
169 | 167 | |
170 | 168 | |
171 | 169 | |
172 | 170 | |
173 | 171 | |
... | ... | @@ -171,23 +169,20 @@ |
171 | 169 | port = sel_netport_find(protocol, pnum); |
172 | 170 | if (port != NULL) { |
173 | 171 | *sid = port->psec.sid; |
174 | - ret = 0; | |
175 | - goto out; | |
172 | + spin_unlock_bh(&sel_netport_lock); | |
173 | + return 0; | |
176 | 174 | } |
177 | 175 | new = kzalloc(sizeof(*new), GFP_ATOMIC); |
178 | - if (new == NULL) { | |
179 | - ret = -ENOMEM; | |
176 | + if (new == NULL) | |
180 | 177 | goto out; |
181 | - } | |
182 | - ret = security_port_sid(protocol, pnum, &new->psec.sid); | |
178 | + ret = security_port_sid(protocol, pnum, sid); | |
183 | 179 | if (ret != 0) |
184 | 180 | goto out; |
181 | + | |
185 | 182 | new->psec.port = pnum; |
186 | 183 | new->psec.protocol = protocol; |
187 | - ret = sel_netport_insert(new); | |
188 | - if (ret != 0) | |
189 | - goto out; | |
190 | - *sid = new->psec.sid; | |
184 | + new->psec.sid = *sid; | |
185 | + sel_netport_insert(new); | |
191 | 186 | |
192 | 187 | out: |
193 | 188 | spin_unlock_bh(&sel_netport_lock); |
194 | 189 | |
... | ... | @@ -239,11 +234,12 @@ |
239 | 234 | static void sel_netport_flush(void) |
240 | 235 | { |
241 | 236 | unsigned int idx; |
242 | - struct sel_netport *port; | |
237 | + struct sel_netport *port, *port_tmp; | |
243 | 238 | |
244 | 239 | spin_lock_bh(&sel_netport_lock); |
245 | 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 | 243 | list_del_rcu(&port->list); |
248 | 244 | call_rcu(&port->rcu, sel_netport_free); |
249 | 245 | } |