• Guillaume Nault's avatar
    l2tp: initialise PPP sessions before registering them · ad6c13e2
    Guillaume Nault authored
    commit f98be6c6 upstream.
    
    pppol2tp_connect() initialises L2TP sessions after they've been exposed
    to the rest of the system by l2tp_session_register(). This puts
    sessions into transient states that are the source of several races, in
    particular with session's deletion path.
    
    This patch centralises the initialisation code into
    pppol2tp_session_init(), which is called before the registration phase.
    The only field that can't be set before session registration is the
    pppol2tp socket pointer, which has already been converted to RCU. So
    pppol2tp_connect() should now be race-free.
    
    The session's .session_close() callback is now set before registration.
    Therefore, it's always called when l2tp_core deletes the session, even
    if it was created by pppol2tp_session_create() and hasn't been plugged
    to a pppol2tp socket yet. That'd prevent session free because the extra
    reference taken by pppol2tp_session_close() wouldn't be dropped by the
    socket's ->sk_destruct() callback (pppol2tp_session_destruct()).
    We could set .session_close() only while connecting a session to its
    pppol2tp socket, or teach pppol2tp_session_close() to avoid grabbing a
    reference when the session isn't connected, but that'd require adding
    some form of synchronisation to be race free.
    
    Instead of that, we can just let the pppol2tp socket hold a reference
    on the session as soon as it starts depending on it (that is, in
    pppol2tp_connect()). Then we don't need to utilise
    pppol2tp_session_close() to hold a reference at the last moment to
    prevent l2tp_core from dropping it.
    
    When releasing the socket, pppol2tp_release() now deletes the session
    using the standard l2tp_session_delete() function, instead of merely
    removing it from hash tables. l2tp_session_delete() drops the reference
    the sessions holds on itself, but also makes sure it doesn't remove a
    session twice. So it can safely be called, even if l2tp_core already
    tried, or is concurrently trying, to remove the session.
    Finally, pppol2tp_session_destruct() drops the reference held by the
    socket.
    
    Fixes: fd558d18 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
    Signed-off-by: default avatarGuillaume Nault <g.nault@alphalink.fr>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    Signed-off-by: default avatarGiuliano Procida <gprocida@google.com>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    ad6c13e2
l2tp_ppp.c 48.2 KB