处理用户空间和内核之间的buf

时间:2013-05-14 11:20:33

标签: c linux device-driver uart

我正在制作一个简单的设备驱动程序,能够使用UART接收和发送字符。

我的读写功能如下:

unsigned char UART_read(void){
     unsigned int buf;
     while( ( ( inb(UART_LSR + UART) ) & UART_LSR_DR ) == 0 ){
             schedule();
     }
     buf = inb(UART);
     return (char)buf;
}

ssize_t serp_read(struct file *filep, char __user *buf, size_t count, loff_t *offp){
        ssize_t cnt, ret;
        char *buffer;
        unsigned char data;
        int i;

        buffer = kmalloc(count * sizeof(char), GFP_KERNEL);

        printk("\nTHIS IS KERNEL, read was called and count is %zd\n", count);

        while(1){        
           buffer[i] = UART_read();
           if(buffer[i] == '\n') break;
           i++;
        }

        buffer[strlen(buffer) - 1] = '\0';

        if( (cnt = (copy_to_user(buf, buffer, strlen(buffer)))) != 0 )
               printk("Error in copy_to_user() cnt is %d\n", cnt);

        ret = strlen(buffer);

        printk("\nTHIS IS KERNEL, read is going away and buf is %s\n", buf);

        return ret; 
}

void UART_send(unsigned char data){
       while( ( ( inb(UART_LSR + UART) ) & UART_LSR_THRE ) == 0 ){
             schedule();
       }
       outb(data, (UART + UART_TX));
}

ssize_t serp_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){
        ssize_t cnt, ret;
        int i;
        char *buffer;

        buffer = kmalloc(count * sizeof(char), GFP_KERNEL);

        if( (cnt = (copy_from_user(buffer, buf, count))) != 0 ) printk("Error in     copy_from_user()\n");
        buffer[count] = '\0';

        for(i = 0; i < strlen(buffer); i++){
            UART_send(buffer[i]);
        }

        ret = strlen(buffer);

        return ret; 
}

我用来测试的程序的一部分是:

    char *str = "HELLO MY NAME IS";
    strcpy(buffer, str);
    printf("\nThe message is [ %s ]\n", buffer);
    if ( (write(fd, buffer, strlen(buffer))) < 0) perror("Error");

    buffer[0] = '\0';

    if ( (read(fd, buffer, sizeof(buffer))) < 0) perror("Error");

    //buffer[strlen(buffer)] = '\0';

    printf("\nThe content of buffer after read() is [ %s ]\n", buffer);

凭借我所拥有的,我在编写“HELLO,MY NAME IS”字符串时没有任何问题,但是当我阅读时,让我们从UART说“嘿”,读取功能中的buf显示为“heyLO,MY NAME” IS“,我不明白为什么会发生这种覆盖。我现在最好的猜测是,首先我写入内核访问的用户空间,然后我读到相同的用户空间,最后覆盖已经存在的内容。

2 个答案:

答案 0 :(得分:1)

read返回读取的字节数,并且不会终止。 此外,这一行:

buffer[strlen(buffer)] = '\0';

没有,因为缓冲区的strlen是第一个'\0'的偏移量! 尝试

ssize_t rb = read(fd, buffer, sizeof(buffer));
if ( rb < 0)
    perror("Error");
else {
    buffer[rb] = '\0';
    ...

重新阅读serp_read,我发现strlen的这种破坏用途无处不在。如果您还没有以nul结尾的C字符串,则无法使用strlen。它只是寻找nul!

ssize_t serp_read(struct file *filep,
                  char __user *buf, size_t count, loff_t *offp) {
    ssize_t cnt, i;
    char *buffer;

    buffer = kmalloc(count, GFP_KERNEL);
    printk("\nTHIS IS KERNEL, read was called and count is %zd\n", count);

    /* your while loop started with i uninitialized,
     * and didn't check for overflow */
    for (i = 0; i != count; ++i) {
        buffer[i] = UART_read();
        if(buffer[i] == '\n')
            break;
    }
    /* this is incorrect because:
     * 1. strlen isn't useable unless the buffer is already nul-terminated
     * 2. the read(2) system call isn't expected to nul-terminate buffers anyway
    buffer[strlen(buffer) - 1] = '\0';
     */

    /* Use i+1 as the length if you want to include the newline */
    cnt = copy_to_user(buf, buffer, i);
    kfree(buffer);
    if(cnt) {
        printk("Error in copy_to_user() cnt is %d\n", cnt);
        i -= cnt; /* bytes successfully copied */
    }

    printk("\nTHIS IS KERNEL, read is going away and buf is %.*s\n", i, buf);
    /* this is how to print non-nul-terminated variable-length strings */

    return i; /* NB. should return an error above, as well as printing */ 
}

答案 1 :(得分:0)

使用memset,你没有清除内存

char *str = "HELLO MY NAME IS";

strcpy(buffer, str);
printf("\nThe message is [ %s ]\n", buffer);

if ( (write(fd, buffer, strlen(buffer))) < 0) perror("Error");

buffer[0] = '\0'; // this is clearing only single character
memset( buffer , 0 , sizeof(buffer)); // clear the entire buffer here

if ( (read(fd, buffer, sizeof(buffer))) < 0) perror("Error");

//buffer[strlen(buffer)] = '\0';

printf("\nThe content of buffer after read() is [ %s ]\n", buffer);