Invalid speed settings by the user are currently acknowledged as correct but not stored. Instead the return of the store operation of the file "gw_bandwidth" should indicate that the given value is not acceptable.
Signed-off-by: Sven Eckelmann sven@narfation.org --- net/batman-adv/gateway_common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 39cf44c..f907a20 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -157,7 +157,7 @@ ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff,
ret = batadv_parse_gw_bandwidth(net_dev, buff, &down_new, &up_new); if (!ret) - goto end; + return -EINVAL;
if (!down_new) down_new = 1; @@ -181,7 +181,6 @@ ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff, atomic_set(&bat_priv->gw.bandwidth_up, up_new); batadv_gw_tvlv_container_update(bat_priv);
-end: return count; }
Signed-off-by: Sven Eckelmann sven@narfation.org --- compat-include/linux/kernel.h | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/compat-include/linux/kernel.h b/compat-include/linux/kernel.h index 6eee020..e401fe2 100644 --- a/compat-include/linux/kernel.h +++ b/compat-include/linux/kernel.h @@ -36,6 +36,18 @@ _r = -ERANGE;\ _r;\ }) + +#define kstrtou64(cp, base, v)\ +({\ + unsigned long long _v;\ + int _r;\ + _r = strict_strtoull(cp, base, &_v);\ + *(v) = (uint64_t)_v;\ + if ((unsigned long long)*(v) != _v)\ + _r = -ERANGE;\ + _r;\ +}) + #define kstrtoul strict_strtoul #define kstrtol strict_strtol
Signed-off-by: Sven Eckelmann sven@narfation.org --- compat-include/linux/kernel.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
diff --git a/compat-include/linux/kernel.h b/compat-include/linux/kernel.h index e401fe2..94232ac 100644 --- a/compat-include/linux/kernel.h +++ b/compat-include/linux/kernel.h @@ -53,4 +53,21 @@
#endif /* < KERNEL_VERSION(2, 6, 39) */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0) + +#define U8_MAX ((u8)~0U) +#define S8_MAX ((s8)(U8_MAX >> 1)) +#define S8_MIN ((s8)(-S8_MAX - 1)) +#define U16_MAX ((u16)~0U) +#define S16_MAX ((s16)(U16_MAX >> 1)) +#define S16_MIN ((s16)(-S16_MAX - 1)) +#define U32_MAX ((u32)~0U) +#define S32_MAX ((s32)(U32_MAX >> 1)) +#define S32_MIN ((s32)(-S32_MAX - 1)) +#define U64_MAX ((u64)~0ULL) +#define S64_MAX ((s64)(U64_MAX >> 1)) +#define S64_MIN ((s64)(-S64_MAX - 1)) + +#endif /* < KERNEL_VERSION(3, 14, 0) */ + #endif /* _NET_BATMAN_ADV_COMPAT_LINUX_KERNEL_H_ */
The TVLV for the gw_bandwidth stores everything as u32. But the gw_bandwidth reads the signed long which limits the maximum value to (2 ** 31 - 1) on systems with 4 byte long. Also the input value is always converted from either Mibit/s or Kibit/s to 100Kibit/s. This reduces the values even further when the user sets it via the default unit Kibit/s. It may even cause an integer overflow and end up with a value the user never intended.
Instead read the values as u64, check for possible overflows, do the unit adjustments and then reduce the size to u32.
Signed-off-by: Sven Eckelmann sven@narfation.org --- net/batman-adv/gateway_common.c | 49 +++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-)
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index f907a20..7430b12 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -18,6 +18,7 @@ #include "gateway_common.h" #include "main.h"
+#include <asm/div64.h> #include <linux/atomic.h> #include <linux/byteorder/generic.h> #include <linux/kernel.h> @@ -43,7 +44,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, { enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT; char *slash_ptr, *tmp_ptr; - long ldown, lup; + u64 ldown, lup; int ret;
slash_ptr = strchr(buff, '/'); @@ -61,7 +62,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, *tmp_ptr = '\0'; }
- ret = kstrtol(buff, 10, &ldown); + ret = kstrtou64(buff, 10, &ldown); if (ret) { batadv_err(net_dev, "Download speed of gateway mode invalid: %s\n", @@ -71,14 +72,31 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
switch (bw_unit_type) { case BATADV_BW_UNIT_MBIT: - *down = ldown * 10; + /* prevent overflow */ + if (U64_MAX / 10 < ldown) { + batadv_err(net_dev, + "Download speed of gateway mode too large: %s\n", + buff); + return false; + } + + ldown *= 10; break; case BATADV_BW_UNIT_KBIT: default: - *down = ldown / 100; + do_div(ldown, 100); break; }
+ if (U32_MAX < ldown) { + batadv_err(net_dev, + "Download speed of gateway mode too large: %s\n", + buff); + return false; + } + + *down = ldown; + /* we also got some upload info */ if (slash_ptr) { bw_unit_type = BATADV_BW_UNIT_KBIT; @@ -94,7 +112,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, *tmp_ptr = '\0'; }
- ret = kstrtol(slash_ptr + 1, 10, &lup); + ret = kstrtou64(slash_ptr + 1, 10, &lup); if (ret) { batadv_err(net_dev, "Upload speed of gateway mode invalid: %s\n", @@ -104,13 +122,30 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
switch (bw_unit_type) { case BATADV_BW_UNIT_MBIT: - *up = lup * 10; + /* prevent overflow */ + if (U64_MAX / 10 < lup) { + batadv_err(net_dev, + "Upload speed of gateway mode too large: %s\n", + slash_ptr + 1); + return false; + } + + lup *= 10; break; case BATADV_BW_UNIT_KBIT: default: - *up = lup / 100; + do_div(lup, 100); break; } + + if (U32_MAX < lup) { + batadv_err(net_dev, + "Upload speed of gateway mode too large: %s\n", + slash_ptr + 1); + return false; + } + + *up = lup; }
return true;
b.a.t.m.a.n@lists.open-mesh.org