2006-01-08 12:01:43 +03:00
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/module.h>
2006-03-24 14:18:42 +03:00
# include <linux/err.h>
# include <asm/uaccess.h>
2006-01-08 12:01:43 +03:00
/**
* kstrdup - allocate space for and copy an existing string
* @ s : the string to duplicate
* @ gfp : the GFP mask used in the kmalloc ( ) call when allocating memory
*/
char * kstrdup ( const char * s , gfp_t gfp )
{
size_t len ;
char * buf ;
if ( ! s )
return NULL ;
len = strlen ( s ) + 1 ;
2006-10-04 13:15:25 +04:00
buf = kmalloc_track_caller ( len , gfp ) ;
2006-01-08 12:01:43 +03:00
if ( buf )
memcpy ( buf , s , len ) ;
return buf ;
}
EXPORT_SYMBOL ( kstrdup ) ;
2006-03-24 14:18:42 +03:00
2007-07-18 05:37:02 +04:00
/**
* kstrndup - allocate space for and copy an existing string
* @ s : the string to duplicate
* @ max : read at most @ max chars from @ s
* @ gfp : the GFP mask used in the kmalloc ( ) call when allocating memory
*/
char * kstrndup ( const char * s , size_t max , gfp_t gfp )
{
size_t len ;
char * buf ;
if ( ! s )
return NULL ;
len = strnlen ( s , max ) ;
buf = kmalloc_track_caller ( len + 1 , gfp ) ;
if ( buf ) {
memcpy ( buf , s , len ) ;
buf [ len ] = ' \0 ' ;
}
return buf ;
}
EXPORT_SYMBOL ( kstrndup ) ;
[PATCH] kmemdup: introduce
One of idiomatic ways to duplicate a region of memory is
dst = kmalloc(len, GFP_KERNEL);
if (!dst)
return -ENOMEM;
memcpy(dst, src, len);
which is neat code except a programmer needs to write size twice. Which
sometimes leads to mistakes. If len passed to kmalloc is smaller that len
passed to memcpy, it's straight overwrite-beyond-end. If len passed to
memcpy is smaller than len passed to kmalloc, it's either a) legit
behaviour ;-), or b) cloned buffer will contain garbage in second half.
Slight trolling of commit lists shows several duplications bugs
done exactly because of diverged lenghts:
Linux:
[CRYPTO]: Fix memcpy/memset args.
[PATCH] memcpy/memset fixes
OpenBSD:
kerberosV/src/lib/asn1: der_copy.c:1.4
If programmer is given only one place to play with lengths, I believe, such
mistakes could be avoided.
With kmemdup, the snippet above will be rewritten as:
dst = kmemdup(src, len, GFP_KERNEL);
if (!dst)
return -ENOMEM;
This also leads to smaller code (kzalloc effect). Quick grep shows
200+ places where kmemdup() can be used.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-01 10:27:20 +04:00
/**
* kmemdup - duplicate region of memory
*
* @ src : memory region to duplicate
* @ len : memory region length
* @ gfp : GFP mask to use
*/
void * kmemdup ( const void * src , size_t len , gfp_t gfp )
{
void * p ;
2006-10-04 13:15:25 +04:00
p = kmalloc_track_caller ( len , gfp ) ;
[PATCH] kmemdup: introduce
One of idiomatic ways to duplicate a region of memory is
dst = kmalloc(len, GFP_KERNEL);
if (!dst)
return -ENOMEM;
memcpy(dst, src, len);
which is neat code except a programmer needs to write size twice. Which
sometimes leads to mistakes. If len passed to kmalloc is smaller that len
passed to memcpy, it's straight overwrite-beyond-end. If len passed to
memcpy is smaller than len passed to kmalloc, it's either a) legit
behaviour ;-), or b) cloned buffer will contain garbage in second half.
Slight trolling of commit lists shows several duplications bugs
done exactly because of diverged lenghts:
Linux:
[CRYPTO]: Fix memcpy/memset args.
[PATCH] memcpy/memset fixes
OpenBSD:
kerberosV/src/lib/asn1: der_copy.c:1.4
If programmer is given only one place to play with lengths, I believe, such
mistakes could be avoided.
With kmemdup, the snippet above will be rewritten as:
dst = kmemdup(src, len, GFP_KERNEL);
if (!dst)
return -ENOMEM;
This also leads to smaller code (kzalloc effect). Quick grep shows
200+ places where kmemdup() can be used.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-01 10:27:20 +04:00
if ( p )
memcpy ( p , src , len ) ;
return p ;
}
EXPORT_SYMBOL ( kmemdup ) ;
2007-07-17 15:03:21 +04:00
/**
* krealloc - reallocate memory . The contents will remain unchanged .
* @ p : object to reallocate memory for .
* @ new_size : how many bytes of memory are required .
* @ flags : the type of memory to allocate .
*
* The contents of the object pointed to are preserved up to the
* lesser of the new and old sizes . If @ p is % NULL , krealloc ( )
* behaves exactly like kmalloc ( ) . If @ size is 0 and @ p is not a
* % NULL pointer , the object pointed to is freed .
*/
void * krealloc ( const void * p , size_t new_size , gfp_t flags )
{
void * ret ;
2007-10-16 12:24:46 +04:00
size_t ks = 0 ;
2007-07-17 15:03:21 +04:00
if ( unlikely ( ! new_size ) ) {
kfree ( p ) ;
2007-07-17 15:03:22 +04:00
return ZERO_SIZE_PTR ;
2007-07-17 15:03:21 +04:00
}
2007-10-16 12:24:46 +04:00
if ( p )
ks = ksize ( p ) ;
2007-07-17 15:03:21 +04:00
if ( ks > = new_size )
return ( void * ) p ;
ret = kmalloc_track_caller ( new_size , flags ) ;
if ( ret ) {
memcpy ( ret , p , min ( new_size , ks ) ) ;
kfree ( p ) ;
}
return ret ;
}
EXPORT_SYMBOL ( krealloc ) ;
2006-03-24 14:18:42 +03:00
/*
* strndup_user - duplicate an existing string from user space
* @ s : The string to duplicate
* @ n : Maximum number of bytes to copy , including the trailing NUL .
*/
char * strndup_user ( const char __user * s , long n )
{
char * p ;
long length ;
length = strnlen_user ( s , n ) ;
if ( ! length )
return ERR_PTR ( - EFAULT ) ;
if ( length > n )
return ERR_PTR ( - EINVAL ) ;
p = kmalloc ( length , GFP_KERNEL ) ;
if ( ! p )
return ERR_PTR ( - ENOMEM ) ;
if ( copy_from_user ( p , s , length ) ) {
kfree ( p ) ;
return ERR_PTR ( - EFAULT ) ;
}
p [ length - 1 ] = ' \0 ' ;
return p ;
}
EXPORT_SYMBOL ( strndup_user ) ;