diff mbox series

[net-next,v3,08/10] net: pktgen: fix access outside of user given buffer in pktgen_if_write()

Message ID 20250203170201.1661703-9-ps.report@gmx.net (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series Some pktgen fixes/improvments | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/build_clang success Errors and warnings before: 1 this patch: 1
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: line length of 92 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2025-02-04--00-00 (tests: 886)

Commit Message

Peter Seiderer Feb. 3, 2025, 5:01 p.m. UTC
Honour the user given buffer size for the hex32_arg(), num_arg(),
strn_len(), get_imix_entries() and get_labels() calls (otherwise they will
access memory outside of the user given buffer).

In hex32_arg(), num_arg(), strn_len() error out in case no characters are
available (maxlen = 0), in num_arg() additional error out in case no valid
character is parsed.

In get_labels() additional enable parsing labels up to MAX_IMIX_ENTRIES
instead of (MAX_IMIX_ENTRIES - 1).

Additional remove some superfluous variable initializing and align some
variable declarations to the most common pattern.

Signed-off-by: Peter Seiderer <ps.report@gmx.net>
---
Changes v2 -> v3:
  - no changes

Changes v1 -> v2:
  - additional fix get_imix_entries() and get_labels()
---
 net/core/pktgen.c | 211 ++++++++++++++++++++++++++++++----------------
 1 file changed, 139 insertions(+), 72 deletions(-)

Comments

Paolo Abeni Feb. 4, 2025, 10:40 a.m. UTC | #1
Hi,

On 2/3/25 6:01 PM, Peter Seiderer wrote:
> @@ -806,6 +812,9 @@ static long num_arg(const char __user *user_buffer, unsigned long maxlen,
>  		if ((c >= '0') && (c <= '9')) {
>  			*num *= 10;
>  			*num += c - '0';
> +		} else if (i == 0) {
> +			// no valid character parsed, error out

Minor nit: please don't use C99 comments, even for single line one.

> +			return -EINVAL;
>  		} else
>  			break;
>  	}
> @@ -816,6 +825,9 @@ static int strn_len(const char __user * user_buffer, unsigned int maxlen)
>  {
>  	int i;
>  
> +	if (!maxlen)
> +		return -EINVAL;

It looks like this check is not needed? strn_len() will return 0 and the
caller will read 0 bytes from the user_buffer.

> @@ -882,39 +897,45 @@ static ssize_t get_imix_entries(const char __user *buffer,
>  		pkt_dev->imix_entries[pkt_dev->n_imix_entries].weight = weight;
>  
>  		i += len;
> +		pkt_dev->n_imix_entries++;
> +
> +		if (i >= maxlen)
> +			break;
>  		if (get_user(c, &buffer[i]))
>  			return -EFAULT;
> -
>  		i++;
> -		pkt_dev->n_imix_entries++;
>  	} while (c == ' ');
>  
>  	return i;
>  }
>  
> -static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
> +static ssize_t get_labels(const char __user *buffer, int maxlen, struct pktgen_dev *pkt_dev)
>  {
>  	unsigned int n = 0;
>  	char c;
> -	ssize_t i = 0;
> -	int len;
> +	int i = 0, max, len;

Minor nit: since you are touching the variables declaration, please fix
them to respect the reverse christmas tree order.

This patch is quite large and mixes several things. I'll split out at
least the strn_len() caller fixes (possibly even the num_arg() and
hex32_arg() ones) and the refactoring in pktgen_if_write().

/P
Simon Horman Feb. 4, 2025, 10:52 a.m. UTC | #2
On Mon, Feb 03, 2025 at 06:01:59PM +0100, Peter Seiderer wrote:
> Honour the user given buffer size for the hex32_arg(), num_arg(),
> strn_len(), get_imix_entries() and get_labels() calls (otherwise they will
> access memory outside of the user given buffer).
> 
> In hex32_arg(), num_arg(), strn_len() error out in case no characters are
> available (maxlen = 0), in num_arg() additional error out in case no valid
> character is parsed.
> 
> In get_labels() additional enable parsing labels up to MAX_IMIX_ENTRIES
> instead of (MAX_IMIX_ENTRIES - 1).
> 
> Additional remove some superfluous variable initializing and align some
> variable declarations to the most common pattern.
> 
> Signed-off-by: Peter Seiderer <ps.report@gmx.net>

...

> diff --git a/net/core/pktgen.c b/net/core/pktgen.c

...

> @@ -872,7 +886,8 @@ static ssize_t get_imix_entries(const char __user *buffer,
>  		if (size < 14 + 20 + 8)
>  			size = 14 + 20 + 8;
>  
> -		len = num_arg(&buffer[i], max_digits, &weight);
> +		max = min(10, maxlen - i);

Hi Peter,

10 is used as a magic value here. I think it would be best if
it were a #define so it has a name. Likewise for other constants
used as arguments to min() in this patch.

...
Peter Seiderer Feb. 5, 2025, 1:09 p.m. UTC | #3
Hello Paolo,

On Tue, 4 Feb 2025 11:40:21 +0100, Paolo Abeni <pabeni@redhat.com> wrote:

> Hi,
>
> On 2/3/25 6:01 PM, Peter Seiderer wrote:
> > @@ -806,6 +812,9 @@ static long num_arg(const char __user *user_buffer, unsigned long maxlen,
> >  		if ((c >= '0') && (c <= '9')) {
> >  			*num *= 10;
> >  			*num += c - '0';
> > +		} else if (i == 0) {
> > +			// no valid character parsed, error out
>
> Minor nit: please don't use C99 comments, even for single line one.

Fixed...

>
> > +			return -EINVAL;
> >  		} else
> >  			break;
> >  	}
> > @@ -816,6 +825,9 @@ static int strn_len(const char __user * user_buffer, unsigned int maxlen)
> >  {
> >  	int i;
> >
> > +	if (!maxlen)
> > +		return -EINVAL;
>
> It looks like this check is not needed? strn_len() will return 0 and the
> caller will read 0 bytes from the user_buffer.

Checked all call sites, your are right, fixed in next patch iteration...

>
> > @@ -882,39 +897,45 @@ static ssize_t get_imix_entries(const char __user *buffer,
> >  		pkt_dev->imix_entries[pkt_dev->n_imix_entries].weight = weight;
> >
> >  		i += len;
> > +		pkt_dev->n_imix_entries++;
> > +
> > +		if (i >= maxlen)
> > +			break;
> >  		if (get_user(c, &buffer[i]))
> >  			return -EFAULT;
> > -
> >  		i++;
> > -		pkt_dev->n_imix_entries++;
> >  	} while (c == ' ');
> >
> >  	return i;
> >  }
> >
> > -static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
> > +static ssize_t get_labels(const char __user *buffer, int maxlen, struct pktgen_dev *pkt_dev)
> >  {
> >  	unsigned int n = 0;
> >  	char c;
> > -	ssize_t i = 0;
> > -	int len;
> > +	int i = 0, max, len;
>
> Minor nit: since you are touching the variables declaration, please fix
> them to respect the reverse christmas tree order.

Fixed...

>
> This patch is quite large and mixes several things. I'll split out at
> least the strn_len() caller fixes (possibly even the num_arg() and
> hex32_arg() ones) and the refactoring in pktgen_if_write().

Done in the next patch iteration....

Thanks for review!

Regards,
Peter

>
> /P
>
Peter Seiderer Feb. 5, 2025, 1:10 p.m. UTC | #4
Hello Simon,

On Tue, 4 Feb 2025 10:52:51 +0000, Simon Horman <horms@kernel.org> wrote:

> On Mon, Feb 03, 2025 at 06:01:59PM +0100, Peter Seiderer wrote:
> > Honour the user given buffer size for the hex32_arg(), num_arg(),
> > strn_len(), get_imix_entries() and get_labels() calls (otherwise they will
> > access memory outside of the user given buffer).
> >
> > In hex32_arg(), num_arg(), strn_len() error out in case no characters are
> > available (maxlen = 0), in num_arg() additional error out in case no valid
> > character is parsed.
> >
> > In get_labels() additional enable parsing labels up to MAX_IMIX_ENTRIES
> > instead of (MAX_IMIX_ENTRIES - 1).
> >
> > Additional remove some superfluous variable initializing and align some
> > variable declarations to the most common pattern.
> >
> > Signed-off-by: Peter Seiderer <ps.report@gmx.net>
>
> ...
>
> > diff --git a/net/core/pktgen.c b/net/core/pktgen.c
>
> ...
>
> > @@ -872,7 +886,8 @@ static ssize_t get_imix_entries(const char __user *buffer,
> >  		if (size < 14 + 20 + 8)
> >  			size = 14 + 20 + 8;
> >
> > -		len = num_arg(&buffer[i], max_digits, &weight);
> > +		max = min(10, maxlen - i);
>
> Hi Peter,
>
> 10 is used as a magic value here. I think it would be best if
> it were a #define so it has a name. Likewise for other constants
> used as arguments to min() in this patch.
>
> ...

O.k, done in the next patch iteration...

Thanks for review!

Regards,
Peter
diff mbox series

Patch

diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 55064713223e..6675375c052c 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -752,6 +752,9 @@  static int hex32_arg(const char __user *user_buffer, unsigned long maxlen,
 	int i = 0;
 	*num = 0;
 
+	if (!maxlen)
+		return -EINVAL;
+
 	for (; i < maxlen; i++) {
 		int value;
 		char c;
@@ -799,6 +802,9 @@  static long num_arg(const char __user *user_buffer, unsigned long maxlen,
 	int i;
 	*num = 0;
 
+	if (!maxlen)
+		return -EINVAL;
+
 	for (i = 0; i < maxlen; i++) {
 		char c;
 		if (get_user(c, &user_buffer[i]))
@@ -806,6 +812,9 @@  static long num_arg(const char __user *user_buffer, unsigned long maxlen,
 		if ((c >= '0') && (c <= '9')) {
 			*num *= 10;
 			*num += c - '0';
+		} else if (i == 0) {
+			// no valid character parsed, error out
+			return -EINVAL;
 		} else
 			break;
 	}
@@ -816,6 +825,9 @@  static int strn_len(const char __user * user_buffer, unsigned int maxlen)
 {
 	int i;
 
+	if (!maxlen)
+		return -EINVAL;
+
 	for (i = 0; i < maxlen; i++) {
 		char c;
 		if (get_user(c, &user_buffer[i]))
@@ -842,11 +854,10 @@  static int strn_len(const char __user * user_buffer, unsigned int maxlen)
  * "size1,weight_1 size2,weight_2 ... size_n,weight_n" for example.
  */
 static ssize_t get_imix_entries(const char __user *buffer,
+				unsigned int maxlen,
 				struct pktgen_dev *pkt_dev)
 {
-	const int max_digits = 10;
-	int i = 0;
-	long len;
+	int i = 0, max, len;
 	char c;
 
 	pkt_dev->n_imix_entries = 0;
@@ -858,10 +869,13 @@  static ssize_t get_imix_entries(const char __user *buffer,
 		if (pkt_dev->n_imix_entries >= MAX_IMIX_ENTRIES)
 			return -E2BIG;
 
-		len = num_arg(&buffer[i], max_digits, &size);
+		max = min(10, maxlen - i);
+		len = num_arg(&buffer[i], max, &size);
 		if (len < 0)
 			return len;
 		i += len;
+		if (i >= maxlen)
+			return -EINVAL;
 		if (get_user(c, &buffer[i]))
 			return -EFAULT;
 		/* Check for comma between size_i and weight_i */
@@ -872,7 +886,8 @@  static ssize_t get_imix_entries(const char __user *buffer,
 		if (size < 14 + 20 + 8)
 			size = 14 + 20 + 8;
 
-		len = num_arg(&buffer[i], max_digits, &weight);
+		max = min(10, maxlen - i);
+		len = num_arg(&buffer[i], max, &weight);
 		if (len < 0)
 			return len;
 		if (weight <= 0)
@@ -882,39 +897,45 @@  static ssize_t get_imix_entries(const char __user *buffer,
 		pkt_dev->imix_entries[pkt_dev->n_imix_entries].weight = weight;
 
 		i += len;
+		pkt_dev->n_imix_entries++;
+
+		if (i >= maxlen)
+			break;
 		if (get_user(c, &buffer[i]))
 			return -EFAULT;
-
 		i++;
-		pkt_dev->n_imix_entries++;
 	} while (c == ' ');
 
 	return i;
 }
 
-static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
+static ssize_t get_labels(const char __user *buffer, int maxlen, struct pktgen_dev *pkt_dev)
 {
 	unsigned int n = 0;
 	char c;
-	ssize_t i = 0;
-	int len;
+	int i = 0, max, len;
 
 	pkt_dev->nr_labels = 0;
 	do {
 		__u32 tmp;
-		len = hex32_arg(&buffer[i], 8, &tmp);
+
+		if (n >= MAX_MPLS_LABELS)
+			return -E2BIG;
+
+		max = min(8, maxlen - i);
+		len = hex32_arg(&buffer[i], max, &tmp);
 		if (len <= 0)
 			return len;
 		pkt_dev->labels[n] = htonl(tmp);
 		if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
 			pkt_dev->flags |= F_MPLS_RND;
 		i += len;
+		n++;
+		if (i >= maxlen)
+			break;
 		if (get_user(c, &buffer[i]))
 			return -EFAULT;
 		i++;
-		n++;
-		if (n >= MAX_MPLS_LABELS)
-			return -E2BIG;
 	} while (c == ',');
 
 	pkt_dev->nr_labels = n;
@@ -960,7 +981,6 @@  static ssize_t pktgen_if_write(struct file *file,
 	char name[16], valstr[32];
 	unsigned long value = 0;
 	char *pg_result = NULL;
-	int tmp = 0;
 	char buf[128];
 
 	pg_result = &(pkt_dev->result[0]);
@@ -970,17 +990,16 @@  static ssize_t pktgen_if_write(struct file *file,
 		return -EINVAL;
 	}
 
-	max = count;
-	tmp = count_trail_chars(user_buffer, max);
-	if (tmp < 0) {
+	len = count_trail_chars(user_buffer, count);
+	if (len < 0) {
 		pr_warn("illegal format\n");
-		return tmp;
+		return len;
 	}
-	i = tmp;
+	i = len;
 
 	/* Read variable name */
-
-	len = strn_len(&user_buffer[i], sizeof(name) - 1);
+	max = min(sizeof(name) - 1, count - i);
+	len = strn_len(&user_buffer[i], max);
 	if (len < 0)
 		return len;
 
@@ -1008,7 +1027,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "min_pkt_size")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1025,7 +1045,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "max_pkt_size")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1044,7 +1065,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	/* Shortcut for min = max */
 
 	if (!strcmp(name, "pkt_size")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1064,7 +1086,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		if (pkt_dev->clone_skb > 0)
 			return -EINVAL;
 
-		len = get_imix_entries(&user_buffer[i], pkt_dev);
+		max = count - i;
+		len = get_imix_entries(&user_buffer[i], max, pkt_dev);
 		if (len < 0)
 			return len;
 
@@ -1075,7 +1098,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "debug")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1086,7 +1110,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "frags")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1096,7 +1121,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "delay")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1111,7 +1137,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "rate")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1126,7 +1153,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "ratep")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1141,7 +1169,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "udp_src_min")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1154,7 +1183,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "udp_dst_min")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1167,7 +1197,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "udp_src_max")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1180,7 +1211,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "udp_dst_max")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1193,7 +1225,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "clone_skb")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 		/* clone_skb is not supported for netif_receive xmit_mode and
@@ -1214,7 +1247,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "count")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1225,7 +1259,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "src_mac_count")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1239,7 +1274,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "dst_mac_count")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1253,7 +1289,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "burst")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1272,7 +1309,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "node")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1293,11 +1331,12 @@  static ssize_t pktgen_if_write(struct file *file,
 	if (!strcmp(name, "xmit_mode")) {
 		char f[32];
 
-		memset(f, 0, 32);
-		len = strn_len(&user_buffer[i], sizeof(f) - 1);
+		max = min(sizeof(f) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
+		memset(f, 0, sizeof(f));
 		if (copy_from_user(f, &user_buffer[i], len))
 			return -EFAULT;
 		i += len;
@@ -1333,11 +1372,12 @@  static ssize_t pktgen_if_write(struct file *file,
 		char f[32];
 		char *end;
 
-		memset(f, 0, 32);
-		len = strn_len(&user_buffer[i], sizeof(f) - 1);
+		max = min(sizeof(f) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
+		memset(f, 0, 32);
 		if (copy_from_user(f, &user_buffer[i], len))
 			return -EFAULT;
 		i += len;
@@ -1382,7 +1422,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
-		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
+		max = min(sizeof(pkt_dev->dst_min) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1402,7 +1443,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "dst_max")) {
-		len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
+		max = min(sizeof(pkt_dev->dst_max) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1422,7 +1464,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "dst6")) {
-		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
+		max = min(sizeof(buf) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1445,7 +1488,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "dst6_min")) {
-		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
+		max = min(sizeof(buf) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1467,7 +1511,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "dst6_max")) {
-		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
+		max = min(sizeof(buf) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1488,7 +1533,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "src6")) {
-		len = strn_len(&user_buffer[i], sizeof(buf) - 1);
+		max = min(sizeof(buf) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1511,7 +1557,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "src_min")) {
-		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
+		max = min(sizeof(pkt_dev->src_min) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1531,7 +1578,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "src_max")) {
-		len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
+		max = min(sizeof(pkt_dev->src_max) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1551,7 +1599,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "dst_mac")) {
-		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
+		max = min(sizeof(valstr) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1568,7 +1617,8 @@  static ssize_t pktgen_if_write(struct file *file,
 		return count;
 	}
 	if (!strcmp(name, "src_mac")) {
-		len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
+		max = min(sizeof(valstr) - 1, count - i);
+		len = strn_len(&user_buffer[i], max);
 		if (len < 0)
 			return len;
 
@@ -1592,7 +1642,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "flows")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1606,7 +1657,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 #ifdef CONFIG_XFRM
 	if (!strcmp(name, "spi")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1617,7 +1669,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 #endif
 	if (!strcmp(name, "flowlen")) {
-		len = num_arg(&user_buffer[i], 10, &value);
+		max = min(10, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1628,7 +1681,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "queue_map_min")) {
-		len = num_arg(&user_buffer[i], 5, &value);
+		max = min(5, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1639,7 +1693,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "queue_map_max")) {
-		len = num_arg(&user_buffer[i], 5, &value);
+		max = min(5, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1652,7 +1707,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	if (!strcmp(name, "mpls")) {
 		unsigned int n, cnt;
 
-		len = get_labels(&user_buffer[i], pkt_dev);
+		max = count - i;
+		len = get_labels(&user_buffer[i], max, pkt_dev);
 		if (len < 0)
 			return len;
 		i += len;
@@ -1673,7 +1729,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "vlan_id")) {
-		len = num_arg(&user_buffer[i], 4, &value);
+		max = min(4, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1700,7 +1757,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "vlan_p")) {
-		len = num_arg(&user_buffer[i], 1, &value);
+		max = min(1, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1715,7 +1773,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "vlan_cfi")) {
-		len = num_arg(&user_buffer[i], 1, &value);
+		max = min(1, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1730,7 +1789,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "svlan_id")) {
-		len = num_arg(&user_buffer[i], 4, &value);
+		max = min(4, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1757,7 +1817,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "svlan_p")) {
-		len = num_arg(&user_buffer[i], 1, &value);
+		max = min(1, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1772,7 +1833,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "svlan_cfi")) {
-		len = num_arg(&user_buffer[i], 1, &value);
+		max = min(1, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;
 
@@ -1787,8 +1849,10 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "tos")) {
-		__u32 tmp_value = 0;
-		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
+		__u32 tmp_value;
+
+		max = min(2, count - i);
+		len = hex32_arg(&user_buffer[i], max, &tmp_value);
 		if (len < 0)
 			return len;
 
@@ -1803,8 +1867,10 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "traffic_class")) {
-		__u32 tmp_value = 0;
-		len = hex32_arg(&user_buffer[i], 2, &tmp_value);
+		__u32 tmp_value;
+
+		max = min(2, count - i);
+		len = hex32_arg(&user_buffer[i], max, &tmp_value);
 		if (len < 0)
 			return len;
 
@@ -1819,7 +1885,8 @@  static ssize_t pktgen_if_write(struct file *file,
 	}
 
 	if (!strcmp(name, "skb_priority")) {
-		len = num_arg(&user_buffer[i], 9, &value);
+		max = min(9, count - i);
+		len = num_arg(&user_buffer[i], max, &value);
 		if (len < 0)
 			return len;