Linux内核模块:在信号量上调用导致NULL指针解除

发布于 2025-02-13 05:29:44 字数 4179 浏览 0 评论 0原文

我正在尝试学习Linux内核模块开发,并遵循“ Linux设备驱动程序,第三版”。我正在尝试实现仅内存的设备,例如SCULL,用打开和关闭设备,Close> Close在用户空间中工作正常,但是我的阅读和<代码>写入实现不起作用。在首次加载模块时初始化新设备的函数中,我创建了这样的信号:

// ...
struct semaphore *sema;

sema = kmalloc(sizeof(struct semaphore), GFP_KERNEL);
sema_init(sema, 1);

my_device->sem = *sema;

my_device是包含cdev struct and struct and其他内容的设备结构。

在我的写入实现中,我的

ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    // ...
    struct MyDev *dev = filp->private_data; // MyDev is the struct initialized above
    if (down_interruptible(&dev->sem))
    {
        printk(KERN_ALERT "Failed to get semaphore\n");
        return -ERESTARTSYS;
    }

部分正常工作,就像将数据写入设备一样。 致电时

up(&dev->sem);

但是,当我在末尾

,我会得到无法在虚拟地址处理内核NULL指针删除0000000000000007A4。内核“ oops”是内部错误:oops:96000004 [#1],跟踪是:

Call trace:
__raw_spin_lock_irqsave+0x3c/0xa0
try_to_wake_up+0x50/0x674
wake_up_process+0x24/0x30
__up.isra.0+0x58/0x70
up+0x68/0x70
my_write+0x250/0x2dc [devices]     // my function
vfs_write+0xfc/0x2c0
ksys_write+0x74/0x100
__arm64_sys_write+0x28/0x34
invoke_syscall+0x50/0x120
el0_svc_common+0x48/0x100
do_el0_svc+0x34/0xa0
el0_svc+0x2c/0x54
el0t_64_sync_handler+0xe8/0xf0
el0t_64_sync+0x198/0x19c
Code: aa1303e0 52800001 52800022 2a0103e3 (88e37e62) 

如果是相关的,则我的file> file_operations struct struct struct看起来

static struct file_operations my_ops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_close,
    .read = my_read,
    .write = my_write,
};

可能是内核在我尚未定义的那个结构上寻找其他功能吗?我已经记录了my_write中使用的所有指针,我知道它们不是零。为什么down_interible工作但up不起作用?

编辑:我还尝试了down,而不是down_interible - down也有效,但是up仍然不行。

仅出于完整性,这是整个my_write

ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct MyDev *dev = filp->private_data;
    struct MySet *dptr;
    int quantum = dev->quantum;
    int qset = dev->qset;
    int itemsize = quantum * qset;
    int item, s_pos, q_pos, rest;
    ssize_t retval = -ENOMEM;

    printk(KERN_ALERT "about to get semaphore dev->sem %p\n", &dev->sem);

    if (down_interruptible(&dev->sem))
    {
        printk(KERN_ALERT "Failed to get semaphore\n");
        return -ERESTARTSYS;
    }

    printk(KERN_ALERT "got past semaphore\n");

    item = (long)*f_pos / itemsize;
    rest = (long)*f_pos % itemsize;
    s_pos = rest / quantum;
    q_pos = rest % quantum;

    dptr = my_follow(dev, item);
    printk(KERN_ALERT "after follow, dptr: %p\b", dptr);

    if (dptr == NULL)
        goto out;

    if (!dptr->data)
    {
        dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
        if (!dptr->data)
            goto out;
        memset(dptr->data, 0, qset * sizeof(char *));
        printk(KERN_ALERT "zeroed dptr->data\n");
    }



    if (!dptr->data[s_pos])
    {
        dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
        printk(KERN_ALERT "allocated mem for dptr->data[%d]\n", s_pos);
        if (!dptr->data[s_pos])
            goto out;
    }

    if (count > quantum - q_pos)
        count = quantum - q_pos;

    if (copy_from_user(dptr->data[s_pos] + q_pos, buf, count))
    {
        retval = -EFAULT;
        goto out;
    }

    printk(KERN_ALERT "copied from user\n");

    *f_pos += count;
    retval = count;

    if (dev->size < *f_pos)
        dev->size = *f_pos;

    printk(KERN_ALERT "calling up...\n");

