首页 文章

使用ctypes和python并调用libc accept时的EFAULT

提问于
浏览
0

创建套接字时,普通的python套接字模块不支持除AF_INET之外的协议:

来自cpython socketmodule.c:

  • a中仅支持AF_INET,AF_INET6和AF_UNIX地址系列
    便携式方式,但支持AF_PACKET,AF_NETLINK和AF_TIPC
    在Linux下 .

所以我已经开始使用ctypes手动调用正常的BSD套接字库的套接字,绑定,监听和接受来自libc的调用 . 我可以创建套接字,将套接字绑定到一个地址,并将套接字置于监听模式,这需要转换对我所做的结构继承的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)                                                                                                
    ]

但是当我尝试调用accept时,它传递了一个地址,内核将写入连接套接字的SockAddr_In结构信息,我收到的EFAULT(错误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

在桁架输出中我们看到:

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

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

1 回答

  • 0

    我看到一个明显的问题 . 我已成功使用 ctypes 但不能声称自己是专家,因此可能还存在其他一些问题 .

    我认为你必须在某个地方定义 libc.accept 的参数原型,但是's not in your posted source code so I will assume it'是正确的 . 假设原型与libc绑定一致: accept's 第三个参数( addrlen )是指向 socklen_t 的指针,但是你只是传递一个长度(不是指向长度的指针) .

    通常的方法是你创建一个 socklen_t 变量并用 addr 缓冲区的大小填充它,然后 accept (因为它's completing) copies the peer'的地址到你的 addr 缓冲区,截断它是否太大,但更新传递的 addrlen 与实际大小 . 来自(linux版本) accept(2)

    addrlen参数是一个value-result参数:调用者必须初始化它以包含addr指向的结构的大小(以字节为单位);返回时它将包含对等地址的实际大小 . 如果提供的缓冲区太小,则截断返回的地址;在这种情况下,addrlen将返回一个大于提供给调用的值 .

    所以,总而言之,它可能是导致你的第三个参数 - 而不是第二个参数 . 这是由 truss 输出支持的,它显示 addrlen 作为 0x10 传递给内核:这通常不是有效的内存地址 .

相关问题