aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpaul.moore@hp.com <paul.moore@hp.com>2006-10-04 11:46:31 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-10-12 02:59:29 -0400
commitffb733c65000ee701294f7b80c4eca2a5f335637 (patch)
treeedda8e25792fe4a7bf0c619787949291276b9ed7
parentc25d5180441e344a3368d100c57f0a481c6944f7 (diff)
NetLabel: fix a cache race condition
Testing revealed a problem with the NetLabel cache where a cached entry could be freed while in use by the LSM layer causing an oops and other problems. This patch fixes that problem by introducing a reference counter to the cache entry so that it is only freed when it is no longer in use. Signed-off-by: Paul Moore <paul.moore@hp.com> Signed-off-by: James Morris <jmorris@namei.org>
-rw-r--r--include/net/netlabel.h62
-rw-r--r--net/ipv4/cipso_ipv4.c18
-rw-r--r--net/netlabel/netlabel_kapi.c2
-rw-r--r--security/selinux/ss/services.c37
4 files changed, 79 insertions, 40 deletions
diff --git a/include/net/netlabel.h b/include/net/netlabel.h
index c63a58058e21..113337c27955 100644
--- a/include/net/netlabel.h
+++ b/include/net/netlabel.h
@@ -34,6 +34,7 @@
34#include <linux/net.h> 34#include <linux/net.h>
35#include <linux/skbuff.h> 35#include <linux/skbuff.h>
36#include <net/netlink.h> 36#include <net/netlink.h>
37#include <asm/atomic.h>
37 38
38/* 39/*
39 * NetLabel - A management interface for maintaining network packet label 40 * NetLabel - A management interface for maintaining network packet label
@@ -106,6 +107,7 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info);
106 107
107/* LSM security attributes */ 108/* LSM security attributes */
108struct netlbl_lsm_cache { 109struct netlbl_lsm_cache {
110 atomic_t refcount;
109 void (*free) (const void *data); 111 void (*free) (const void *data);
110 void *data; 112 void *data;
111}; 113};
@@ -117,7 +119,7 @@ struct netlbl_lsm_secattr {
117 unsigned char *mls_cat; 119 unsigned char *mls_cat;
118 size_t mls_cat_len; 120 size_t mls_cat_len;
119 121
120 struct netlbl_lsm_cache cache; 122 struct netlbl_lsm_cache *cache;
121}; 123};
122 124
123/* 125/*
@@ -126,6 +128,43 @@ struct netlbl_lsm_secattr {
126 128
127 129
128/** 130/**
131 * netlbl_secattr_cache_alloc - Allocate and initialize a secattr cache
132 * @flags: the memory allocation flags
133 *
134 * Description:
135 * Allocate and initialize a netlbl_lsm_cache structure. Returns a pointer
136 * on success, NULL on failure.
137 *
138 */
139static inline struct netlbl_lsm_cache *netlbl_secattr_cache_alloc(int flags)
140{
141 struct netlbl_lsm_cache *cache;
142
143 cache = kzalloc(sizeof(*cache), flags);
144 if (cache)
145 atomic_set(&cache->refcount, 1);
146 return cache;
147}
148
149/**
150 * netlbl_secattr_cache_free - Frees a netlbl_lsm_cache struct
151 * @cache: the struct to free
152 *
153 * Description:
154 * Frees @secattr including all of the internal buffers.
155 *
156 */
157static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache)
158{
159 if (!atomic_dec_and_test(&cache->refcount))
160 return;
161
162 if (cache->free)
163 cache->free(cache->data);
164 kfree(cache);
165}
166
167/**
129 * netlbl_secattr_init - Initialize a netlbl_lsm_secattr struct 168 * netlbl_secattr_init - Initialize a netlbl_lsm_secattr struct
130 * @secattr: the struct to initialize 169 * @secattr: the struct to initialize
131 * 170 *
@@ -143,20 +182,16 @@ static inline int netlbl_secattr_init(struct netlbl_lsm_secattr *secattr)
143/** 182/**
144 * netlbl_secattr_destroy - Clears a netlbl_lsm_secattr struct 183 * netlbl_secattr_destroy - Clears a netlbl_lsm_secattr struct
145 * @secattr: the struct to clear 184 * @secattr: the struct to clear
146 * @clear_cache: cache clear flag
147 * 185 *
148 * Description: 186 * Description:
149 * Destroys the @secattr struct, including freeing all of the internal buffers. 187 * Destroys the @secattr struct, including freeing all of the internal buffers.
150 * If @clear_cache is true then free the cache fields, otherwise leave them 188 * The struct must be reset with a call to netlbl_secattr_init() before reuse.
151 * intact. The struct must be reset with a call to netlbl_secattr_init()
152 * before reuse.
153 * 189 *
154 */ 190 */
155static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr, 191static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr)
156 u32 clear_cache)
157{ 192{
158 if (clear_cache && secattr->cache.data != NULL && secattr->cache.free) 193 if (secattr->cache)
159 secattr->cache.free(secattr->cache.data); 194 netlbl_secattr_cache_free(secattr->cache);
160 kfree(secattr->domain); 195 kfree(secattr->domain);
161 kfree(secattr->mls_cat); 196 kfree(secattr->mls_cat);
162} 197}
@@ -178,17 +213,14 @@ static inline struct netlbl_lsm_secattr *netlbl_secattr_alloc(int flags)
178/** 213/**
179 * netlbl_secattr_free - Frees a netlbl_lsm_secattr struct 214 * netlbl_secattr_free - Frees a netlbl_lsm_secattr struct
180 * @secattr: the struct to free 215 * @secattr: the struct to free
181 * @clear_cache: cache clear flag
182 * 216 *
183 * Description: 217 * Description:
184 * Frees @secattr including all of the internal buffers. If @clear_cache is 218 * Frees @secattr including all of the internal buffers.
185 * true then free the cache fields, otherwise leave them intact.
186 * 219 *
187 */ 220 */
188static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr, 221static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr)
189 u32 clear_cache)
190{ 222{
191 netlbl_secattr_destroy(secattr, clear_cache); 223 netlbl_secattr_destroy(secattr);
192 kfree(secattr); 224 kfree(secattr);
193} 225}
194 226
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index a8e2e879a647..bde8ccaa1531 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -43,6 +43,7 @@
43#include <net/tcp.h> 43#include <net/tcp.h>
44#include <net/netlabel.h> 44#include <net/netlabel.h>
45#include <net/cipso_ipv4.h> 45#include <net/cipso_ipv4.h>
46#include <asm/atomic.h>
46#include <asm/bug.h> 47#include <asm/bug.h>
47 48
48struct cipso_v4_domhsh_entry { 49struct cipso_v4_domhsh_entry {
@@ -79,7 +80,7 @@ struct cipso_v4_map_cache_entry {
79 unsigned char *key; 80 unsigned char *key;
80 size_t key_len; 81 size_t key_len;
81 82
82 struct netlbl_lsm_cache lsm_data; 83 struct netlbl_lsm_cache *lsm_data;
83 84
84 u32 activity; 85 u32 activity;
85 struct list_head list; 86 struct list_head list;
@@ -188,13 +189,14 @@ static void cipso_v4_doi_domhsh_free(struct rcu_head *entry)
188 * @entry: the entry to free 189 * @entry: the entry to free
189 * 190 *
190 * Description: 191 * Description:
191 * This function frees the memory associated with a cache entry. 192 * This function frees the memory associated with a cache entry including the
193 * LSM cache data if there are no longer any users, i.e. reference count == 0.
192 * 194 *
193 */ 195 */
194static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry) 196static void cipso_v4_cache_entry_free(struct cipso_v4_map_cache_entry *entry)
195{ 197{
196 if (entry->lsm_data.free) 198 if (entry->lsm_data)
197 entry->lsm_data.free(entry->lsm_data.data); 199 netlbl_secattr_cache_free(entry->lsm_data);
198 kfree(entry->key); 200 kfree(entry->key);
199 kfree(entry); 201 kfree(entry);
200} 202}
@@ -315,8 +317,8 @@ static int cipso_v4_cache_check(const unsigned char *key,
315 entry->key_len == key_len && 317 entry->key_len == key_len &&
316 memcmp(entry->key, key, key_len) == 0) { 318 memcmp(entry->key, key, key_len) == 0) {
317 entry->activity += 1; 319 entry->activity += 1;
318 secattr->cache.free = entry->lsm_data.free; 320 atomic_inc(&entry->lsm_data->refcount);
319 secattr->cache.data = entry->lsm_data.data; 321 secattr->cache = entry->lsm_data;
320 if (prev_entry == NULL) { 322 if (prev_entry == NULL) {
321 spin_unlock_bh(&cipso_v4_cache[bkt].lock); 323 spin_unlock_bh(&cipso_v4_cache[bkt].lock);
322 return 0; 324 return 0;
@@ -383,8 +385,8 @@ int cipso_v4_cache_add(const struct sk_buff *skb,
383 memcpy(entry->key, cipso_ptr, cipso_ptr_len); 385 memcpy(entry->key, cipso_ptr, cipso_ptr_len);
384 entry->key_len = cipso_ptr_len; 386 entry->key_len = cipso_ptr_len;
385 entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len); 387 entry->hash = cipso_v4_map_cache_hash(cipso_ptr, cipso_ptr_len);
386 entry->lsm_data.free = secattr->cache.free; 388 atomic_inc(&secattr->cache->refcount);
387 entry->lsm_data.data = secattr->cache.data; 389 entry->lsm_data = secattr->cache;
388 390
389 bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1); 391 bkt = entry->hash & (CIPSO_V4_CACHE_BUCKETBITS - 1);
390 spin_lock_bh(&cipso_v4_cache[bkt].lock); 392 spin_lock_bh(&cipso_v4_cache[bkt].lock);
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 54fb7de3c2b1..ff971103fd0c 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -200,7 +200,7 @@ void netlbl_cache_invalidate(void)
200int netlbl_cache_add(const struct sk_buff *skb, 200int netlbl_cache_add(const struct sk_buff *skb,
201 const struct netlbl_lsm_secattr *secattr) 201 const struct netlbl_lsm_secattr *secattr)
202{ 202{
203 if (secattr->cache.data == NULL) 203 if (secattr->cache == NULL)
204 return -ENOMSG; 204 return -ENOMSG;
205 205
206 if (CIPSO_V4_OPTEXIST(skb)) 206 if (CIPSO_V4_OPTEXIST(skb))
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 0c219a1b3243..bb2d2bc869ba 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2172,7 +2172,12 @@ struct netlbl_cache {
2172 */ 2172 */
2173static void selinux_netlbl_cache_free(const void *data) 2173static void selinux_netlbl_cache_free(const void *data)
2174{ 2174{
2175 struct netlbl_cache *cache = NETLBL_CACHE(data); 2175 struct netlbl_cache *cache;
2176
2177 if (data == NULL)
2178 return;
2179
2180 cache = NETLBL_CACHE(data);
2176 switch (cache->type) { 2181 switch (cache->type) {
2177 case NETLBL_CACHE_T_MLS: 2182 case NETLBL_CACHE_T_MLS:
2178 ebitmap_destroy(&cache->data.mls_label.level[0].cat); 2183 ebitmap_destroy(&cache->data.mls_label.level[0].cat);
@@ -2197,17 +2202,20 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx)
2197 struct netlbl_lsm_secattr secattr; 2202 struct netlbl_lsm_secattr secattr;
2198 2203
2199 netlbl_secattr_init(&secattr); 2204 netlbl_secattr_init(&secattr);
2205 secattr.cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
2206 if (secattr.cache == NULL)
2207 goto netlbl_cache_add_return;
2200 2208
2201 cache = kzalloc(sizeof(*cache), GFP_ATOMIC); 2209 cache = kzalloc(sizeof(*cache), GFP_ATOMIC);
2202 if (cache == NULL) 2210 if (cache == NULL)
2203 goto netlbl_cache_add_failure; 2211 goto netlbl_cache_add_return;
2204 secattr.cache.free = selinux_netlbl_cache_free; 2212 secattr.cache->free = selinux_netlbl_cache_free;
2205 secattr.cache.data = (void *)cache; 2213 secattr.cache->data = (void *)cache;
2206 2214
2207 cache->type = NETLBL_CACHE_T_MLS; 2215 cache->type = NETLBL_CACHE_T_MLS;
2208 if (ebitmap_cpy(&cache->data.mls_label.level[0].cat, 2216 if (ebitmap_cpy(&cache->data.mls_label.level[0].cat,
2209 &ctx->range.level[0].cat) != 0) 2217 &ctx->range.level[0].cat) != 0)
2210 goto netlbl_cache_add_failure; 2218 goto netlbl_cache_add_return;
2211 cache->data.mls_label.level[1].cat.highbit = 2219 cache->data.mls_label.level[1].cat.highbit =
2212 cache->data.mls_label.level[0].cat.highbit; 2220 cache->data.mls_label.level[0].cat.highbit;
2213 cache->data.mls_label.level[1].cat.node = 2221 cache->data.mls_label.level[1].cat.node =
@@ -2215,13 +2223,10 @@ static void selinux_netlbl_cache_add(struct sk_buff *skb, struct context *ctx)
2215 cache->data.mls_label.level[0].sens = ctx->range.level[0].sens; 2223 cache->data.mls_label.level[0].sens = ctx->range.level[0].sens;
2216 cache->data.mls_label.level[1].sens = ctx->range.level[0].sens; 2224 cache->data.mls_label.level[1].sens = ctx->range.level[0].sens;
2217 2225
2218 if (netlbl_cache_add(skb, &secattr) != 0) 2226 netlbl_cache_add(skb, &secattr);
2219 goto netlbl_cache_add_failure;
2220
2221 return;
2222 2227
2223netlbl_cache_add_failure: 2228netlbl_cache_add_return:
2224 netlbl_secattr_destroy(&secattr, 1); 2229 netlbl_secattr_destroy(&secattr);
2225} 2230}
2226 2231
2227/** 2232/**
@@ -2263,8 +2268,8 @@ static int selinux_netlbl_secattr_to_sid(struct sk_buff *skb,
2263 2268
2264 POLICY_RDLOCK; 2269 POLICY_RDLOCK;
2265 2270
2266 if (secattr->cache.data) { 2271 if (secattr->cache) {
2267 cache = NETLBL_CACHE(secattr->cache.data); 2272 cache = NETLBL_CACHE(secattr->cache->data);
2268 switch (cache->type) { 2273 switch (cache->type) {
2269 case NETLBL_CACHE_T_SID: 2274 case NETLBL_CACHE_T_SID:
2270 *sid = cache->data.sid; 2275 *sid = cache->data.sid;
@@ -2369,7 +2374,7 @@ static int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
2369 &secattr, 2374 &secattr,
2370 base_sid, 2375 base_sid,
2371 sid); 2376 sid);
2372 netlbl_secattr_destroy(&secattr, 0); 2377 netlbl_secattr_destroy(&secattr);
2373 2378
2374 return rc; 2379 return rc;
2375} 2380}
@@ -2415,7 +2420,7 @@ static int selinux_netlbl_socket_setsid(struct socket *sock, u32 sid)
2415 if (rc == 0) 2420 if (rc == 0)
2416 sksec->nlbl_state = NLBL_LABELED; 2421 sksec->nlbl_state = NLBL_LABELED;
2417 2422
2418 netlbl_secattr_destroy(&secattr, 0); 2423 netlbl_secattr_destroy(&secattr);
2419 2424
2420netlbl_socket_setsid_return: 2425netlbl_socket_setsid_return:
2421 POLICY_RDUNLOCK; 2426 POLICY_RDUNLOCK;
@@ -2517,7 +2522,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock)
2517 sksec->sid, 2522 sksec->sid,
2518 &nlbl_peer_sid) == 0) 2523 &nlbl_peer_sid) == 0)
2519 sksec->peer_sid = nlbl_peer_sid; 2524 sksec->peer_sid = nlbl_peer_sid;
2520 netlbl_secattr_destroy(&secattr, 0); 2525 netlbl_secattr_destroy(&secattr);
2521 2526
2522 sksec->nlbl_state = NLBL_REQUIRE; 2527 sksec->nlbl_state = NLBL_REQUIRE;
2523 2528