batman-adv: tvlv: avoid race of cifsnotfound handler state

TVLV handlers can have the flag BATADV_TVLV_HANDLER_OGM_CIFNOTFND set to
signal that the OGM handler should be called (with NULL for data) when the
specific TVLV container was not found in the OGM. This is used by:

* DAT
* GW
* Multicast (OGM + Tracker)

The state whether the handler was executed was stored in the struct
batadv_tvlv_handler. But the TVLV processing is started without any lock.
Multiple parallel contexts processing TVLVs would therefore overwrite each
others BATADV_TVLV_HANDLER_OGM_CALLED flag in the shared
batadv_tvlv_handler.

Drop the shared BATADV_TVLV_HANDLER_OGM_CALLED flag and instead determine,
per TVLV buffer, whether a matching container was present by scanning the
packet's buffer.

Cc: stable@kernel.org
Fixes: ef26157747 ("batman-adv: tvlv - basic infrastructure")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Sven Eckelmann
2026-06-14 11:22:43 +02:00
parent 32a6799255
commit edb557b2ba
2 changed files with 57 additions and 13 deletions

View File

@@ -411,7 +411,6 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
tvlv_handler->ogm_handler(bat_priv, orig_node,
BATADV_NO_FLAGS,
tvlv_value, tvlv_value_len);
tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED;
break;
case BATADV_UNICAST_TVLV:
if (!skb)
@@ -443,6 +442,48 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
return NET_RX_SUCCESS;
}
/**
* batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
* @type: tvlv container type to look for
* @version: tvlv container version to look for
*
* Return: true if a container of the given type and version is present in the
* tvlv buffer, false otherwise.
*/
static bool batadv_tvlv_containers_contain(void *tvlv_value,
u16 tvlv_value_len, u8 type,
u8 version)
{
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value = tvlv_hdr + 1;
tvlv_value_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_value_len)
break;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
break;
if (tvlv_hdr->type == type && tvlv_hdr->version == version)
return true;
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
return false;
}
/**
* batadv_tvlv_containers_process() - parse the given tvlv buffer to call the
* appropriate handlers
@@ -462,7 +503,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
struct sk_buff *skb, void *tvlv_value,
u16 tvlv_value_len)
{
u16 tvlv_value_start_len = tvlv_value_len;
struct batadv_tvlv_handler *tvlv_handler;
void *tvlv_value_start = tvlv_value;
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
@@ -506,12 +549,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
if (!tvlv_handler->ogm_handler)
continue;
if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) &&
!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED))
tvlv_handler->ogm_handler(bat_priv, orig_node,
cifnotfound, NULL, 0);
if (!(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND))
continue;
tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED;
/* if the corresponding container was present then the handler
* was already called from the loop above
*/
if (batadv_tvlv_containers_contain(tvlv_value_start,
tvlv_value_start_len,
tvlv_handler->type,
tvlv_handler->version))
continue;
tvlv_handler->ogm_handler(bat_priv, orig_node,
cifnotfound, NULL, 0);
}
rcu_read_unlock();

View File

@@ -2294,13 +2294,6 @@ enum batadv_tvlv_handler_flags {
* will call this handler even if its type was not found (with no data)
*/
BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1),
/**
* @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the
* API marks a handler as being called, so it won't be called if the
* BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set
*/
BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2),
};
#endif /* _NET_BATMAN_ADV_TYPES_H_ */