代码之家  ›  专栏  ›  技术社区  ›  md.jamal

当字符串被传递到内核设备驱动程序时,用户应用程序被终止

  •  0
  • md.jamal  · 技术社区  · 7 年前

    这是内核驱动程序代码:

    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <asm/uaccess.h>    /* for put_user */
    #include "test_strlen.h"
    
    /*  
     *  Prototypes - this would normally go in a .h file
     */
    int init_module(void);
    void cleanup_module(void);
    static int device_open(struct inode *, struct file *);
    static int device_release(struct inode *, struct file *);
    static ssize_t device_read(struct file *, char *, size_t, loff_t *);
    static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
    static long test_ioctl(struct file *, unsigned int,
                  unsigned long);
    
    #define SUCCESS 0
    #define DEVICE_NAME "test_strlen"   /* Dev name as it appears in /proc/devices   */
    #define BUF_LEN 80      /* Max length of the message from the device */
    
    /* 
     * Global variables are declared as static, so are global within the file. 
     */
    
    static int Major;       /* Major number assigned to our device driver */
    static int Device_Open = 0; /* Is device open?  
                     * Used to prevent multiple access to device */
    static char msg[BUF_LEN];   /* The msg the device will give when asked */
    static char *msg_Ptr;
    
    static struct file_operations fops = {
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .release = device_release,
        .unlocked_ioctl = test_ioctl 
    };
    
    static long test_ioctl(struct file *file, unsigned int cmd,
            unsigned long arg)
    {
        switch(cmd) {
            case IOCTL_CP210x_SET_SER_STR:
                {
                    int retval = 0;
                    unsigned char serial_str[127];
                    memset(serial_str, 0x00, sizeof(serial_str));
                    retval = copy_from_user(serial_str, (unsigned char *)arg,
                            strlen((unsigned char *)arg));
                    printk(KERN_INFO"string from user: %s\n", serial_str);
                    if (retval) {
                        printk(KERN_ERR"%s: failed to retrieve user argument %d\n",
                                __func__, retval);
                        return -EFAULT;
                    }
                    // retval = cp210x_set_serial_str(port, serial_str);
                }
                break;
                default:
                    break;
        }
    
        return 0;
    }
    /*
     * This function is called when the module is loaded
     */
    int init_module(void)
    {
            Major = register_chrdev(0, DEVICE_NAME, &fops);
    
        if (Major < 0) {
          printk(KERN_ALERT "Registering char device failed with %d\n", Major);
          return Major;
        }
    
        printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
        printk(KERN_INFO "the driver, create a dev file with\n");
        printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
        printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
        printk(KERN_INFO "the device file.\n");
        printk(KERN_INFO "Remove the device file and module when done.\n");
    
        return SUCCESS;
    }
    
    /*
     * This function is called when the module is unloaded
     */
    void cleanup_module(void)
    {
        /* 
         * Unregister the device 
         */
        unregister_chrdev(Major, DEVICE_NAME);
        // if (ret < 0)
            // printk(KERN_ALERT "Error in unregister_chrdev: %d\n", ret);
    }
    
    /*
     * Methods
     */
    
    /* 
     * Called when a process tries to open the device file, like
     * "cat /dev/mycharfile"
     */
    static int device_open(struct inode *inode, struct file *file)
    {
        static int counter = 0;
    
        if (Device_Open)
            return -EBUSY;
    
        Device_Open++;
        sprintf(msg, "I already told you %d times Hello world!\n", counter++);
        msg_Ptr = msg;
        try_module_get(THIS_MODULE);
    
        return SUCCESS;
    }
    
    /* 
     * Called when a process closes the device file.
     */
    static int device_release(struct inode *inode, struct file *file)
    {
        Device_Open--;      /* We're now ready for our next caller */
    
        /* 
         * Decrement the usage count, or else once you opened the file, you'll
         * never get get rid of the module. 
         */
        module_put(THIS_MODULE);
    
        return 0;
    }
    
    /* 
     * Called when a process, which already opened the dev file, attempts to
     * read from it.
     */
    static ssize_t device_read(struct file *filp,   /* see include/linux/fs.h   */
                   char *buffer,    /* buffer to fill with data */
                   size_t length,   /* length of the buffer     */
                   loff_t * offset)
    {
        /*
         * Number of bytes actually written to the buffer 
         */
        int bytes_read = 0;
    
        /*
         * If we're at the end of the message, 
         * return 0 signifying end of file 
         */
        if (*msg_Ptr == 0)
            return 0;
    
        /* 
         * Actually put the data into the buffer 
         */
        while (length && *msg_Ptr) {
    
            /* 
             * The buffer is in the user data segment, not the kernel 
             * segment so "*" assignment won't work.  We have to use 
             * put_user which copies data from the kernel data segment to
             * the user data segment. 
             */
            put_user(*(msg_Ptr++), buffer++);
    
            length--;
            bytes_read++;
        }
    
        /* 
         * Most read functions return the number of bytes put into the buffer
         */
        return bytes_read;
    }
    
    /*  
     * Called when a process writes to dev file: echo "hi" > /dev/hello 
     */
    static ssize_t
    device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
    {
        printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
        return -EINVAL;
    }  
    

    以及应用程序代码。

    #include <stdio.h>
    #include <fcntl.h>
    #include <stropts.h>
    #include <termios.h>
    #include <unistd.h> // to get close() define
    #include <sys/ioctl.h>
    #include <stdbool.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    #include "test_strlen.h"
    
    #define SER_STR "test"
    #define PRODUCT_STR "CP2108 Quad USB to UART Bridge Controller"
    
    int err_check(int ret, char* TAG);
    
    int main()
    {
        int fd, ret;
        unsigned char set_serial_str[256] = {};
        unsigned char get_serial_str[256] = {};
        unsigned char product_str[256] = {};
    
    
        fd = open("/dev/test_strlen", O_RDWR); 
    
        if (fd == -1) {
            printf( "Error opening port");
            return -99;
        }
    
        // Set serial string
        strcpy(set_serial_str, SER_STR) ;
        ret = ioctl(fd, IOCTL_CP210x_SET_SER_STR, set_serial_str);
        ret = err_check(ret, "SET_SER_STR");
    
        close(fd);
        return 0;
    }
    
    int err_check(int ret, char* TAG)
    {
        if (ret) {
            printf("FAILED - %s: ret = %d\t errno = %d\n", TAG, ret, errno);
            return -9999;
        }
        else {
            printf("SUCCESS - %s\n", TAG);
            return 0;
        }
    }
    

    当我在虚拟机上运行它时,代码工作正常。。但是,当我在yocto映像(core-image-minimal intel-core-i7-x64)上运行相同的操作时,应用程序就会被杀死。

    dmesg在访问 strlen((unsigned char *)arg)

    BUG: unable to handle kernel paging request at 00007ffef2503480
    IP: test_ioctl.part.0+0x2d/0xdcc [test_strlen]
    PGD 1790e1067 P4D 1790e1067 PUD 17708b067 PMD 179d8b067 PTE 8000000176ee5067
    Oops: 0001 [#3] PREEMPT SMP NOPTI
    Modules linked in: test_strlen(PO) [last unloaded: test_strlen]
    CPU: 1 PID: 13742 Comm: app Tainted: P      D    O    4.14.56-intel-pk-standard #1
    Hardware name: NCR Corporation 7746-1410-8801/PX10, BIOS 4.0.5.0 01/17/2018
    task: ffff9ca6b7beee40 task.stack: ffffaadb40138000
    RIP: 0010:test_ioctl.part.0+0x2d/0xdcc [test_strlen]
    RSP: 0018:ffffaadb4013bdd0 EFLAGS: 00010286
    RAX: 0000000000000000 RBX: ffff9ca6b90cabd8 RCX: ffffffffffffffff
    RDX: 00007ffef2503480 RSI: 00007ffef2503480 RDI: 00007ffef2503480
    RBP: ffffaadb4013be60 R08: 0000003b9a5aed80 R09: 0000003b9a5aed80
    R10: 0000000000000000 R11: 0000000000000000 R12: ffffaadb4013bdd1
    R13: ffff9ca6b903b500 R14: 0000000040018801 R15: 00007ffef2503480
    FS:  00007fbecd6224c0(0000) GS:ffff9ca6bfc80000(0000) knlGS:0000000000000000
    CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
    CR2: 00007ffef2503480 CR3: 000000017a344000 CR4: 00000000003406e0
    Call Trace:
     test_ioctl+0x1c/0x20 [test_strlen]
     do_vfs_ioctl+0x99/0x5e0
     ? putname+0x4c/0x60
     SyS_ioctl+0x79/0x90
     do_syscall_64+0x65/0x120
     entry_SYSCALL_64_after_hwframe+0x3d/0xa2
    RIP: 0033:0x3b9a2e98c7
    RSP: 002b:00007ffef2503468 EFLAGS: 00000202 ORIG_RAX: 0000000000000010
    RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000003b9a2e98c7
    RDX: 00007ffef2503480 RSI: 0000000040018801 RDI: 0000000000000003
    RBP: 00007ffef25037a0 R08: 0000003b9a5aed80 R09: 0000003b9a5aed80
    R10: 0000000000000573 R11: 0000000000000202 R12: 00000000004005e0
    R13: 00007ffef2503880 R14: 0000000000000000 R15: 0000000000000000
    Code: 44 00 00 55 31 c0 48 89 fe b9 7f 00 00 00 48 89 e5 41 54 4c 8d a5 71 ff ff ff 53 4c 89 e7 48 83 c4 80 f3 aa 48 83 c9 ff 48 89 f7 <f2> ae 48 89 ca 48 f7 d2 48 8d 5a ff 48 83 fb 7f 48 89 da 77 0d
    RIP: test_ioctl.part.0+0x2d/0xdcc [test_strlen] RSP: ffffaadb4013bdd0
    CR2: 00007ffef2503480
    ---[ end trace ea1ff013117d3c15 ]---
    

    你们能帮我解释一下为什么它发生在yocto上而不是VM上。。

    谢谢你的帮助。。非常感谢。

    1 回复  |  直到 7 年前
        1
  •  3
  •   gone    7 年前
    copy_from_user(serial_str, (unsigned char *)arg,
                        strlen((unsigned char *)arg));
    

    arg 是用户指针,因此使用起来不安全 strlen() 在上面。使用 strnlen_user() sizeof(serial_str) !)

    这可能是因为您的处理器和/或虚拟化软件不支持虚拟机 SMAP ,它检测并防止这种不适当的访问。