【问题标题】:EFAULT when using ctypes with python and calling libc accept将 ctypes 与 python 一起使用并调用 libc 时的 EFAULT 接受
【发布时间】:2018-02-28 22:22:08
【问题描述】:

普通的python socket模块在创建socket时不支持AF_INET以外的协议:

来自 cpython socketmodule.c


  • 仅支持 AF_INET、AF_INET6 和 AF_UNIX 地址系列 可移植的方式,虽然支持 AF_PACKET、AF_NETLINK 和 AF_TIPC
    在 Linux 下。

所以我已经着手使用 ctypes 直接从 libc 手动调用普通 BSD 套接字库的套接字、绑定、侦听和接受调用。 我可以创建套接字,将套接字绑定到一个地址,并将套接字置于侦听模式,这需要对我所做的继承 SockAddr_In 类的结构的引用进行转换:

CharArr14 = c_char * 14                                                                                                       
In_Addr = c_uint32                                                                                                            
CharArr8 = c_char * 8                                                                                                         


class SockAddr(Structure):                                                                                                    
    _fields_ = [                                                                                                              
        ('sa_len', c_uint8),                                                                                                  
        ('sa_family', c_uint8),                                                                                               
        ('sa_data', CharArr14)                                                                                                
    ]                                                                                                                         


class SockAddr_In(Structure):                                                                                                 
    _fields_ = [                                                                                                              
        ('sa_len', c_uint8),                                                                                                  
        ('sa_family', c_uint8),                                                                                               
        ('sin_port', c_uint16),                                                                                               
        ('sin_addr', In_Addr),                                                                                                
        ('sin_zero', CharArr8)                                                                                                
    ]  

但是当我尝试调用接受时,它传递了一个地址,内核将写入连接套接字的 SockAddr_In 结构信息,我收到与错误内存地址相对应的 EFAULT (errno 14)。

def accept_sdp_sock(self):                                                                                                
    accept = libc.accept                                                                                                  
    logger.debug("Accepting socket on: {}".format(self.ip_addr))                                                          
    # int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);                                                  
    # Why is this a bad address? Is it being deallocated?                                                                 
    # Maybe it isn't allocating until we fill it with data?                                                               

    addr = SockAddr_In()                                                                                                  
    logger.debug("Memory address of addr struct: {}".format(addr))                                                        
    afd = accept(self.fd,                                                                                                 
                 cast(byref(addr), POINTER(SockAddr)),                                                                    
                 sizeof(SockAddr_In))                                                                                     
    # Check accept fd for errors                                                                                          
    if afd == -1:                                                                                                         
        self.errno = get_errno()                                                                                          
        logger.debug("Accept call failed: {} ".format(self.errno))                                                        

    return afd

所以我们看到这个输出带有程序列出的内存地址

#truss -fead -s65535 -o truss_out python sdp_sock_cmd.py -l --address 192.168.66.140 --debug
2018-02-28 13:59:40,008 - DEBUG - This is a debug message
2018-02-28 13:59:40,009 - INFO - And an info
2018-02-28 13:59:40,009 - DEBUG - Creating SDP socket 
2018-02-28 13:59:40,010 - DEBUG - Binding SDP socket to : 192.168.66.140
2018-02-28 13:59:40,011 - DEBUG - Memory address of addr struct: <ib_socks.SockAddr_In object at 0x80385d9e0>
2018-02-28 13:59:40,012 - DEBUG - Socket listening on: 192.168.66.140
2018-02-28 13:59:40,013 - DEBUG - Accepting socket on: 192.168.66.140
2018-02-28 13:59:40,014 - DEBUG - Memory address of addr struct: <ib_socks.SockAddr_In object at 0x80385d9e0>
2018-02-28 13:59:40,015 - DEBUG - Accept call failed: 14

在 truss 输出中我们看到:

18105: 0.288079361 accept(3,0x80385da30,0x10)    ERR#14 'Bad address'

我尝试使用 libc 中的 malloc 进行分配,使用 ctypes 中的 create_string_buffer() 创建字符串缓冲区,但在这两种情况下我都看到了 EFAULT。为什么在这种情况下我会看到 EFAULT?如何使用 ctypes 使用 python 分配数据结构以允许内核将数据移动到用户空间?

【问题讨论】:

  • 你应该定义acceptargtypesrestype

标签: python sockets ctypes cpython


【解决方案1】:

我看到一个明显的问题。我已经成功使用ctypes,但不能自称是专家,所以可能还有其他问题。

我认为您必须在某个地方为libc.accept 定义参数原型,但这不在您发布的源代码中,所以我认为它是正确的。假设原型与 libc 绑定一致:accept's 第三个参数 (addrlen) 是一个指向socklen_t指针,但您只是传递了一个长度(不是一个指针到一定长度)。

通常的做法是创建一个socklen_t 变量并用addr 缓冲区的大小填充它,然后accept(正在完成)复制对等方的地址到您的addr缓冲区,如果它太大则截断,但更新传递的addrlen 与实际大小。来自(linux版本)accept(2)

addrlen 参数是一个值结果参数:调用者必须初始化它以包含addr 指向的结构的大小(以字节为单位);返回时它将包含对等地址的实际大小。

如果提供的缓冲区太小,返回的地址会被截断;在这种情况下,addrlen 将返回一个大于提供给调用的值。

因此,总而言之,可能是 第三个​​ 参数导致您的EFAULT - 而不是第二个。您的truss 输出支持这一点,该输出显示addrlen 作为0x10 传递给内核:这通常不是有效的内存地址。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-09
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 2017-07-28
    相关资源
    最近更新 更多