首页 > 解决方案 > 当从缓冲区直接复制值时,strscp 将永远循环

问题描述

问题

我正在学习 Linux 内核模块开发,并试图理解为什么 strscp 在复制缓冲区中的值时会无限循环。

代码

#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>

#define LEN 13
#define ID "ABCDEF2103a5"
static  char buffer[LEN];

static ssize_t hello_write(struct file *file,
        const char *buf, size_t count,
        loff_t *ppos)
{
    
    ssize_t len;
    
    if (count != LEN)
        return -EINVAL;
    
    // This loops forever when 0 returned!
    strscpy(buffer, buf, LEN);
    
    return len;
}


static const struct file_operations file_ops = {
    .owner  = THIS_MODULE,
    .write  = hello_write,
};

static struct miscdevice misc_dev = {
    MISC_DYNAMIC_MINOR,
    "check",
    &file_ops
};

当值“ABCDEF2103a5”(或任何相同长度的值)通过 传递给模块时echo ABCDEF2103a5 > /dev/check,它只是连续打印该值。

以下是来自的输出dmesg -wH

[  +0.000002] ABCDEF2103a5
[  +0.000003] ABCDEF2103a5
[  +0.000003] ABCDEF2103a5
[  +0.000002] ABCDEF2103a5
[  +0.000003] ABCDEF2103a5
[  +0.000002] ABCDEF2103a5
[  +0.000003] ABCDEF2103a5
[  +0.000002] ABCDEF2103a5
[  +0.000003] ABCDEF2103a5
[  +0.000003] ABCDEF2103a5
[  +0.000002] ABCDEF2103a5
[  +0.000008] ABCDEF2103a5 

它占用CPU 100%。为了让它停止,我必须找到 shell 的 PID 并杀死它。

当返回 0 时(len 被指定为 0 或return 0),没有打印任何内容并且 bash 挂起。

当返回其他值(0 以外)时,bash: echo: write error:...会抛出某种形式的错误消息,但不会挂起。

应该返回什么值?我们不能只是这样做return;

工作正常

完整的功能代码,首先将缓冲区复制到临时数组,然后将其复制到全局变量。

#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>

#define LEN 13
#define ID "ABCDEF2103a5"
static  char buffer[LEN];

static ssize_t hello_write(struct file *file,
        const char *buf, size_t count,
        loff_t *ppos)
{
    ssize_t len;
    char *msg = kmalloc (count + 1, GFP_KERNEL);

    if (!msg)
        return -ENOMEM;

    if (count != LEN)
        return -EINVAL;

    len = simple_write_to_buffer(msg, count, ppos, buf,
      count);

    if ( strncmp(msg, ID, LEN-1 ) != 0 )
      return -EINVAL;
    
    // Copied from the temporary variable
    // which works fine.
    strscpy(buffer, msg, LEN);

    return len;
}


static const struct file_operations file_ops = {
    .owner  = THIS_MODULE,
    .write  = hello_write,
};

static struct miscdevice misc_dev = {
    MISC_DYNAMIC_MINOR,
    "check",
    &file_ops
};

上面评论的部分没有评论。在这里,缓冲区首先被复制到一个临时变量。strscpy并且使用which 可以正常复制 temp 变量的值。

标签: linuxlinux-kernellinux-device-driver

解决方案


我相信@Tsyvarev 评论了正确的答案。

“至于循环,你声明了 ssize_t len 变量,但从未初始化它。可能,它是 0,所以你的 write 方法总是返回 0,这被用户空间解释为“没有写入任何字节”,用户部分重复一次又一次地写作。”

重构后的代码是:

static ssize_t hello_write(struct file *file,
        const char *buf, size_t count,
        loff_t *ppos)
{

    ssize_t len;
    ssize_t copy_len;
    char *msg = kmalloc(count + 1, GFP_KERNEL);

    if (!msg)
        return -ENOMEM;

    if (count != LEN)
        goto einval;

    len = simple_write_to_buffer(msg, count, ppos,
            buf, count);

    if (len <= 0) {
        kfree(msg);
        return len;
    }

    if (strncmp(msg, ID, LEN-1) != 0)
        goto einval;

    copy_len = strscpy(buffer, strim(msg), LEN);

    if (copy_len < 0) {
        kfree(msg);
        return copy_len;
    }

    return len;

einval:
    kfree(msg);
    return -EINVAL;
}

推荐阅读