diff options
author | Reinette Chatre <reinette.chatre@linux.intel.com> | 2006-10-11 04:21:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-11 14:14:22 -0400 |
commit | 01a3ee2b203e511e20f98b85a9172fd32c53e87c (patch) | |
tree | 0dd90d81dc86f231828af23bdb97522405b06cab | |
parent | 39484e53bb00f55b6303a908070db133608ef2a5 (diff) |
[PATCH] bitmap: parse input from kernel and user buffers
lib/bitmap.c:bitmap_parse() is a library function that received as input a
user buffer. This seemed to have originated from the way the write_proc
function of the /proc filesystem operates.
This has been reworked to not use kmalloc and eliminates a lot of
get_user() overhead by performing one access_ok before using __get_user().
We need to test if we are in kernel or user space (is_user) and access the
buffer differently. We cannot use __get_user() to access kernel addresses
in all cases, for example in architectures with separate address space for
kernel and user.
This function will be useful for other uses as well; for example, taking
input for /sysfs instead of /proc, so it was changed to accept kernel
buffers. We have this use for the Linux UWB project, as part as the
upcoming bandwidth allocator code.
Only a few routines used this function and they were changed too.
Signed-off-by: Reinette Chatre <reinette.chatre@linux.intel.com>
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
Cc: Paul Jackson <pj@sgi.com>
Cc: Joe Korty <joe.korty@ccur.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | include/linux/bitmap.h | 13 | ||||
-rw-r--r-- | include/linux/cpumask.h | 14 | ||||
-rw-r--r-- | include/linux/nodemask.h | 14 | ||||
-rw-r--r-- | kernel/irq/proc.c | 2 | ||||
-rw-r--r-- | kernel/profile.c | 2 | ||||
-rw-r--r-- | lib/bitmap.c | 54 |
6 files changed, 70 insertions, 29 deletions
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index dcc5de7cc48..64b4641904f 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h | |||
@@ -46,7 +46,8 @@ | |||
46 | * bitmap_remap(dst, src, old, new, nbits) *dst = map(old, new)(src) | 46 | * bitmap_remap(dst, src, old, new, nbits) *dst = map(old, new)(src) |
47 | * bitmap_bitremap(oldbit, old, new, nbits) newbit = map(old, new)(oldbit) | 47 | * bitmap_bitremap(oldbit, old, new, nbits) newbit = map(old, new)(oldbit) |
48 | * bitmap_scnprintf(buf, len, src, nbits) Print bitmap src to buf | 48 | * bitmap_scnprintf(buf, len, src, nbits) Print bitmap src to buf |
49 | * bitmap_parse(ubuf, ulen, dst, nbits) Parse bitmap dst from user buf | 49 | * bitmap_parse(buf, buflen, dst, nbits) Parse bitmap dst from kernel buf |
50 | * bitmap_parse_user(ubuf, ulen, dst, nbits) Parse bitmap dst from user buf | ||
50 | * bitmap_scnlistprintf(buf, len, src, nbits) Print bitmap src as list to buf | 51 | * bitmap_scnlistprintf(buf, len, src, nbits) Print bitmap src as list to buf |
51 | * bitmap_parselist(buf, dst, nbits) Parse bitmap dst from list | 52 | * bitmap_parselist(buf, dst, nbits) Parse bitmap dst from list |
52 | * bitmap_find_free_region(bitmap, bits, order) Find and allocate bit region | 53 | * bitmap_find_free_region(bitmap, bits, order) Find and allocate bit region |
@@ -106,7 +107,9 @@ extern int __bitmap_weight(const unsigned long *bitmap, int bits); | |||
106 | 107 | ||
107 | extern int bitmap_scnprintf(char *buf, unsigned int len, | 108 | extern int bitmap_scnprintf(char *buf, unsigned int len, |
108 | const unsigned long *src, int nbits); | 109 | const unsigned long *src, int nbits); |
109 | extern int bitmap_parse(const char __user *ubuf, unsigned int ulen, | 110 | extern int __bitmap_parse(const char *buf, unsigned int buflen, int is_user, |
111 | unsigned long *dst, int nbits); | ||
112 | extern int bitmap_parse_user(const char __user *ubuf, unsigned int ulen, | ||
110 | unsigned long *dst, int nbits); | 113 | unsigned long *dst, int nbits); |
111 | extern int bitmap_scnlistprintf(char *buf, unsigned int len, | 114 | extern int bitmap_scnlistprintf(char *buf, unsigned int len, |
112 | const unsigned long *src, int nbits); | 115 | const unsigned long *src, int nbits); |
@@ -270,6 +273,12 @@ static inline void bitmap_shift_left(unsigned long *dst, | |||
270 | __bitmap_shift_left(dst, src, n, nbits); | 273 | __bitmap_shift_left(dst, src, n, nbits); |
271 | } | 274 | } |
272 | 275 | ||
276 | static inline int bitmap_parse(const char *buf, unsigned int buflen, | ||
277 | unsigned long *maskp, int nmaskbits) | ||
278 | { | ||
279 | return __bitmap_parse(buf, buflen, 0, maskp, nmaskbits); | ||
280 | } | ||
281 | |||
273 | #endif /* __ASSEMBLY__ */ | 282 | #endif /* __ASSEMBLY__ */ |
274 | 283 | ||
275 | #endif /* __LINUX_BITMAP_H */ | 284 | #endif /* __LINUX_BITMAP_H */ |
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index b268a3c0c37..d0e8c8b0e34 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h | |||
@@ -8,8 +8,8 @@ | |||
8 | * See detailed comments in the file linux/bitmap.h describing the | 8 | * See detailed comments in the file linux/bitmap.h describing the |
9 | * data type on which these cpumasks are based. | 9 | * data type on which these cpumasks are based. |
10 | * | 10 | * |
11 | * For details of cpumask_scnprintf() and cpumask_parse(), | 11 | * For details of cpumask_scnprintf() and cpumask_parse_user(), |
12 | * see bitmap_scnprintf() and bitmap_parse() in lib/bitmap.c. | 12 | * see bitmap_scnprintf() and bitmap_parse_user() in lib/bitmap.c. |
13 | * For details of cpulist_scnprintf() and cpulist_parse(), see | 13 | * For details of cpulist_scnprintf() and cpulist_parse(), see |
14 | * bitmap_scnlistprintf() and bitmap_parselist(), also in bitmap.c. | 14 | * bitmap_scnlistprintf() and bitmap_parselist(), also in bitmap.c. |
15 | * For details of cpu_remap(), see bitmap_bitremap in lib/bitmap.c | 15 | * For details of cpu_remap(), see bitmap_bitremap in lib/bitmap.c |
@@ -49,7 +49,7 @@ | |||
49 | * unsigned long *cpus_addr(mask) Array of unsigned long's in mask | 49 | * unsigned long *cpus_addr(mask) Array of unsigned long's in mask |
50 | * | 50 | * |
51 | * int cpumask_scnprintf(buf, len, mask) Format cpumask for printing | 51 | * int cpumask_scnprintf(buf, len, mask) Format cpumask for printing |
52 | * int cpumask_parse(ubuf, ulen, mask) Parse ascii string as cpumask | 52 | * int cpumask_parse_user(ubuf, ulen, mask) Parse ascii string as cpumask |
53 | * int cpulist_scnprintf(buf, len, mask) Format cpumask as list for printing | 53 | * int cpulist_scnprintf(buf, len, mask) Format cpumask as list for printing |
54 | * int cpulist_parse(buf, map) Parse ascii string as cpulist | 54 | * int cpulist_parse(buf, map) Parse ascii string as cpulist |
55 | * int cpu_remap(oldbit, old, new) newbit = map(old, new)(oldbit) | 55 | * int cpu_remap(oldbit, old, new) newbit = map(old, new)(oldbit) |
@@ -273,12 +273,12 @@ static inline int __cpumask_scnprintf(char *buf, int len, | |||
273 | return bitmap_scnprintf(buf, len, srcp->bits, nbits); | 273 | return bitmap_scnprintf(buf, len, srcp->bits, nbits); |
274 | } | 274 | } |
275 | 275 | ||
276 | #define cpumask_parse(ubuf, ulen, dst) \ | 276 | #define cpumask_parse_user(ubuf, ulen, dst) \ |
277 | __cpumask_parse((ubuf), (ulen), &(dst), NR_CPUS) | 277 | __cpumask_parse_user((ubuf), (ulen), &(dst), NR_CPUS) |
278 | static inline int __cpumask_parse(const char __user *buf, int len, | 278 | static inline int __cpumask_parse_user(const char __user *buf, int len, |
279 | cpumask_t *dstp, int nbits) | 279 | cpumask_t *dstp, int nbits) |
280 | { | 280 | { |
281 | return bitmap_parse(buf, len, dstp->bits, nbits); | 281 | return bitmap_parse_user(buf, len, dstp->bits, nbits); |
282 | } | 282 | } |
283 | 283 | ||
284 | #define cpulist_scnprintf(buf, len, src) \ | 284 | #define cpulist_scnprintf(buf, len, src) \ |
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h index 5dce5c21822..b1063e9cdb1 100644 --- a/include/linux/nodemask.h +++ b/include/linux/nodemask.h | |||
@@ -8,8 +8,8 @@ | |||
8 | * See detailed comments in the file linux/bitmap.h describing the | 8 | * See detailed comments in the file linux/bitmap.h describing the |
9 | * data type on which these nodemasks are based. | 9 | * data type on which these nodemasks are based. |
10 | * | 10 | * |
11 | * For details of nodemask_scnprintf() and nodemask_parse(), | 11 | * For details of nodemask_scnprintf() and nodemask_parse_user(), |
12 | * see bitmap_scnprintf() and bitmap_parse() in lib/bitmap.c. | 12 | * see bitmap_scnprintf() and bitmap_parse_user() in lib/bitmap.c. |
13 | * For details of nodelist_scnprintf() and nodelist_parse(), see | 13 | * For details of nodelist_scnprintf() and nodelist_parse(), see |
14 | * bitmap_scnlistprintf() and bitmap_parselist(), also in bitmap.c. | 14 | * bitmap_scnlistprintf() and bitmap_parselist(), also in bitmap.c. |
15 | * For details of node_remap(), see bitmap_bitremap in lib/bitmap.c. | 15 | * For details of node_remap(), see bitmap_bitremap in lib/bitmap.c. |
@@ -51,7 +51,7 @@ | |||
51 | * unsigned long *nodes_addr(mask) Array of unsigned long's in mask | 51 | * unsigned long *nodes_addr(mask) Array of unsigned long's in mask |
52 | * | 52 | * |
53 | * int nodemask_scnprintf(buf, len, mask) Format nodemask for printing | 53 | * int nodemask_scnprintf(buf, len, mask) Format nodemask for printing |
54 | * int nodemask_parse(ubuf, ulen, mask) Parse ascii string as nodemask | 54 | * int nodemask_parse_user(ubuf, ulen, mask) Parse ascii string as nodemask |
55 | * int nodelist_scnprintf(buf, len, mask) Format nodemask as list for printing | 55 | * int nodelist_scnprintf(buf, len, mask) Format nodemask as list for printing |
56 | * int nodelist_parse(buf, map) Parse ascii string as nodelist | 56 | * int nodelist_parse(buf, map) Parse ascii string as nodelist |
57 | * int node_remap(oldbit, old, new) newbit = map(old, new)(oldbit) | 57 | * int node_remap(oldbit, old, new) newbit = map(old, new)(oldbit) |
@@ -288,12 +288,12 @@ static inline int __nodemask_scnprintf(char *buf, int len, | |||
288 | return bitmap_scnprintf(buf, len, srcp->bits, nbits); | 288 | return bitmap_scnprintf(buf, len, srcp->bits, nbits); |
289 | } | 289 | } |
290 | 290 | ||
291 | #define nodemask_parse(ubuf, ulen, dst) \ | 291 | #define nodemask_parse_user(ubuf, ulen, dst) \ |
292 | __nodemask_parse((ubuf), (ulen), &(dst), MAX_NUMNODES) | 292 | __nodemask_parse_user((ubuf), (ulen), &(dst), MAX_NUMNODES) |
293 | static inline int __nodemask_parse(const char __user *buf, int len, | 293 | static inline int __nodemask_parse_user(const char __user *buf, int len, |
294 | nodemask_t *dstp, int nbits) | 294 | nodemask_t *dstp, int nbits) |
295 | { | 295 | { |
296 | return bitmap_parse(buf, len, dstp->bits, nbits); | 296 | return bitmap_parse_user(buf, len, dstp->bits, nbits); |
297 | } | 297 | } |
298 | 298 | ||
299 | #define nodelist_scnprintf(buf, len, src) \ | 299 | #define nodelist_scnprintf(buf, len, src) \ |
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 607c7809ad0..9a352667007 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c | |||
@@ -57,7 +57,7 @@ static int irq_affinity_write_proc(struct file *file, const char __user *buffer, | |||
57 | if (!irq_desc[irq].chip->set_affinity || no_irq_affinity) | 57 | if (!irq_desc[irq].chip->set_affinity || no_irq_affinity) |
58 | return -EIO; | 58 | return -EIO; |
59 | 59 | ||
60 | err = cpumask_parse(buffer, count, new_value); | 60 | err = cpumask_parse_user(buffer, count, new_value); |
61 | if (err) | 61 | if (err) |
62 | return err; | 62 | return err; |
63 | 63 | ||
diff --git a/kernel/profile.c b/kernel/profile.c index 857300a2afe..f940b462eec 100644 --- a/kernel/profile.c +++ b/kernel/profile.c | |||
@@ -399,7 +399,7 @@ static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffe | |||
399 | unsigned long full_count = count, err; | 399 | unsigned long full_count = count, err; |
400 | cpumask_t new_value; | 400 | cpumask_t new_value; |
401 | 401 | ||
402 | err = cpumask_parse(buffer, count, new_value); | 402 | err = cpumask_parse_user(buffer, count, new_value); |
403 | if (err) | 403 | if (err) |
404 | return err; | 404 | return err; |
405 | 405 | ||
diff --git a/lib/bitmap.c b/lib/bitmap.c index d71e38c54ea..037fa9aa2ed 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c | |||
@@ -316,10 +316,11 @@ int bitmap_scnprintf(char *buf, unsigned int buflen, | |||
316 | EXPORT_SYMBOL(bitmap_scnprintf); | 316 | EXPORT_SYMBOL(bitmap_scnprintf); |
317 | 317 | ||
318 | /** | 318 | /** |
319 | * bitmap_parse - convert an ASCII hex string into a bitmap. | 319 | * __bitmap_parse - convert an ASCII hex string into a bitmap. |
320 | * @ubuf: pointer to buffer in user space containing string. | 320 | * @buf: pointer to buffer containing string. |
321 | * @ubuflen: buffer size in bytes. If string is smaller than this | 321 | * @buflen: buffer size in bytes. If string is smaller than this |
322 | * then it must be terminated with a \0. | 322 | * then it must be terminated with a \0. |
323 | * @is_user: location of buffer, 0 indicates kernel space | ||
323 | * @maskp: pointer to bitmap array that will contain result. | 324 | * @maskp: pointer to bitmap array that will contain result. |
324 | * @nmaskbits: size of bitmap, in bits. | 325 | * @nmaskbits: size of bitmap, in bits. |
325 | * | 326 | * |
@@ -330,11 +331,13 @@ EXPORT_SYMBOL(bitmap_scnprintf); | |||
330 | * characters and for grouping errors such as "1,,5", ",44", "," and "". | 331 | * characters and for grouping errors such as "1,,5", ",44", "," and "". |
331 | * Leading and trailing whitespace accepted, but not embedded whitespace. | 332 | * Leading and trailing whitespace accepted, but not embedded whitespace. |
332 | */ | 333 | */ |
333 | int bitmap_parse(const char __user *ubuf, unsigned int ubuflen, | 334 | int __bitmap_parse(const char *buf, unsigned int buflen, |
334 | unsigned long *maskp, int nmaskbits) | 335 | int is_user, unsigned long *maskp, |
336 | int nmaskbits) | ||
335 | { | 337 | { |
336 | int c, old_c, totaldigits, ndigits, nchunks, nbits; | 338 | int c, old_c, totaldigits, ndigits, nchunks, nbits; |
337 | u32 chunk; | 339 | u32 chunk; |
340 | const char __user *ubuf = buf; | ||
338 | 341 | ||
339 | bitmap_zero(maskp, nmaskbits); | 342 | bitmap_zero(maskp, nmaskbits); |
340 | 343 | ||
@@ -343,11 +346,15 @@ int bitmap_parse(const char __user *ubuf, unsigned int ubuflen, | |||
343 | chunk = ndigits = 0; | 346 | chunk = ndigits = 0; |
344 | 347 | ||
345 | /* Get the next chunk of the bitmap */ | 348 | /* Get the next chunk of the bitmap */ |
346 | while (ubuflen) { | 349 | while (buflen) { |
347 | old_c = c; | 350 | old_c = c; |
348 | if (get_user(c, ubuf++)) | 351 | if (is_user) { |
349 | return -EFAULT; | 352 | if (__get_user(c, ubuf++)) |
350 | ubuflen--; | 353 | return -EFAULT; |
354 | } | ||
355 | else | ||
356 | c = *buf++; | ||
357 | buflen--; | ||
351 | if (isspace(c)) | 358 | if (isspace(c)) |
352 | continue; | 359 | continue; |
353 | 360 | ||
@@ -388,11 +395,36 @@ int bitmap_parse(const char __user *ubuf, unsigned int ubuflen, | |||
388 | nbits += (nchunks == 1) ? nbits_to_hold_value(chunk) : CHUNKSZ; | 395 | nbits += (nchunks == 1) ? nbits_to_hold_value(chunk) : CHUNKSZ; |
389 | if (nbits > nmaskbits) | 396 | if (nbits > nmaskbits) |
390 | return -EOVERFLOW; | 397 | return -EOVERFLOW; |
391 | } while (ubuflen && c == ','); | 398 | } while (buflen && c == ','); |
392 | 399 | ||
393 | return 0; | 400 | return 0; |
394 | } | 401 | } |
395 | EXPORT_SYMBOL(bitmap_parse); | 402 | EXPORT_SYMBOL(__bitmap_parse); |
403 | |||
404 | /** | ||
405 | * bitmap_parse_user() | ||
406 | * | ||
407 | * @ubuf: pointer to user buffer containing string. | ||
408 | * @ulen: buffer size in bytes. If string is smaller than this | ||
409 | * then it must be terminated with a \0. | ||
410 | * @maskp: pointer to bitmap array that will contain result. | ||
411 | * @nmaskbits: size of bitmap, in bits. | ||
412 | * | ||
413 | * Wrapper for __bitmap_parse(), providing it with user buffer. | ||
414 | * | ||
415 | * We cannot have this as an inline function in bitmap.h because it needs | ||
416 | * linux/uaccess.h to get the access_ok() declaration and this causes | ||
417 | * cyclic dependencies. | ||
418 | */ | ||
419 | int bitmap_parse_user(const char __user *ubuf, | ||
420 | unsigned int ulen, unsigned long *maskp, | ||
421 | int nmaskbits) | ||
422 | { | ||
423 | if (!access_ok(VERIFY_READ, ubuf, ulen)) | ||
424 | return -EFAULT; | ||
425 | return __bitmap_parse((const char *)ubuf, ulen, 1, maskp, nmaskbits); | ||
426 | } | ||
427 | EXPORT_SYMBOL(bitmap_parse_user); | ||
396 | 428 | ||
397 | /* | 429 | /* |
398 | * bscnl_emit(buf, buflen, rbot, rtop, bp) | 430 | * bscnl_emit(buf, buflen, rbot, rtop, bp) |