• Xin Long's avatar
    sctp: add chunks to sk_backlog when the newsk sk_socket is not set · 819be810
    Xin Long authored
    This patch is to fix a NULL-ptr deref in selinux_socket_connect_helper:
    
      [...] kasan: GPF could be caused by NULL-ptr deref or user memory access
      [...] RIP: 0010:selinux_socket_connect_helper+0x94/0x460
      [...] Call Trace:
      [...]  selinux_sctp_bind_connect+0x16a/0x1d0
      [...]  security_sctp_bind_connect+0x58/0x90
      [...]  sctp_process_asconf+0xa52/0xfd0 [sctp]
      [...]  sctp_sf_do_asconf+0x785/0x980 [sctp]
      [...]  sctp_do_sm+0x175/0x5a0 [sctp]
      [...]  sctp_assoc_bh_rcv+0x285/0x5b0 [sctp]
      [...]  sctp_backlog_rcv+0x482/0x910 [sctp]
      [...]  __release_sock+0x11e/0x310
      [...]  release_sock+0x4f/0x180
      [...]  sctp_accept+0x3f9/0x5a0 [sctp]
      [...]  inet_accept+0xe7/0x720
    
    It was caused by that the 'newsk' sk_socket was not set before going to
    security sctp hook when processing asconf chunk with SCTP_PARAM_ADD_IP
    or SCTP_PARAM_SET_PRIMARY:
    
      inet_accept()->
        sctp_accept():
          lock_sock():
              lock listening 'sk'
                                              do_softirq():
                                                sctp_rcv():  <-- [1]
                                                    asconf chunk arrives and
                                                    enqueued in 'sk' backlog
          sctp_sock_migrate():
              set asoc's sk to 'newsk'
          release_sock():
              sctp_backlog_rcv():
                lock 'newsk'
                sctp_process_asconf()  <-- [2]
                unlock 'newsk'
        sock_graft():
            set sk_socket  <-- [3]
    
    As it shows, at [1] the asconf chunk would be put into the listening 'sk'
    backlog, as accept() was holding its sock lock. Then at [2] asconf would
    get processed with 'newsk' as asoc's sk had been set to 'newsk'. However,
    'newsk' sk_socket is not set until [3], while selinux_sctp_bind_connect()
    would deref it, then kernel crashed.
    
    Here to fix it by adding the chunk to sk_backlog until newsk sk_socket is
    set when .accept() is done.
    
    Note that sk->sk_socket can be NULL when the sock is closed, so SOCK_DEAD
    flag is also needed to check in sctp_newsk_ready().
    
    Thanks to Ondrej for reviewing the code.
    
    Fixes: d452930f ("selinux: Add SCTP support")
    Reported-by: default avatarYing Xu <yinxu@redhat.com>
    Suggested-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
    Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
    Acked-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
    Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
    Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
    819be810
input.c 34.9 KB