2010-01-20 8 views
6

Estoy tratando de asignar algo de memoria para un char * de la siguiente manera.¿Cómo puedo asignar memoria en el kernel de Linux para una cadena tipo * char *?

static ssize_t memo_write(struct file *filp, const char __user *buf, 
    size_t count, loff_t *f_pos){ 
    ssize_t retval = -ENOMEM; 
    printk("write function\n"); 

    if((data = kmalloc(strlen(buf), GFP_KERNEL)) == NULL) 
     printk("kmalloc fail\n"); 

    if(copy_from_user(data, buf, strlen(buf))){ 
     retval = -EFAULT; 
     goto out; 
    } 
    *f_pos += strlen(buf); 
    retval = strlen(buf); 

    out: 
     return retval; 
} 

'datos' se declara en un archivo de cabecera como

char *data; 

Cuando llamo a la función de escritura, el 'kmalloc fracasar' no se alcanza la línea, lo que me lleva a creer que el éxito kmalloc Sin embargo, los datos no se muestran cuando intento leer de nuevo la variable 'datos'.

Más confuso, si me deshago de la bit de kmalloc por completo, los datos se pueden leer desde el controlador. Aunque el problema es seguido por una carga de otros datos porque no tengo la oportunidad de memset().

¿Estoy usando kmalloc correctamente? Presumiblemente no. ¿Cómo debería estar haciendo esto?

Además, mi función de lectura es la siguiente.

static ssize_t memo_read(struct file *f, char __user *buf, 
    size_t count, loff_t *f_pos){ 
    ssize_t retval = 0; 

    printk("read function\n"); 
    printk("data = %s\n", data); 

    if(*f_pos >= strlen(data)){ 
     printk("EOF\n"); 
     goto out; 
    } 

    if(copy_to_user(buf, data, strlen(data))){ 
     retval = -EFAULT; 
     goto out; 
    } 
    printk("copy_to_user success\n"); 
    *f_pos += strlen(data); 
    retval = strlen(data); 
    out: 
     return retval; 
} 

Thanks.

+0

Gracias tanto para estos grandes respuestas, es realmente una gran ayuda! Los marcaría como la respuesta elegida si pudiera, aunque elegiré café porque incluyó el bloqueo y tiene menos reputación: p ¡Gracias de nuevo! – cheesysam

Respuesta

9

Debería utilizar strlen_user() en el puntero del espacio de usuario, en lugar de strlen() - y sólo se debe llamar una vez, y mantener el resultado en todo (por lo demás, que tienen un potencial núcleo de explotar, porque un segundo hilo espacio de usuario podría cambiar el buffer mientras trabajas en ello).

Como alternativa, puede usar strncpy_from_user().

Aparte de eso, el kmalloc se ve bien.


(Pero en realidad, como dice ephemient, debe reconsiderar su enfoque de conjunto y utilizar el argumento count lugar de tratar a la entrada como una cadena).


Puesto que no se puede confiar en los datos escritos en un archivo nul siendo cadenas terminadas, que necesita para mantener un parámetro data_len longitud en torno al lado del data. A continuación, sus read/write implementaciones serían lo largo de estas líneas:

static char *data = NULL; 
static size_t data_len; 
static DEFINE_MUTEX(data_mutex); 

static ssize_t memo_read(struct file *f, char __user *buf, size_t count, loff_t *f_pos 
{ 
    ssize_t retval = 0; 
    char *start; 

    mutex_lock(&data_mutex); 

    if (!data) 
    { 
     retval = -EINVAL; /* Or whatever you want to do here... */ 
     goto out; 
    } 

    if (*f_pos >= data_len) 
     goto out; /* EOF */ 

    start = data + *f_pos; 
    retval = data_len - *f_pos; 

    if (retval > count) 
     retval = count; 

    if (copy_to_user(buf, start, retval)) 
    { 
     retval = -EFAULT; 
     goto out; 
    } 

    *f_pos += retval; 

out: 
    mutex_unlock(&data_mutex); 
    return retval; 
} 

static ssize_t memo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) 
{ 
    ssize_t retval = -ENOMEM; 

    mutex_lock(&data_mutex); 

    if (data) 
     kfree(data); 

    data = kmalloc(count, GFP_KERNEL); 

    if (!data) 
     goto out; 

    if (copy_from_user(data, buf, count)) 
    { 
     kfree(data); 
     retval = -EFAULT; 
     goto out; 
    } 

    *f_pos = count; 
    retval = count; 
    data_len = count; 

out: 
    mutex_unlock(&data_mutex); 
    return retval; 
} 
+0

