aboutsummaryrefslogtreecommitdiffstats
path: root/security/keys/user_defined.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2005-06-24 01:00:49 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-24 03:05:18 -0400
commit76d8aeabfeb1c42641a81c44280177b9a08670d8 (patch)
tree0a584439bb44e440717aa77a1398ba9eea24a137 /security/keys/user_defined.c
parent7286aa9b9ab35f20b1ff16d867f4535701df99b5 (diff)
[PATCH] keys: Discard key spinlock and use RCU for key payload
The attached patch changes the key implementation in a number of ways: (1) It removes the spinlock from the key structure. (2) The key flags are now accessed using atomic bitops instead of write-locking the key spinlock and using C bitwise operators. The three instantiation flags are dealt with with the construction semaphore held during the request_key/instantiate/negate sequence, thus rendering the spinlock superfluous. The key flags are also now bit numbers not bit masks. (3) The key payload is now accessed using RCU. This permits the recursive keyring search algorithm to be simplified greatly since no locks need be taken other than the usual RCU preemption disablement. Searching now does not require any locks or semaphores to be held; merely that the starting keyring be pinned. (4) The keyring payload now includes an RCU head so that it can be disposed of by call_rcu(). This requires that the payload be copied on unlink to prevent introducing races in copy-down vs search-up. (5) The user key payload is now a structure with the data following it. It includes an RCU head like the keyring payload and for the same reason. It also contains a data length because the data length in the key may be changed on another CPU whilst an RCU protected read is in progress on the payload. This would then see the supposed RCU payload and the on-key data length getting out of sync. I'm tempted to drop the key's datalen entirely, except that it's used in conjunction with quota management and so is a little tricky to get rid of. (6) Update the keys documentation. Signed-Off-By: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'security/keys/user_defined.c')
-rw-r--r--security/keys/user_defined.c85
1 files changed, 61 insertions, 24 deletions
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 8d65b3a28129..c33d3614a0db 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -42,12 +42,19 @@ struct key_type key_type_user = {
42 .read = user_read, 42 .read = user_read,
43}; 43};
44 44
45struct user_key_payload {
46 struct rcu_head rcu; /* RCU destructor */
47 unsigned short datalen; /* length of this data */
48 char data[0]; /* actual data */
49};
50
45/*****************************************************************************/ 51/*****************************************************************************/
46/* 52/*
47 * instantiate a user defined key 53 * instantiate a user defined key
48 */ 54 */
49static int user_instantiate(struct key *key, const void *data, size_t datalen) 55static int user_instantiate(struct key *key, const void *data, size_t datalen)
50{ 56{
57 struct user_key_payload *upayload;
51 int ret; 58 int ret;
52 59
53 ret = -EINVAL; 60 ret = -EINVAL;
@@ -58,13 +65,15 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen)
58 if (ret < 0) 65 if (ret < 0)
59 goto error; 66 goto error;
60 67
61 /* attach the data */
62 ret = -ENOMEM; 68 ret = -ENOMEM;
63 key->payload.data = kmalloc(datalen, GFP_KERNEL); 69 upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
64 if (!key->payload.data) 70 if (!upayload)
65 goto error; 71 goto error;
66 72
67 memcpy(key->payload.data, data, datalen); 73 /* attach the data */
74 upayload->datalen = datalen;
75 memcpy(upayload->data, data, datalen);
76 rcu_assign_pointer(key->payload.data, upayload);
68 ret = 0; 77 ret = 0;
69 78
70 error: 79 error:
@@ -75,18 +84,25 @@ static int user_instantiate(struct key *key, const void *data, size_t datalen)
75/*****************************************************************************/ 84/*****************************************************************************/
76/* 85/*
77 * duplicate a user defined key 86 * duplicate a user defined key
87 * - both keys' semaphores are locked against further modification
88 * - the new key cannot yet be accessed
78 */ 89 */
79static int user_duplicate(struct key *key, const struct key *source) 90static int user_duplicate(struct key *key, const struct key *source)
80{ 91{
92 struct user_key_payload *upayload, *spayload;
81 int ret; 93 int ret;
82 94
83 /* just copy the payload */ 95 /* just copy the payload */
84 ret = -ENOMEM; 96 ret = -ENOMEM;
85 key->payload.data = kmalloc(source->datalen, GFP_KERNEL); 97 upayload = kmalloc(sizeof(*upayload) + source->datalen, GFP_KERNEL);
98 if (upayload) {
99 spayload = rcu_dereference(source->payload.data);
100 BUG_ON(source->datalen != spayload->datalen);
86 101
87 if (key->payload.data) { 102 upayload->datalen = key->datalen = spayload->datalen;
88 key->datalen = source->datalen; 103 memcpy(upayload->data, spayload->data, key->datalen);
89 memcpy(key->payload.data, source->payload.data, source->datalen); 104
105 key->payload.data = upayload;
90 ret = 0; 106 ret = 0;
91 } 107 }
92 108
@@ -96,40 +112,54 @@ static int user_duplicate(struct key *key, const struct key *source)
96 112
97/*****************************************************************************/ 113/*****************************************************************************/
98/* 114/*
115 * dispose of the old data from an updated user defined key
116 */
117static void user_update_rcu_disposal(struct rcu_head *rcu)
118{
119 struct user_key_payload *upayload;
120
121 upayload = container_of(rcu, struct user_key_payload, rcu);
122
123 kfree(upayload);
124
125} /* end user_update_rcu_disposal() */
126
127/*****************************************************************************/
128/*
99 * update a user defined key 129 * update a user defined key
130 * - the key's semaphore is write-locked
100 */ 131 */
101static int user_update(struct key *key, const void *data, size_t datalen) 132static int user_update(struct key *key, const void *data, size_t datalen)
102{ 133{
103 void *new, *zap; 134 struct user_key_payload *upayload, *zap;
104 int ret; 135 int ret;
105 136
106 ret = -EINVAL; 137 ret = -EINVAL;
107 if (datalen <= 0 || datalen > 32767 || !data) 138 if (datalen <= 0 || datalen > 32767 || !data)
108 goto error; 139 goto error;
109 140
110 /* copy the data */ 141 /* construct a replacement payload */
111 ret = -ENOMEM; 142 ret = -ENOMEM;
112 new = kmalloc(datalen, GFP_KERNEL); 143 upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
113 if (!new) 144 if (!upayload)
114 goto error; 145 goto error;
115 146
116 memcpy(new, data, datalen); 147 upayload->datalen = datalen;
148 memcpy(upayload->data, data, datalen);
117 149
118 /* check the quota and attach the new data */ 150 /* check the quota and attach the new data */
119 zap = new; 151 zap = upayload;
120 write_lock(&key->lock);
121 152
122 ret = key_payload_reserve(key, datalen); 153 ret = key_payload_reserve(key, datalen);
123 154
124 if (ret == 0) { 155 if (ret == 0) {
125 /* attach the new data, displacing the old */ 156 /* attach the new data, displacing the old */
126 zap = key->payload.data; 157 zap = key->payload.data;
127 key->payload.data = new; 158 rcu_assign_pointer(key->payload.data, upayload);
128 key->expiry = 0; 159 key->expiry = 0;
129 } 160 }
130 161
131 write_unlock(&key->lock); 162 call_rcu(&zap->rcu, user_update_rcu_disposal);
132 kfree(zap);
133 163
134 error: 164 error:
135 return ret; 165 return ret;
@@ -152,13 +182,15 @@ static int user_match(const struct key *key, const void *description)
152 */ 182 */
153static void user_destroy(struct key *key) 183static void user_destroy(struct key *key)
154{ 184{
155 kfree(key->payload.data); 185 struct user_key_payload *upayload = key->payload.data;
186
187 kfree(upayload);
156 188
157} /* end user_destroy() */ 189} /* end user_destroy() */
158 190
159/*****************************************************************************/ 191/*****************************************************************************/
160/* 192/*
161 * describe the user 193 * describe the user key
162 */ 194 */
163static void user_describe(const struct key *key, struct seq_file *m) 195static void user_describe(const struct key *key, struct seq_file *m)
164{ 196{
@@ -171,18 +203,23 @@ static void user_describe(const struct key *key, struct seq_file *m)
171/*****************************************************************************/ 203/*****************************************************************************/
172/* 204/*
173 * read the key data 205 * read the key data
206 * - the key's semaphore is read-locked
174 */ 207 */
175static long user_read(const struct key *key, 208static long user_read(const struct key *key,
176 char __user *buffer, size_t buflen) 209 char __user *buffer, size_t buflen)
177{ 210{
178 long ret = key->datalen; 211 struct user_key_payload *upayload;
212 long ret;
213
214 upayload = rcu_dereference(key->payload.data);
215 ret = upayload->datalen;
179 216
180 /* we can return the data as is */ 217 /* we can return the data as is */
181 if (buffer && buflen > 0) { 218 if (buffer && buflen > 0) {
182 if (buflen > key->datalen) 219 if (buflen > upayload->datalen)
183 buflen = key->datalen; 220 buflen = upayload->datalen;
184 221
185 if (copy_to_user(buffer, key->payload.data, buflen) != 0) 222 if (copy_to_user(buffer, upayload->data, buflen) != 0)
186 ret = -EFAULT; 223 ret = -EFAULT;
187 } 224 }
188 225