out:
    up(&dev->sem);
    printk(KERN_ALERT "called up, returning %zx\n", retval);
    return retval;
}

I'm trying to learn Linux kernel module development, and following the book "Linux Device Drivers, Third Edition". I'm trying to implement a memory-only device like scull, opening and closing the device with open and close in userspace works fine, but my read and write implementations aren't working. In the function that initializes a new device when the module is first loaded, I create the semaphore like this:

// ...
struct semaphore *sema;

sema = kmalloc(sizeof(struct semaphore), GFP_KERNEL);
sema_init(sema, 1);

my_device->sem = *sema;

my_device is the device struct containing the cdev struct and other things.

In my write implementation, I have

ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    // ...
    struct MyDev *dev = filp->private_data; // MyDev is the struct initialized above
    if (down_interruptible(&dev->sem))
    {
        printk(KERN_ALERT "Failed to get semaphore\n");
        return -ERESTARTSYS;
    }

That part works fine, as does writing data to the device. But when I call

up(&dev->sem);

at the end, I get Unable to handle kernel NULL pointer dereference at virtual address 00000000000007a4.

The kernel "oops" is Internal error: Oops: 96000004 [#1] and the trace is:

Call trace:
__raw_spin_lock_irqsave+0x3c/0xa0
try_to_wake_up+0x50/0x674
wake_up_process+0x24/0x30
__up.isra.0+0x58/0x70
up+0x68/0x70
my_write+0x250/0x2dc [devices]     // my function
vfs_write+0xfc/0x2c0
ksys_write+0x74/0x100
__arm64_sys_write+0x28/0x34
invoke_syscall+0x50/0x120
el0_svc_common+0x48/0x100
do_el0_svc+0x34/0xa0
el0_svc+0x2c/0x54
el0t_64_sync_handler+0xe8/0xf0
el0t_64_sync+0x198/0x19c
Code: aa1303e0 52800001 52800022 2a0103e3 (88e37e62) 

If it's relevant, my file_operations struct looks like this

static struct file_operations my_ops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_close,
    .read = my_read,
    .write = my_write,
};

Is it possible the kernel is looking for other functions on that struct that I haven't defined yet? I have logged out all the pointers used in my_write and I know they're not null. Why does down_interruptible work but not up?

Edit: I also tried just down instead of down_interruptible - down also works, but up still doesn't.

And just for completeness, here is the entire my_write

ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct MyDev *dev = filp->private_data;
    struct MySet *dptr;
    int quantum = dev->quantum;
    int qset = dev->qset;
    int itemsize = quantum * qset;
    int item, s_pos, q_pos, rest;
    ssize_t retval = -ENOMEM;

    printk(KERN_ALERT "about to get semaphore dev->sem %p\n", &dev->sem);

    if (down_interruptible(&dev->sem))
    {
        printk(KERN_ALERT "Failed to get semaphore\n");
        return -ERESTARTSYS;
    }

    printk(KERN_ALERT "got past semaphore\n");

    item = (long)*f_pos / itemsize;
    rest = (long)*f_pos % itemsize;
    s_pos = rest / quantum;
    q_pos = rest % quantum;

    dptr = my_follow(dev, item);
    printk(KERN_ALERT "after follow, dptr: %p\b", dptr);

    if (dptr == NULL)
        goto out;

    if (!dptr->data)
    {
        dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
        if (!dptr->data)
            goto out;
        memset(dptr->data, 0, qset * sizeof(char *));
        printk(KERN_ALERT "zeroed dptr->data\n");
    }



    if (!dptr->data[s_pos])
    {
        dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
        printk(KERN_ALERT "allocated mem for dptr->data[%d]\n", s_pos);
        if (!dptr->data[s_pos])
            goto out;
    }

    if (count > quantum - q_pos)
        count = quantum - q_pos;

    if (copy_from_user(dptr->data[s_pos] + q_pos, buf, count))
    {
        retval = -EFAULT;
        goto out;
    }

    printk(KERN_ALERT "copied from user\n");

    *f_pos += count;
    retval = count;

    if (dev->size < *f_pos)
        dev->size = *f_pos;

    printk(KERN_ALERT "calling up...\n");

out:
    up(&dev->sem);
    printk(KERN_ALERT "called up, returning %zx\n", retval);
    return retval;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文