Commit 0b8336f5 authored by Sven Eckelmann's avatar Sven Eckelmann Committed by Antonio Quartulli

batman-adv: Fix gw_bandwidth calculation on 32 bit systems

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: default avatarSven Eckelmann <sven@narfation.org>
Signed-off-by: default avatarMarek Lindner <mareklindner@neomailbox.ch>
Signed-off-by: default avatarAntonio Quartulli <antonio@meshcoding.com>
parent 77927b7d
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/byteorder/generic.h> #include <linux/byteorder/generic.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/string.h> #include <linux/string.h>
...@@ -44,7 +45,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, ...@@ -44,7 +45,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; enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT;
char *slash_ptr, *tmp_ptr; char *slash_ptr, *tmp_ptr;
long ldown, lup; u64 ldown, lup;
int ret; int ret;
slash_ptr = strchr(buff, '/'); slash_ptr = strchr(buff, '/');
...@@ -62,7 +63,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, ...@@ -62,7 +63,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
*tmp_ptr = '\0'; *tmp_ptr = '\0';
} }
ret = kstrtol(buff, 10, &ldown); ret = kstrtou64(buff, 10, &ldown);
if (ret) { if (ret) {
batadv_err(net_dev, batadv_err(net_dev,
"Download speed of gateway mode invalid: %s\n", "Download speed of gateway mode invalid: %s\n",
...@@ -72,14 +73,31 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, ...@@ -72,14 +73,31 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
switch (bw_unit_type) { switch (bw_unit_type) {
case BATADV_BW_UNIT_MBIT: 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; break;
case BATADV_BW_UNIT_KBIT: case BATADV_BW_UNIT_KBIT:
default: default:
*down = ldown / 100; ldown = div_u64(ldown, 100);
break; 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 */ /* we also got some upload info */
if (slash_ptr) { if (slash_ptr) {
bw_unit_type = BATADV_BW_UNIT_KBIT; bw_unit_type = BATADV_BW_UNIT_KBIT;
...@@ -95,7 +113,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, ...@@ -95,7 +113,7 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
*tmp_ptr = '\0'; *tmp_ptr = '\0';
} }
ret = kstrtol(slash_ptr + 1, 10, &lup); ret = kstrtou64(slash_ptr + 1, 10, &lup);
if (ret) { if (ret) {
batadv_err(net_dev, batadv_err(net_dev,
"Upload speed of gateway mode invalid: %s\n", "Upload speed of gateway mode invalid: %s\n",
...@@ -105,13 +123,30 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, ...@@ -105,13 +123,30 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
switch (bw_unit_type) { switch (bw_unit_type) {
case BATADV_BW_UNIT_MBIT: 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; break;
case BATADV_BW_UNIT_KBIT: case BATADV_BW_UNIT_KBIT:
default: default:
*up = lup / 100; lup = div_u64(lup, 100);
break; 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; return true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment