Changeset 5778


Ignore:
Timestamp:
Apr 6, 2018 6:11:36 AM (6 years ago)
Author:
ming
Message:

Re #2091:

  • Fix possible multiple socket closes and querying already-closed sockets.
  • Also prevent possible exception if replace_udp_sock() fails.

If replace_udp_sock() fails, then key->fd will have already been closed. So when calling pj_ioqueue_unregister(key), it will attempt to close the socket again. This may (but not always) result in an exception, which seems to happen when the socket descriptor has been reused by another app.

Explanation: EXC_GUARD exception happens when you try to close a file descriptor that you don't own.

Stack trace:
Exception Type: EXC_GUARD
Exception Subtype: GUARD_TYPE_FD
Exception Message: CLOSE on file descriptor 11 (guarded with 0x08fd4dbfade2dead)
Exception Note: SIMULATED (this is NOT a crash) requested by (null)
Triggered by Thread: 7

Thread 7 Crashed:
0 libsystem_kernel.dylib 0x0000000183b09224 close + 8
1 0x00000001031b5b58 pj_sock_close + 12
2 0x00000001031b1d58 pj_ioqueue_unregister + 120
3 0x000000010313b018 udp_destroy + 44
4 0x0000000103138bf8 destroy_transport + 220
5 0x000000010313942c pjsip_tpmgr_destroy + 132
6 0x0000000103133a40 pjsip_endpt_destroy + 244
7 0x000000010315a1b8 pjsua_destroy2 + 2640

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/src/pj/ioqueue_select.c

    r5737 r5778  
    510510 
    511511    /* Close socket. */ 
    512     pj_sock_close(key->fd); 
     512    if (key->fd != PJ_INVALID_SOCKET) { 
     513        pj_sock_close(key->fd); 
     514        key->fd = PJ_INVALID_SOCKET; 
     515    } 
    513516 
    514517    /* Clear callback */ 
     
    723726    old_sock = h->fd; 
    724727 
     728    fds_cnt = 0; 
     729    fds[fds_cnt++] = &h->ioqueue->rfdset; 
     730    fds[fds_cnt++] = &h->ioqueue->wfdset; 
     731#if PJ_HAS_TCP 
     732    fds[fds_cnt++] = &h->ioqueue->xfdset; 
     733#endif 
     734 
    725735    /* Can only replace UDP socket */ 
    726736    pj_assert(h->fd_type == pj_SOCK_DGRAM()); 
     
    736746        } 
    737747         
    738         /* Investigate the old socket */ 
    739         addr_len = sizeof(local_addr); 
    740         status = pj_sock_getsockname(old_sock, &local_addr, &addr_len); 
    741         if (status != PJ_SUCCESS) { 
    742             PJ_LOG(5,(THIS_FILE, "Error get socket name %d", status)); 
    743             continue; 
    744         } 
     748        if (old_sock != PJ_INVALID_SOCKET) { 
     749            /* Investigate the old socket */ 
     750            addr_len = sizeof(local_addr); 
     751            status = pj_sock_getsockname(old_sock, &local_addr, &addr_len); 
     752            if (status != PJ_SUCCESS) { 
     753                PJ_LOG(5,(THIS_FILE, "Error get socket name %d", status)); 
     754                continue; 
     755            } 
    745756         
    746         addr_len = sizeof(rem_addr); 
    747         status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len); 
    748         if (status != PJ_SUCCESS) { 
    749             PJ_LOG(5,(THIS_FILE, "Error get peer name %d", status)); 
    750         } else { 
    751             flags |= HAS_PEER_ADDR; 
    752         } 
    753  
    754         status = pj_sock_get_qos_params(old_sock, &qos_params); 
    755         if (status == PJ_STATUS_FROM_OS(EBADF) || 
    756             status == PJ_STATUS_FROM_OS(EINVAL)) 
    757         { 
    758             PJ_LOG(5,(THIS_FILE, "Error get qos param %d", status)); 
    759             continue; 
    760         } 
     757            addr_len = sizeof(rem_addr); 
     758            status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len); 
     759            if (status != PJ_SUCCESS) { 
     760                PJ_LOG(5,(THIS_FILE, "Error get peer name %d", status)); 
     761            } else { 
     762                flags |= HAS_PEER_ADDR; 
     763            } 
     764 
     765            status = pj_sock_get_qos_params(old_sock, &qos_params); 
     766            if (status == PJ_STATUS_FROM_OS(EBADF) || 
     767                status == PJ_STATUS_FROM_OS(EINVAL)) 
     768            { 
     769                PJ_LOG(5,(THIS_FILE, "Error get qos param %d", status)); 
     770                continue; 
     771            } 
    761772         
    762         if (status != PJ_SUCCESS) { 
    763             PJ_LOG(5,(THIS_FILE, "Error get qos param %d", status)); 
    764         } else { 
    765             flags |= HAS_QOS; 
    766         } 
    767  
    768         /* We're done with the old socket, close it otherwise we'll get 
    769          * error in bind() 
    770          */ 
    771         status = pj_sock_close(old_sock); 
    772         if (status != PJ_SUCCESS) { 
    773             PJ_LOG(5,(THIS_FILE, "Error closing socket %d", status)); 
     773            if (status != PJ_SUCCESS) { 
     774                PJ_LOG(5,(THIS_FILE, "Error get qos param %d", status)); 
     775            } else { 
     776                flags |= HAS_QOS; 
     777            } 
     778 
     779            /* We're done with the old socket, close it otherwise we'll get 
     780             * error in bind() 
     781             */ 
     782            status = pj_sock_close(old_sock); 
     783            if (status != PJ_SUCCESS) { 
     784                PJ_LOG(5,(THIS_FILE, "Error closing socket %d", status)); 
     785            } 
     786             
     787            old_sock = PJ_INVALID_SOCKET; 
    774788        } 
    775789 
     
    845859     * fd sets. 
    846860     */ 
    847     fds_cnt = 0; 
    848     fds[fds_cnt++] = &h->ioqueue->rfdset; 
    849     fds[fds_cnt++] = &h->ioqueue->wfdset; 
    850 #if PJ_HAS_TCP 
    851     fds[fds_cnt++] = &h->ioqueue->xfdset; 
    852 #endif 
    853  
    854861    for (i=0; i<fds_cnt; ++i) { 
    855         if (PJ_FD_ISSET(old_sock, fds[i])) { 
    856             PJ_FD_CLR(old_sock, fds[i]); 
     862        if (PJ_FD_ISSET(h->fd, fds[i])) { 
     863            PJ_FD_CLR(h->fd, fds[i]); 
    857864            PJ_FD_SET(new_sock, fds[i]); 
    858865        } 
     
    871878    if (new_sock != PJ_INVALID_SOCKET) 
    872879        pj_sock_close(new_sock); 
     880    if (old_sock != PJ_INVALID_SOCKET) 
     881        pj_sock_close(old_sock); 
     882 
     883    /* Clear the occurrence of old socket in the fd sets. */ 
     884    for (i=0; i<fds_cnt; ++i) { 
     885        if (PJ_FD_ISSET(h->fd, fds[i])) { 
     886            PJ_FD_CLR(h->fd, fds[i]); 
     887        } 
     888    } 
     889 
     890    h->fd = PJ_INVALID_SOCKET; 
    873891    PJ_PERROR(1,(THIS_FILE, status, "Error replacing socket [%d]", status)); 
    874892    pj_lock_release(h->ioqueue->lock); 
Note: See TracChangeset for help on using the changeset viewer.