为什么我在netlink_setsockopt中无法调用wake_up_interruptible() 函数

编程语言 2026-07-10

我正在尝试在Linux内核3.16中访问在 netlink_setsockopt() 内的 wake_up_interruptible() 调用。不过我注意到,只有在我提供的 optlen 的情况下,代码路径才会到达。

当我对 setsockopt 进行调用,长度为 1 时,无法到达目标代码:

char *val = "A";
setsockopt(dangling_fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &val, 1);

然而,如果我使用 sizeof(val),则按预期工作:

char *val = "A";
setsockopt(dangling_fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &val, sizeof(val));

查看内核源码,我看到这是这样的检查:

static int netlink_setsockopt(struct socket *sock, int level, int optname,
                  char __user *optval, unsigned int optlen)
{
    struct sock *sk = sock->sk;
    struct netlink_sock *nlk = nlk_sk(sk);
    unsigned int val = 0;
    int err;

    if (level != SOL_NETLINK)
        return -ENOPROTOOPT;

    if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING &&
        optlen >= sizeof(int) &&
        get_user(val, (unsigned int __user *)optval))
        return -EFAULT;

    switch (optname) {
    ...
    case NETLINK_NO_ENOBUFS:
        if (val) {
            nlk->flags |= NETLINK_RECV_NO_ENOBUFS;
            clear_bit(NETLINK_CONGESTED, &nlk->state);
            wake_up_interruptible(&nlk->wait);
        } else {
            nlk->flags &= ~NETLINK_RECV_NO_ENOBUFS;
        }
        err = 0;
        break;
        ...
#endif /* CONFIG_NETLINK_MMAP */
    default:
        err = -ENOPROTOOPT;
    }
    return err;
}

为什么第一种方法无法进入 NETLINK_NO_ENOBUFS 的逻辑?

解决方案

经过一番研究和试错,我发现把 optlen 设为1,意味着它小于 sizeof(int),后者的值是4。

这意味着。

if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING &&
        optlen >= sizeof(int) &&
        get_user(val, (unsigned int __user *)optval))
        return -EFAULT;

...
optlen >= sizeof(int) // 1 is not greater than 4

因为,在该if语句中,我们的第三个条件返回了false。我们的 get_user(val, (unsigned int __user *)optval) 被跳过,所以val仍然是0。后面的if语句返回了false,这就是为什么 wake_up_interruptible() 无法被调用的原因。

case NETLINK_NO_ENOBUFS:
        if (val) { // This will be false because our val is still 0.
            nlk->flags |= NETLINK_RECV_NO_ENOBUFS;
            clear_bit(NETLINK_CONGESTED, &nlk->state);
            wake_up_interruptible(&nlk->wait);
        } else {
            nlk->flags &= ~NETLINK_RECV_NO_ENOBUFS;
        }
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章