lkml.org 
[lkml]   [2017]   [Jul]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 049/172] l2tp: take a reference on sessions used in genetlink handlers
    Date
    4.9-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Guillaume Nault <g.nault@alphalink.fr>

    commit 2777e2ab5a9cf2b4524486c6db1517a6ded25261 upstream.

    Callers of l2tp_nl_session_find() need to hold a reference on the
    returned session since there's no guarantee that it isn't going to
    disappear from under them.

    Relying on the fact that no l2tp netlink message may be processed
    concurrently isn't enough: sessions can be deleted by other means
    (e.g. by closing the PPPOL2TP socket of a ppp pseudowire).

    l2tp_nl_cmd_session_delete() is a bit special: it runs a callback
    function that may require a previous call to session->ref(). In
    particular, for ppp pseudowires, the callback is l2tp_session_delete(),
    which then calls pppol2tp_session_close() and dereferences the PPPOL2TP
    socket. The socket might already be gone at the moment
    l2tp_session_delete() calls session->ref(), so we need to take a
    reference during the session lookup. So we need to pass the do_ref
    variable down to l2tp_session_get() and l2tp_session_get_by_ifname().

    Since all callers have to be updated, l2tp_session_find_by_ifname() and
    l2tp_nl_session_find() are renamed to reflect their new behaviour.

    Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
    Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    net/l2tp/l2tp_core.c | 9 +++++++--
    net/l2tp/l2tp_core.h | 3 ++-
    net/l2tp/l2tp_netlink.c | 39 ++++++++++++++++++++++++++-------------
    3 files changed, 35 insertions(+), 16 deletions(-)

    --- a/net/l2tp/l2tp_core.c
    +++ b/net/l2tp/l2tp_core.c
    @@ -356,7 +356,8 @@ EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
    /* Lookup a session by interface name.
    * This is very inefficient but is only used by management interfaces.
    */
    -struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
    +struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
    + bool do_ref)
    {
    struct l2tp_net *pn = l2tp_pernet(net);
    int hash;
    @@ -366,7 +367,11 @@ struct l2tp_session *l2tp_session_find_b
    for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) {
    hlist_for_each_entry_rcu(session, &pn->l2tp_session_hlist[hash], global_hlist) {
    if (!strcmp(session->ifname, ifname)) {
    + l2tp_session_inc_refcount(session);
    + if (do_ref && session->ref)
    + session->ref(session);
    rcu_read_unlock_bh();
    +
    return session;
    }
    }
    @@ -376,7 +381,7 @@ struct l2tp_session *l2tp_session_find_b

    return NULL;
    }
    -EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname);
    +EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);

    static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
    struct l2tp_session *session)
    --- a/net/l2tp/l2tp_core.h
    +++ b/net/l2tp/l2tp_core.h
    @@ -248,7 +248,8 @@ struct l2tp_session *l2tp_session_find(s
    u32 session_id);
    struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
    bool do_ref);
    -struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
    +struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
    + bool do_ref);
    struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
    struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);

    --- a/net/l2tp/l2tp_netlink.c
    +++ b/net/l2tp/l2tp_netlink.c
    @@ -55,7 +55,8 @@ static int l2tp_nl_session_send(struct s
    /* Accessed under genl lock */
    static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];

    -static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info)
    +static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info,
    + bool do_ref)
    {
    u32 tunnel_id;
    u32 session_id;
    @@ -66,14 +67,15 @@ static struct l2tp_session *l2tp_nl_sess

    if (info->attrs[L2TP_ATTR_IFNAME]) {
    ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
    - session = l2tp_session_find_by_ifname(net, ifname);
    + session = l2tp_session_get_by_ifname(net, ifname, do_ref);
    } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
    (info->attrs[L2TP_ATTR_CONN_ID])) {
    tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
    session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
    tunnel = l2tp_tunnel_find(net, tunnel_id);
    if (tunnel)
    - session = l2tp_session_find(net, tunnel, session_id);
    + session = l2tp_session_get(net, tunnel, session_id,
    + do_ref);
    }

    return session;
    @@ -652,7 +654,7 @@ static int l2tp_nl_cmd_session_delete(st
    struct l2tp_session *session;
    u16 pw_type;

    - session = l2tp_nl_session_find(info);
    + session = l2tp_nl_session_get(info, true);
    if (session == NULL) {
    ret = -ENODEV;
    goto out;
    @@ -666,6 +668,10 @@ static int l2tp_nl_cmd_session_delete(st
    if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
    ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);

    + if (session->deref)
    + session->deref(session);
    + l2tp_session_dec_refcount(session);
    +
    out:
    return ret;
    }
    @@ -675,7 +681,7 @@ static int l2tp_nl_cmd_session_modify(st
    int ret = 0;
    struct l2tp_session *session;

    - session = l2tp_nl_session_find(info);
    + session = l2tp_nl_session_get(info, false);
    if (session == NULL) {
    ret = -ENODEV;
    goto out;
    @@ -710,6 +716,8 @@ static int l2tp_nl_cmd_session_modify(st
    ret = l2tp_session_notify(&l2tp_nl_family, info,
    session, L2TP_CMD_SESSION_MODIFY);

    + l2tp_session_dec_refcount(session);
    +
    out:
    return ret;
    }
    @@ -805,29 +813,34 @@ static int l2tp_nl_cmd_session_get(struc
    struct sk_buff *msg;
    int ret;

    - session = l2tp_nl_session_find(info);
    + session = l2tp_nl_session_get(info, false);
    if (session == NULL) {
    ret = -ENODEV;
    - goto out;
    + goto err;
    }

    msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
    if (!msg) {
    ret = -ENOMEM;
    - goto out;
    + goto err_ref;
    }

    ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
    0, session, L2TP_CMD_SESSION_GET);
    if (ret < 0)
    - goto err_out;
    + goto err_ref_msg;

    - return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
    + ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);

    -err_out:
    - nlmsg_free(msg);
    + l2tp_session_dec_refcount(session);

    -out:
    + return ret;
    +
    +err_ref_msg:
    + nlmsg_free(msg);
    +err_ref:
    + l2tp_session_dec_refcount(session);
    +err:
    return ret;
    }


    \
     
     \ /
      Last update: 2017-07-03 16:29    [W:4.046 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site