Linux内核3.16中optlen=1为何无法触发netlink_setsockopt的wake_up_interruptible?
为什么指定optlen=1无法触发NETLINK_NO_ENOBUFS分支中的wake_up_interruptible()?
问题核心出在netlink_setsockopt的条件判断逻辑,以及val变量的初始化值:
1. 内核中的关键条件判断
先看这段核心代码:
if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING && optlen >= sizeof(int) && get_user(val, (unsigned int __user *)optval)) return -EFAULT;
这里采用短路与逻辑:只有所有条件同时满足时,才会返回-EFAULT。反之,若任一条件不成立,整个if块不会执行,val会保持初始值0。
2. optlen=1时的执行流程
当传入optlen=1时:
optname=NETLINK_NO_ENOBUFS,满足第一个条件(不是RX/TX_RING);- 但
1 < sizeof(int)(32位系统sizeof(int)=4,64位系统sizeof(int)为4或8),第二个条件不成立; - 短路逻辑导致
get_user(val, ...)完全不执行,val保持初始化的0; - 进入
NETLINK_NO_ENOBUFS分支后,if(val)判断为假,仅执行else分支的标志位清除操作,不会触发wake_up_interruptible(),看起来像是未进入分支,实际是进入分支但未走到唤醒逻辑。
3. 使用sizeof(val)时的执行流程
当传入optlen=sizeof(val)时:
val是char*类型,32位系统中sizeof(val)=4,64位系统中sizeof(val)=8,均满足optlen >= sizeof(int);get_user(val, ...)执行,将用户空间中&val指向的指针值(字符串"A"的内存地址,非0)读取到内核的val变量;- 进入
NETLINK_NO_ENOBUFS分支后,if(val)判断为真,执行标志位设置、清除拥塞位和wake_up_interruptible()的完整逻辑。
额外注意:用户代码中的潜在问题
你的代码中setsockopt第四个参数传的是&val(char**类型),但内核按unsigned int __user*读取,本质是把指针值当作选项值使用。虽然能触发逻辑,但不符合NETLINK_NO_ENOBUFS的设计预期——该选项的正确用法应传入unsigned int类型的0/1值,而非指针地址。
内容的提问来源于stack exchange,提问作者Cyb3rCr0wCC
相关产品推荐
相关产品推荐

