• Pavel Emelyanov's avatar
    [INET]: Fix inet_diag register vs rcv race · 07693198
    Pavel Emelyanov authored
    The following race is possible when one cpu unregisters the handler
    while other one is trying to receive a message and call this one:
    
    CPU1:                                                 CPU2:
    inet_diag_rcv()                                       inet_diag_unregister()
      mutex_lock(&inet_diag_mutex);
      netlink_rcv_skb(skb, &inet_diag_rcv_msg);
        if (inet_diag_table[nlh->nlmsg_type] == 
                                   NULL) /* false handler is still registered */
        ...
        netlink_dump_start(idiagnl, skb, nlh,
                               inet_diag_dump, NULL);
               cb = kzalloc(sizeof(*cb), GFP_KERNEL);
                       /* sleep here freeing memory 
                        * or preempt
                        * or sleep later on nlk->cb_mutex
                        */
                                                             spin_lock(&inet_diag_register_lock);
                                                             inet_diag_table[type] = NULL;
        ...                                                  spin_unlock(&inet_diag_register_lock);
                                                             synchronize_rcu();
                                                             /* CPU1 is sleeping - RCU quiescent
                                                              * state is passed
                                                              */
                                                             return;
        /* inet_diag_dump is finally called: */
        inet_diag_dump()
          handler = inet_diag_table[cb->nlh->nlmsg_type];
          BUG_ON(handler == NULL); 
          /* OOPS! While we slept the unregister has set
           * handler to NULL :(
           */
    
    Grep showed, that the register/unregister functions are called
    from init/fini module callbacks for tcp_/dccp_diag, so it's OK
    to use the inet_diag_mutex to synchronize manipulations with the
    inet_diag_table and the access to it.
    
    Besides, as Herbert pointed out, asynchronous dumps should hold 
    this mutex as well, and thus, we provide the mutex as cb_mutex one.
    Signed-off-by: default avatarPavel Emelyanov <xemul@openvz.org>
    Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
    07693198
inet_diag.c 22 KB