mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-07-25 16:19:48 +02:00
batman-adv: tvlv: avoid race of cifsnotfound handler state
commitedb557b2baupstream. 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") [ Context ] Signed-off-by: Sven Eckelmann <sven@narfation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
bc86a13f4f
commit
bb7ee1ccca
+57
-6
@@ -394,7 +394,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;
|
||||
} else {
|
||||
if (!src)
|
||||
return NET_RX_SUCCESS;
|
||||
@@ -413,6 +412,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
|
||||
@@ -433,7 +474,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
|
||||
u8 *src, u8 *dst,
|
||||
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;
|
||||
@@ -473,12 +516,20 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
|
||||
rcu_read_lock();
|
||||
hlist_for_each_entry_rcu(tvlv_handler,
|
||||
&bat_priv->tvlv.handler_list, list) {
|
||||
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();
|
||||
|
||||
|
||||
@@ -2408,13 +2408,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_ */
|
||||
|
||||
Reference in New Issue
Block a user