PAYATHON 2026

netlink_setsockopt() 为何未触发 wake_up_interruptible()?

数据小许

结论

根据给出的内核代码,当 optlen 为 1 时,optlen >= sizeof(int) 不成立,get_user() 不会执行,内核局部变量 val 保持初始值 0。代码随后仍可进入 case NETLINK_NO_ENOBUFS,但会执行其中的 else 分支,不会调用 wake_up_interruptible()。

用户态示例的传参类型也有问题:val 是 char *,因此 &val 指向保存指针值的变量,sizeof(val) 得到的是指针大小。应使用整数选项值,传入该整数的地址和大小。

为什么长度为 1 时跳过读取

内核先将变量初始化为零:

unsigned int val = 0;

随后执行条件判断:

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

C 的 && 从左向右求值,遇到不成立的条件时会短路。对于 NETLINK_NO_ENOBUFS,前两个条件成立;当 optlen 为 1 且小于 sizeof(int) 时,第三个条件不成立,后面的 get_user() 就不会执行。

长度条件不成立只会让整个 if 条件为假,不会直接退出函数。假设前面的调用路径正常,代码仍会继续执行 switch。

进入对应的 case 后,val 仍为零,因此执行:

nlk->flags &= ~NETLINK_RECV_NO_ENOBUFS;

按照这段代码,该分支随后设置 err = 0。因此,即使没有触发目标调用,setsockopt() 也可能调用成功。

为什么 sizeof(val) 看起来有效

用户态代码声明的是一个指针:

char *val = "A";

这几个表达式的含义不同:

  • val:指向字符串 "A" 的指针。
  • &val:指向这个指针变量的地址。
  • sizeof(val):指针类型的大小,与字符串长度无关。

当 sizeof(val) >= sizeof(int) 时,长度条件成立,内核会尝试执行:

get_user(val, (unsigned int __user *)optval)

get_user() 成功时返回 0,并将读取到的值写入内核局部变量 val;读取失败时,这里的条件才会成立并返回 -EFAULT。

由于传入的是 &val,内核读取的是指针变量所占内存中一个 unsigned int 大小的数据,而不是字符串中的字符 A。如果读出的整数恰好非零,代码就会进入 if (val) 并调用 wake_up_interruptible()。

这可以解释观察到的现象,但不能保证这种写法总能触发该分支。结果取决于指针值的内存表示和实际读取到的内容。

正确传递选项值

假设 fd 是有效的 netlink socket 描述符,可以用整数表示启用该选项:

#include <sys/socket.h>
#include <linux/netlink.h>
#include <stdio.h>

/* fd 已指向一个有效的 netlink socket。 */
int opt = 1;

if (setsockopt(fd, SOL_NETLINK, NETLINK_NO_ENOBUFS,
               &opt, sizeof(opt)) == -1) {
    perror("setsockopt");
}

这样,内核读取到的是整数 1。在所示实现中,只要读取成功并进入该 case,就会执行设置标志、清除 NETLINK_CONGESTED 和调用 wake_up_interruptible() 的分支。

若将 opt 设为 0,则执行清除 NETLINK_RECV_NO_ENOBUFS 标志的分支。

排查时需要区分的情况

先检查 setsockopt() 的返回值,失败时再通过 errno 判断原因。原示例没有检查返回值,因此无法仅凭“没有触发目标函数”确定系统调用是否报错。

以上分析以题目给出的内核代码为依据,并假设文件描述符有效、调用能够到达 netlink_setsockopt()。如果 dangling_fd 实际上已经失效,还需要先排查描述符状态。

另外,调用 wake_up_interruptible() 后是否实际唤醒某个任务,还取决于等待队列中是否存在可被唤醒的任务。

备注:内容仅供参考。