You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

因Python socket限制用ctypes调用libc accept时出现EFAULT错误,求助

我来帮你捋捋这个EFAULT错误的来龙去脉,毕竟用ctypes直接怼系统接口的坑我也踩过不少😉

首先得明确:EFAULT本质是内核没法访问你传给它的内存地址,说白了就是你给accept的参数(尤其是客户端地址相关的指针)有问题,内核读/写不了那块内存。下面是几个最可能的排查方向和解决方法:

1. 别在sockaddr结构体上偷懒

不同地址族对应的sockaddr结构体完全不一样,比如你用AF_PACKET就得用sockaddr_ll,AF_NETLINK用sockaddr_nl,绝对不能随便用个字节串或者空指针糊弄。而且必须用ctypes的结构体类型分配内存,还要传它的指针给accept,不能直接传结构体实例本身。

举个正确的结构体定义+内存分配例子(以AF_PACKET为例):

import ctypes
from ctypes import cdll, Structure, byref, c_uint16, c_int32, c_uint8, c_uint32

libc = cdll.LoadLibrary('libc.so.6')

# 对应AF_PACKET的地址结构体
class sockaddr_ll(Structure):
    _fields_ = [
        ("sll_family", c_uint16),
        ("sll_protocol", c_uint16),
        ("sll_ifindex", c_int32),
        ("sll_hatype", c_uint16),
        ("sll_pkttype", c_uint8),
        ("sll_halen", c_uint8),
        ("sll_addr", c_uint8 * 8)
    ]

# 分配客户端地址内存和长度变量(必须用ctypes的类型,不能用Python原生int/bytes)
client_addr = sockaddr_ll()
addr_len = c_uint32(ctypes.sizeof(client_addr))

然后调用accept的时候一定要传指针:

conn_fd = libc.accept(sock_fd, byref(client_addr), byref(addr_len))

2. 给函数绑定正确的参数类型

别让ctypes自动猜参数类型!如果不给libc.accept设置argtypes和restype,ctypes会乱转参数,很容易把指针截断或者类型不匹配,直接导致内核拿错内存地址。

正确的类型绑定示例:

# 根据你用的地址族,把第二个参数换成对应的结构体指针类型
libc.accept.argtypes = [ctypes.c_int, byref(sockaddr_ll), byref(c_uint32)]
libc.accept.restype = ctypes.c_int

# 顺便把socket/bind/listen也绑定上,避免其他环节出问题
libc.socket.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
libc.socket.restype = ctypes.c_int
libc.bind.argtypes = [ctypes.c_int, Structure, c_uint32]
libc.bind.restype = ctypes.c_int

3. 确认前置步骤的正确性

虽然你说bind和listen成功了,但还是要核对:

  • 绑定的地址结构体是不是完全符合对应地址族的要求?比如AF_PACKET需要指定正确的网卡ifindex,AF_NETLINK要设置nl_pid。
  • listen的backlog参数是不是合法值(比如1-128之间,别传负数或者超大数)?

4. 检查内存权限

如果你是手动用mmap分配的内存,要确保权限是PROT_READ | PROT_WRITE,不然内核没法读写这块内存也会触发EFAULT。不过用ctypes的结构体实例或者create_string_buffer分配的内存一般都是没问题的。

最后给你一个完整的小示例(AF_PACKET版本),可以对照着改自己的代码:

import ctypes
import os
from ctypes import cdll, Structure, byref, c_int, c_uint16, c_int32, c_uint8, c_uint32

libc = cdll.LoadLibrary('libc.so.6')

class sockaddr_ll(Structure):
    _fields_ = [
        ("sll_family", c_uint16),
        ("sll_protocol", c_uint16),
        ("sll_ifindex", c_int32),
        ("sll_hatype", c_uint16),
        ("sll_pkttype", c_uint8),
        ("sll_halen", c_uint8),
        ("sll_addr", c_uint8 * 8)
    ]

# 绑定函数类型
libc.socket.argtypes = [c_int, c_int, c_int]
libc.socket.restype = c_int
libc.bind.argtypes = [c_int, Structure, c_uint32]
libc.bind.restype = c_int
libc.listen.argtypes = [c_int, c_int]
libc.listen.restype = c_int
libc.accept.argtypes = [c_int, byref(sockaddr_ll), byref(c_uint32)]
libc.accept.restype = c_int

AF_PACKET = 17
SOCK_RAW = 3
ETH_P_ALL = 0x0003

# 创建套接字
sock_fd = libc.socket(AF_PACKET, SOCK_RAW, ETH_P_ALL)
if sock_fd == -1:
    raise OSError(f"Socket create failed: {os.strerror(ctypes.get_errno())}")

# 绑定到网卡(替换成你的网卡ifindex,用ip link show可以查)
bind_addr = sockaddr_ll()
bind_addr.sll_family = AF_PACKET
bind_addr.sll_protocol = ETH_P_ALL
bind_addr.sll_ifindex = 1  # 比如eth0的ifindex是1

if libc.bind(sock_fd, bind_addr, c_uint32(ctypes.sizeof(bind_addr))) == -1:
    raise OSError(f"Bind failed: {os.strerror(ctypes.get_errno())}")

if libc.listen(sock_fd, 5) == -1:
    raise OSError(f"Listen failed: {os.strerror(ctypes.get_errno())}")

# 接受连接
client_addr = sockaddr_ll()
addr_len = c_uint32(ctypes.sizeof(client_addr))
conn_fd = libc.accept(sock_fd, byref(client_addr), byref(addr_len))
if conn_fd == -1:
    errno = ctypes.get_errno()
    print(f"Accept failed with errno {errno}: {os.strerror(errno)}")
else:
    print(f"Accepted connection, fd: {conn_fd}")

如果还是有问题,建议打印ctypes.get_errno()对应的错误描述,再仔细核对参数的指针传递——90%的EFAULT都是指针传错了。

内容的提问来源于stack exchange,提问作者Patrick Sullivan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:41:10