Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+42 −7
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/errno.h>
#include <linux/byteorder/generic.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/netdevice.h>
#include <linux/stddef.h>
#include <linux/string.h>
@@ -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;
	char *slash_ptr, *tmp_ptr;
	long ldown, lup;
	u64 ldown, lup;
	int ret;

	slash_ptr = strchr(buff, '/');
@@ -62,7 +63,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",
@@ -72,14 +73,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;
		ldown = div_u64(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;
@@ -95,7 +113,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",
@@ -105,13 +123,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;
			lup = div_u64(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;