Pensé en eso, pero presumiblemente esto está en un controlador de "escritura" ... ¿por qué no utilizar el argumento 'count'? No debería haber una garantía de que 'buf' tiene terminación NUL. – ephemient

+1

Muy cierto: he actualizado la respuesta para aceptar esto (pero no eliminé la respuesta, porque creo que la otra información que he agregado es útil de manera independiente). – caf

+0

Ahora que lo ha mencionado, usar el argumento de contar tiene mucho más sentido. – cheesysam

4

No se olvide de kfree(data) en sus casos de error ...

En cualquier caso, buf es un puntero a la memoria de usuario, por lo que no llame a strlen(buf). Primero debe copy_from_user. ¿Por qué no

data = kmalloc(count); 
copy_from_user(data, buf, count); 

?


Su manejador de lectura asume que data es una cadena terminada en NULL. Cuando estaba usando una matriz, esto pudo haber sido cierto por accidente, pero nunca lo asegura en su controlador de escritura. Mi suposición es que copy_to_user falla.

Aquí está un ejemplo de trabajo de un módulo de "memo" que he escrito hasta ahora, utilizando kmalloc:

#include <linux/fs.h> 
#include <linux/miscdevice.h> 
#include <linux/module.h> 
#include <linux/types.h> 
#include <linux/uaccess.h> 

static char *data; 
static size_t len; 

static ssize_t 
memo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 
{ 
     ssize_t copy_len = min(len - min(len, *ppos), count); 
     ssize_t retval; 

     if (copy_to_user(buf, data + *ppos, copy_len)) { 
       retval = -EFAULT; 
       goto out; 
     } 

     *ppos += copy_len; 
     retval = copy_len; 

out: 
     return retval; 
} 

static ssize_t 
memo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 
{ 
     ssize_t retval; 
     char *newdata; 

     newdata = kmalloc(count, GFP_KERNEL); 
     if (!newdata) { 
       retval = -ENOMEM; 
       goto out; 
     } 

     if (copy_from_user(newdata, buf, count)) { 
       retval = -EFAULT; 
       goto out; 
     } 

     kfree(data); 
     data = newdata; 
     newdata = NULL; 
     retval = len = count; 

out: 
     kfree(newdata); 
     return retval; 
} 

static const struct file_operations memo_fops = { 
     .owner = THIS_MODULE, 
     .llseek = no_llseek, 
     .read = memo_read, 
     .write = memo_write, 
}; 

static struct miscdevice memo_misc = { MISC_DYNAMIC_MINOR, "memo", &memo_fops }; 

static int __init memo_init(void) 
{ 
     int result; 

     result = misc_register(&memo_misc); 
     if (result < 0) 
       return -ENODEV; 

     return 0; 
} 

static void __exit memo_exit(void) 
{ 
     misc_deregister(&memo_misc); 
     kfree(data); 
     return; 
} 

module_init(memo_init); 
module_exit(memo_exit); 
MODULE_AUTHOR("ephemient"); 
MODULE_LICENSE("GPL"); 

Por supuesto, esta falta de bloqueo y otras medidas de seguridad, pero espero que esto ayude.

+0

He intentado esto, sin embargo estoy teniendo el mismo problema. Todo lo que escribo en mi controlador no se refleja cuando uso 'cat/dev/myDriver' para ver qué hay allí. A pesar de que lo hace si me deshice de las cosas kmalloc por completo. – cheesysam

+0

Probablemente también haya un error en su código de lectura, simplemente está funcionando por accidente; podría actualizar su pregunta también con su implementación de lectura. – caf

+0

Bien, gracias, ahí lo tienes :) – cheesysam

Cuestiones relacionadas