aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndi Kleen <andi@firstfloor.org>2010-01-15 20:01:15 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-01-16 15:15:38 -0500
commit64ce1037c5434b1d036cd99ecaee6e00496bc2e9 (patch)
tree37a8c8f1dccdcdb3d00bf07d36d86fe6ea05f3f9
parent8ecc2951534af10e04ddb5e5ff5c6d217b79f5c2 (diff)
kfifo: sanitize *_user error handling
Right now for kfifo_*_user it's not easily possible to distingush between a user copy failing and the FIFO not containing enough data. The problem is that both conditions are multiplexed into the same return code. Avoid this by moving the "copy length" into a separate output parameter and only return 0/-EFAULT in the main return value. I didn't fully adapt the weird "record" variants, those seem to be unused anyways and were rather messy (should they be just removed?) I would appreciate some double checking if I did all the conversions correctly. Signed-off-by: Andi Kleen <ak@linux.intel.com> Cc: Stefani Seibold <stefani@seibold.net> Cc: Roland Dreier <rdreier@cisco.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Andy Walls <awalls@radix.net> Cc: Vikram Dhillon <dhillonv10@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/kfifo.h8
-rw-r--r--kernel/kfifo.c76
2 files changed, 53 insertions, 31 deletions
diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 6fb495ea956a..86ad50a900c8 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -235,11 +235,11 @@ static inline __must_check unsigned int kfifo_out_locked(struct kfifo *fifo,
235 235
236extern void kfifo_skip(struct kfifo *fifo, unsigned int len); 236extern void kfifo_skip(struct kfifo *fifo, unsigned int len);
237 237
238extern __must_check unsigned int kfifo_from_user(struct kfifo *fifo, 238extern __must_check int kfifo_from_user(struct kfifo *fifo,
239 const void __user *from, unsigned int n); 239 const void __user *from, unsigned int n, unsigned *lenout);
240 240
241extern __must_check unsigned int kfifo_to_user(struct kfifo *fifo, 241extern __must_check int kfifo_to_user(struct kfifo *fifo,
242 void __user *to, unsigned int n); 242 void __user *to, unsigned int n, unsigned *lenout);
243 243
244/* 244/*
245 * __kfifo_add_out internal helper function for updating the out offset 245 * __kfifo_add_out internal helper function for updating the out offset
diff --git a/kernel/kfifo.c b/kernel/kfifo.c
index ab615e695052..b50bb622e8b0 100644
--- a/kernel/kfifo.c
+++ b/kernel/kfifo.c
@@ -159,8 +159,9 @@ static inline void __kfifo_out_data(struct kfifo *fifo,
159 memcpy(to + l, fifo->buffer, len - l); 159 memcpy(to + l, fifo->buffer, len - l);
160} 160}
161 161
162static inline unsigned int __kfifo_from_user_data(struct kfifo *fifo, 162static inline int __kfifo_from_user_data(struct kfifo *fifo,
163 const void __user *from, unsigned int len, unsigned int off) 163 const void __user *from, unsigned int len, unsigned int off,
164 unsigned *lenout)
164{ 165{
165 unsigned int l; 166 unsigned int l;
166 int ret; 167 int ret;
@@ -177,16 +178,20 @@ static inline unsigned int __kfifo_from_user_data(struct kfifo *fifo,
177 /* first put the data starting from fifo->in to buffer end */ 178 /* first put the data starting from fifo->in to buffer end */
178 l = min(len, fifo->size - off); 179 l = min(len, fifo->size - off);
179 ret = copy_from_user(fifo->buffer + off, from, l); 180 ret = copy_from_user(fifo->buffer + off, from, l);
180 181 if (unlikely(ret)) {
181 if (unlikely(ret)) 182 *lenout = ret;
182 return ret + len - l; 183 return -EFAULT;
184 }
185 *lenout = l;
183 186
184 /* then put the rest (if any) at the beginning of the buffer */ 187 /* then put the rest (if any) at the beginning of the buffer */
185 return copy_from_user(fifo->buffer, from + l, len - l); 188 ret = copy_from_user(fifo->buffer, from + l, len - l);
189 *lenout += ret ? ret : len - l;
190 return ret ? -EFAULT : 0;
186} 191}
187 192
188static inline unsigned int __kfifo_to_user_data(struct kfifo *fifo, 193static inline int __kfifo_to_user_data(struct kfifo *fifo,
189 void __user *to, unsigned int len, unsigned int off) 194 void __user *to, unsigned int len, unsigned int off, unsigned *lenout)
190{ 195{
191 unsigned int l; 196 unsigned int l;
192 int ret; 197 int ret;
@@ -203,12 +208,21 @@ static inline unsigned int __kfifo_to_user_data(struct kfifo *fifo,
203 /* first get the data from fifo->out until the end of the buffer */ 208 /* first get the data from fifo->out until the end of the buffer */
204 l = min(len, fifo->size - off); 209 l = min(len, fifo->size - off);
205 ret = copy_to_user(to, fifo->buffer + off, l); 210 ret = copy_to_user(to, fifo->buffer + off, l);
206 211 *lenout = l;
207 if (unlikely(ret)) 212 if (unlikely(ret)) {
208 return ret + len - l; 213 *lenout -= ret;
214 return -EFAULT;
215 }
209 216
210 /* then get the rest (if any) from the beginning of the buffer */ 217 /* then get the rest (if any) from the beginning of the buffer */
211 return copy_to_user(to + l, fifo->buffer, len - l); 218 len -= l;
219 ret = copy_to_user(to + l, fifo->buffer, len);
220 if (unlikely(ret)) {
221 *lenout += len - ret;
222 return -EFAULT;
223 }
224 *lenout += len;
225 return 0;
212} 226}
213 227
214unsigned int __kfifo_in_n(struct kfifo *fifo, 228unsigned int __kfifo_in_n(struct kfifo *fifo,
@@ -299,10 +313,13 @@ EXPORT_SYMBOL(__kfifo_out_generic);
299unsigned int __kfifo_from_user_n(struct kfifo *fifo, 313unsigned int __kfifo_from_user_n(struct kfifo *fifo,
300 const void __user *from, unsigned int len, unsigned int recsize) 314 const void __user *from, unsigned int len, unsigned int recsize)
301{ 315{
316 unsigned total;
317
302 if (kfifo_avail(fifo) < len + recsize) 318 if (kfifo_avail(fifo) < len + recsize)
303 return len + 1; 319 return len + 1;
304 320
305 return __kfifo_from_user_data(fifo, from, len, recsize); 321 __kfifo_from_user_data(fifo, from, len, recsize, &total);
322 return total;
306} 323}
307EXPORT_SYMBOL(__kfifo_from_user_n); 324EXPORT_SYMBOL(__kfifo_from_user_n);
308 325
@@ -313,18 +330,21 @@ EXPORT_SYMBOL(__kfifo_from_user_n);
313 * @len: the length of the data to be added. 330 * @len: the length of the data to be added.
314 * 331 *
315 * This function copies at most @len bytes from the @from into the 332 * This function copies at most @len bytes from the @from into the
316 * FIFO depending and returns the number of copied bytes. 333 * FIFO depending and returns -EFAULT/0.
317 * 334 *
318 * Note that with only one concurrent reader and one concurrent 335 * Note that with only one concurrent reader and one concurrent
319 * writer, you don't need extra locking to use these functions. 336 * writer, you don't need extra locking to use these functions.
320 */ 337 */
321unsigned int kfifo_from_user(struct kfifo *fifo, 338int kfifo_from_user(struct kfifo *fifo,
322 const void __user *from, unsigned int len) 339 const void __user *from, unsigned int len, unsigned *total)
323{ 340{
341 int ret;
324 len = min(kfifo_avail(fifo), len); 342 len = min(kfifo_avail(fifo), len);
325 len -= __kfifo_from_user_data(fifo, from, len, 0); 343 ret = __kfifo_from_user_data(fifo, from, len, 0, total);
344 if (ret)
345 return ret;
326 __kfifo_add_in(fifo, len); 346 __kfifo_add_in(fifo, len);
327 return len; 347 return 0;
328} 348}
329EXPORT_SYMBOL(kfifo_from_user); 349EXPORT_SYMBOL(kfifo_from_user);
330 350
@@ -339,17 +359,17 @@ unsigned int __kfifo_to_user_n(struct kfifo *fifo,
339 void __user *to, unsigned int len, unsigned int reclen, 359 void __user *to, unsigned int len, unsigned int reclen,
340 unsigned int recsize) 360 unsigned int recsize)
341{ 361{
342 unsigned int ret; 362 unsigned int ret, total;
343 363
344 if (kfifo_len(fifo) < reclen + recsize) 364 if (kfifo_len(fifo) < reclen + recsize)
345 return len; 365 return len;
346 366
347 ret = __kfifo_to_user_data(fifo, to, reclen, recsize); 367 ret = __kfifo_to_user_data(fifo, to, reclen, recsize, &total);
348 368
349 if (likely(ret == 0)) 369 if (likely(ret == 0))
350 __kfifo_add_out(fifo, reclen + recsize); 370 __kfifo_add_out(fifo, reclen + recsize);
351 371
352 return ret; 372 return total;
353} 373}
354EXPORT_SYMBOL(__kfifo_to_user_n); 374EXPORT_SYMBOL(__kfifo_to_user_n);
355 375
@@ -358,20 +378,22 @@ EXPORT_SYMBOL(__kfifo_to_user_n);
358 * @fifo: the fifo to be used. 378 * @fifo: the fifo to be used.
359 * @to: where the data must be copied. 379 * @to: where the data must be copied.
360 * @len: the size of the destination buffer. 380 * @len: the size of the destination buffer.
381 @ @lenout: pointer to output variable with copied data
361 * 382 *
362 * This function copies at most @len bytes from the FIFO into the 383 * This function copies at most @len bytes from the FIFO into the
363 * @to buffer and returns the number of copied bytes. 384 * @to buffer and 0 or -EFAULT.
364 * 385 *
365 * Note that with only one concurrent reader and one concurrent 386 * Note that with only one concurrent reader and one concurrent
366 * writer, you don't need extra locking to use these functions. 387 * writer, you don't need extra locking to use these functions.
367 */ 388 */
368unsigned int kfifo_to_user(struct kfifo *fifo, 389int kfifo_to_user(struct kfifo *fifo,
369 void __user *to, unsigned int len) 390 void __user *to, unsigned int len, unsigned *lenout)
370{ 391{
392 int ret;
371 len = min(kfifo_len(fifo), len); 393 len = min(kfifo_len(fifo), len);
372 len -= __kfifo_to_user_data(fifo, to, len, 0); 394 ret = __kfifo_to_user_data(fifo, to, len, 0, lenout);
373 __kfifo_add_out(fifo, len); 395 __kfifo_add_out(fifo, *lenout);
374 return len; 396 return ret;
375} 397}
376EXPORT_SYMBOL(kfifo_to_user); 398EXPORT_SYMBOL(kfifo_to_user);
377 399