A malicious actor on the backbone Ethernet may spam the kernel with BLA
claims using a random source MAC address and a valid group ID (local group
ID, or one that exists on the mesh), each of which will create a BLA claim,
a dynamic allocation in the kernel. This will at some point exhaust the
available memory.

Mitigate this by maintaining a per meshif count of those automatically
generated entries in bla.claim_learned, and a limit in
bla.claim_max_learned. If the limit is hit, new entries are not learned
anymore.

For backwards compatibility, the default setting of 0 disables the limit.

Signed-off-by: Sven Eckelmann <[email protected]>
---
 include/uapi/linux/batman_adv.h        |  6 ++++++
 net/batman-adv/bridge_loop_avoidance.c | 21 ++++++++++++++++++---
 net/batman-adv/mesh-interface.c        |  3 +++
 net/batman-adv/netlink.c               | 10 ++++++++++
 net/batman-adv/types.h                 |  6 ++++++
 5 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h
index 33049818..460e75c4 100644
--- a/include/uapi/linux/batman_adv.h
+++ b/include/uapi/linux/batman_adv.h
@@ -505,6 +505,12 @@ enum batadv_nl_attrs {
         */
        BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED,
 
+       /**
+        * @BATADV_ATTR_BLA_CLAIM_MAX_LEARNED: defines the maximum number of 
BLA claims
+        * which can be learned in parallel
+        */
+       BATADV_ATTR_BLA_CLAIM_MAX_LEARNED,
+
        /* add attributes above here, update the policy in netlink.c */
 
        /**
diff --git a/net/batman-adv/bridge_loop_avoidance.c 
b/net/batman-adv/bridge_loop_avoidance.c
index 949c887b..d8e7d7ad 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -182,6 +182,7 @@ static void batadv_claim_release(struct kref *ref)
 {
        struct batadv_bla_backbone_gw *old_backbone_gw;
        struct batadv_bla_claim *claim;
+       struct batadv_priv *bat_priv;
 
        claim = container_of(ref, struct batadv_bla_claim, refcount);
 
@@ -194,8 +195,10 @@ static void batadv_claim_release(struct kref *ref)
        old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
        spin_unlock_bh(&old_backbone_gw->crc_lock);
 
+       bat_priv = old_backbone_gw->bat_priv;
        batadv_backbone_gw_put(old_backbone_gw);
 
+       atomic_dec(&bat_priv->bla.claim_learned);
        kfree_rcu(claim, rcu);
 }
 
@@ -713,6 +716,8 @@ static void batadv_bla_add_claim(struct batadv_priv 
*bat_priv,
        struct batadv_bla_backbone_gw *old_backbone_gw;
        struct batadv_bla_claim search_claim;
        struct batadv_bla_claim *claim;
+       u32 bla_claim_max_learned;
+       int bla_claim_learned;
        int hash_added;
        u16 claim_crc;
        bool changed;
@@ -724,9 +729,18 @@ static void batadv_bla_add_claim(struct batadv_priv 
*bat_priv,
 
        /* create a new claim entry if it does not exist yet. */
        if (!claim) {
-               claim = kzalloc_obj(*claim, GFP_ATOMIC);
-               if (!claim)
+               bla_claim_max_learned = 
READ_ONCE(bat_priv->bla.claim_max_learned);
+               bla_claim_learned = 
atomic_inc_return(&bat_priv->bla.claim_learned);
+               if (bla_claim_max_learned && bla_claim_learned > 
bla_claim_max_learned) {
+                       atomic_dec(&bat_priv->bla.claim_learned);
                        return;
+               }
+
+               claim = kzalloc_obj(*claim, GFP_ATOMIC);
+               if (!claim) {
+                       atomic_dec(&bat_priv->bla.claim_learned);
+                       return;
+               }
 
                ether_addr_copy(claim->addr, mac);
                spin_lock_init(&claim->backbone_lock);
@@ -747,8 +761,9 @@ static void batadv_bla_add_claim(struct batadv_priv 
*bat_priv,
                                             &claim->hash_entry);
 
                if (unlikely(hash_added != 0)) {
-                       /* only local changes happened. */
                        batadv_backbone_gw_put(backbone_gw);
+                       atomic_dec(&bat_priv->bla.claim_learned);
+                       /* only local changes happened. */
                        kfree(claim);
                        return;
                }
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index 0e313ac8..4d83be55 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -849,6 +849,9 @@ static int batadv_meshif_init_late(struct net_device *dev)
 
        atomic_set(&bat_priv->bla.backbone_learned, 0);
        WRITE_ONCE(bat_priv->bla.backbone_max_learned, 0);
+
+       atomic_set(&bat_priv->bla.claim_learned, 0);
+       WRITE_ONCE(bat_priv->bla.claim_max_learned, 0);
 #endif
        atomic_set(&bat_priv->tp_num, 0);
 
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index f49d7a5e..f2c7fd8d 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -158,6 +158,7 @@ static const struct nla_policy 
batadv_netlink_policy[NUM_BATADV_ATTR] = {
        [BATADV_ATTR_ORIG_MAX_LEARNED]          = { .type = NLA_U32 },
        [BATADV_ATTR_DAT_MAX_LEARNED]           = { .type = NLA_U32 },
        [BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED]  = { .type = NLA_U32 },
+       [BATADV_ATTR_BLA_CLAIM_MAX_LEARNED]     = { .type = NLA_U32 },
 };
 
 /**
@@ -305,6 +306,10 @@ static int batadv_netlink_mesh_fill(struct sk_buff *msg,
        if (nla_put_u32(msg, BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED,
                        READ_ONCE(bat_priv->bla.backbone_max_learned)))
                goto nla_put_failure;
+
+       if (nla_put_u32(msg, BATADV_ATTR_BLA_CLAIM_MAX_LEARNED,
+                       READ_ONCE(bat_priv->bla.claim_max_learned)))
+               goto nla_put_failure;
 #endif /* CONFIG_BATMAN_ADV_BLA */
 
 #ifdef CONFIG_BATMAN_ADV_DAT
@@ -500,6 +505,11 @@ static int batadv_netlink_set_mesh(struct sk_buff *skb, 
struct genl_info *info)
                WRITE_ONCE(bat_priv->bla.backbone_max_learned,
                           
nla_get_u32(info->attrs[BATADV_ATTR_BLA_BACKBONE_MAX_LEARNED]));
        }
+
+       if (info->attrs[BATADV_ATTR_BLA_CLAIM_MAX_LEARNED]) {
+               WRITE_ONCE(bat_priv->bla.claim_max_learned,
+                          
nla_get_u32(info->attrs[BATADV_ATTR_BLA_CLAIM_MAX_LEARNED]));
+       }
 #endif /* CONFIG_BATMAN_ADV_BLA */
 
 #ifdef CONFIG_BATMAN_ADV_DAT
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 22d47509..df2d5ccf 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1077,6 +1077,12 @@ struct batadv_priv_bla {
        /** @backbone_learned: current number of learned backbone_gw entries */
        atomic_t backbone_learned;
 
+       /** @claim_max_learned: Maximum number of claims */
+       u32 claim_max_learned;
+
+       /** @claim_learned: current number of learned claim entries */
+       atomic_t claim_learned;
+
        /**
         * @num_requests_lock: locks update num_requests +
         * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update

-- 
2.47.3

Reply via email to