• Kuniyuki Iwashima's avatar
    af_unix: Fix up unix_edge.successor for embryo socket. · dcf70df2
    Kuniyuki Iwashima authored
    To garbage collect inflight AF_UNIX sockets, we must define the
    cyclic reference appropriately.  This is a bit tricky if the loop
    consists of embryo sockets.
    
    Suppose that the fd of AF_UNIX socket A is passed to D and the fd B
    to C and that C and D are embryo sockets of A and B, respectively.
    It may appear that there are two separate graphs, A (-> D) and
    B (-> C), but this is not correct.
    
         A --. .-- B
              X
         C <-' `-> D
    
    Now, D holds A's refcount, and C has B's refcount, so unix_release()
    will never be called for A and B when we close() them.  However, no
    one can call close() for D and C to free skbs holding refcounts of A
    and B because C/D is in A/B's receive queue, which should have been
    purged by unix_release() for A and B.
    
    So, here's another type of cyclic reference.  When a fd of an AF_UNIX
    socket is passed to an embryo socket, the reference is indirectly held
    by its parent listening socket.
    
      .-> A                            .-> B
      |   `- sk_receive_queue          |   `- sk_receive_queue
      |      `- skb                    |      `- skb
      |         `- sk == C             |         `- sk == D
      |            `- sk_receive_queue |           `- sk_receive_queue
      |               `- skb +---------'               `- skb +-.
      |                                                         |
      `---------------------------------------------------------'
    
    Technically, the graph must be denoted as A <-> B instead of A (-> D)
    and B (-> C) to find such a cyclic reference without touching each
    socket's receive queue.
    
      .-> A --. .-- B <-.
      |        X        |  ==  A <-> B
      `-- C <-' `-> D --'
    
    We apply this fixup during GC by fetching the real successor by
    unix_edge_successor().
    
    When we call accept(), we clear unix_sock.listener under unix_gc_lock
    not to confuse GC.
    Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@amazon.com>
    Acked-by: default avatarPaolo Abeni <pabeni@redhat.com>
    Link: https://lore.kernel.org/r/20240325202425.60930-9-kuniyu@amazon.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
    dcf70df2
af_unix.c 88.4 KB