socket()返回的EPERM错误未在manpage中记载的技术咨询
socket(3p)的manpage记载了该函数的签名及基础使用说明,文档的错误章节列出了若干errno值及对应错误触发原因。但实际使用时遇到了未被文档记载的EPERM错误,其数值为1,对应含义为Operation not permitted。该问题最初是在ArchLinux环境、glibc-2.33、clang-12.0.1版本下发现的,以下是对应的概念验证(PoC)代码:
#include <errno.h> #include <sys/socket.h> #include <netinet/ip.h> int main() { socket(PF_INET, SOCK_RAW, IPPROTO_TCP); return errno; }
可通过以下命令复现问题,PoC代码保存在raw-socket-poc.c文件中:
$ clang -O0 -g raw-socket-poc.c -o raw-socket-poc $ ./raw-socket-poc; errno $? EPERM 1 Operation not permitted
额外测试发现:
- 将
IPPROTO_TCP替换为IPPROTO_UDP仍会触发相同错误 - 将
IPPROTO_TCP替换为0时,程序会返回已被文档记载的EPROTONOSUPPORT错误(数值为93,对应含义为Protocol not supported) - 以root身份运行原始PoC不会触发该错误,但无特权用户运行已赋予
CAP_NET_RAW权限的PoC程序时仍会触发该错误
已通过# setcap CAP_NET_RAW=+eip ./raw-socket-poc命令为程序提升了对应权限,且通过$ getcap ./raw-socket-poc命令确认权限设置生效,返回结果为./raw-socket-poc cap_net_raw=eip。
目前现有信息不足以判断该文档缺失是有意设计还是疏漏,希望能得到关于manpage设计逻辑、原始套接字相关权限机制的相关说明。
文档未记载EPERM的原因
你查阅的section 3p类手册属于POSIX标准规范文档,仅记录POSIX标准明确要求的通用接口行为,不会包含任何Linux平台特有的实现细节。原始套接字的权限控制属于Linux专属设计,因此EPERM不会被列入3p类手册的错误列表,不属于文档疏漏。
如果查阅Linux原生系统调用的socket(2)手册,其错误章节已经明确标注EPERM的触发场景:调用方没有足够权限创建指定类型的套接字。
原始套接字权限机制说明
SOCK_RAW类型的套接字允许用户直接操作网络层报文,属于高风险权限,Linux默认仅允许持有CAP_NET_RAW能力的进程创建这类套接字:
- root用户默认持有所有特权能力,因此运行PoC不会报错
- 普通用户默认没有
CAP_NET_RAW能力,直接运行会返回EPERM
配置文件能力后仍报错的原因
你已经给二进制配置了CAP_NET_RAW=eip但仍报错,最常见的原因是二进制所在分区使用了nosuid参数挂载:
文件能力和suid位同属特权配置,nosuid挂载参数会让内核忽略对应分区下所有二进制的suid位与文件能力配置,此时getcap虽然能读取到文件存储的能力标记,但内核实际执行时不会授予对应权限,因此仍然返回EPERM。
少数场景下也可能是系统seccomp过滤规则、systemd服务的能力边界集(CapabilityBoundingSet)、全局sysctl配置封禁了CAP_NET_RAW能力,导致配置不生效。
manpage设计逻辑
Linux的man手册按section区分适用场景:
- section 2:Linux原生系统调用手册,完整记载Linux特有的实现细节、扩展返回值、权限要求等平台相关内容,开发Linux专属程序时优先查阅
- section 3p:POSIX标准接口规范文档,仅记载跨平台兼容的标准内容,用于开发跨POSIX平台兼容的程序时参考
内容的提问来源于stack exchange,提问作者TJM

