git: b77d66032f47 - stable/13 - dummynet: Fix socket option length validation for IP_DUMMYNET3

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 06 Dec 2021 14:04:59 UTC
The branch stable/13 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=b77d66032f47838c5473d8eee3eea3202a080134

commit b77d66032f47838c5473d8eee3eea3202a080134
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2021-11-29 18:50:30 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2021-12-06 14:04:33 +0000

    dummynet: Fix socket option length validation for IP_DUMMYNET3
    
    The socket option handler tries to ensure that the option length is no
    larger than some reasonable maximum, and no smaller than sizeof(struct
    dn_id).  But the loaded option length is stored in an int, which is
    converted to an unsigned integer for the comparison with a size_t, so
    negative values are not caught and instead get passed to malloc().
    
    Change the code to use a size_t for the buffer size.
    
    Reviewed by:    kp
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 1c732c85911eb9e39071cbdb50dfb1f0d76de40f)
---
 sys/netpfil/ipfw/ip_dn_private.h |  2 +-
 sys/netpfil/ipfw/ip_dummynet.c   | 16 ++++++++--------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/sys/netpfil/ipfw/ip_dn_private.h b/sys/netpfil/ipfw/ip_dn_private.h
index cc084f2fcc0d..25015fe02c2d 100644
--- a/sys/netpfil/ipfw/ip_dn_private.h
+++ b/sys/netpfil/ipfw/ip_dn_private.h
@@ -437,7 +437,7 @@ int dn_compat_copy_queue(struct copy_args *a, void *_o);
 int dn_compat_copy_pipe(struct copy_args *a, void *_o);
 int copy_data_helper_compat(void *_o, void *_arg);
 int dn_compat_calc_size(void);
-int do_config(void *p, int l);
+int do_config(void *p, size_t l);
 
 /* function to drain idle object */
 void dn_drain_scheduler(void);
diff --git a/sys/netpfil/ipfw/ip_dummynet.c b/sys/netpfil/ipfw/ip_dummynet.c
index cee478a03274..93b361755d9c 100644
--- a/sys/netpfil/ipfw/ip_dummynet.c
+++ b/sys/netpfil/ipfw/ip_dummynet.c
@@ -1991,7 +1991,7 @@ dummynet_flush(void)
  *   processed on a config_sched.
  */
 int
-do_config(void *p, int l)
+do_config(void *p, size_t l)
 {
 	struct dn_id o;
 	union {
@@ -2015,7 +2015,7 @@ do_config(void *p, int l)
 	while (l >= sizeof(o)) {
 		memcpy(&o, (char *)p + off, sizeof(o));
 		if (o.len < sizeof(o) || l < o.len) {
-			D("bad len o.len %d len %d", o.len, l);
+			D("bad len o.len %d len %zu", o.len, l);
 			err = EINVAL;
 			break;
 		}
@@ -2487,7 +2487,8 @@ ip_dn_ctl(struct sockopt *sopt)
 {
 	struct epoch_tracker et;
 	void *p = NULL;
-	int error, l;
+	size_t l;
+	int error;
 
 	error = priv_check(sopt->sopt_td, PRIV_NETINET_DUMMYNET);
 	if (error)
@@ -2516,14 +2517,14 @@ ip_dn_ctl(struct sockopt *sopt)
 		error = ip_dummynet_compat(sopt);
 		break;
 
-	case IP_DUMMYNET3 :
+	case IP_DUMMYNET3:
 		if (sopt->sopt_dir == SOPT_GET) {
 			error = dummynet_get(sopt, NULL);
 			break;
 		}
 		l = sopt->sopt_valsize;
 		if (l < sizeof(struct dn_id) || l > 12000) {
-			D("argument len %d invalid", l);
+			D("argument len %zu invalid", l);
 			break;
 		}
 		p = malloc(l, M_TEMP, M_NOWAIT);
@@ -2532,9 +2533,8 @@ ip_dn_ctl(struct sockopt *sopt)
 			break;
 		}
 		error = sooptcopyin(sopt, p, l, l);
-		if (error)
-			break ;
-		error = do_config(p, l);
+		if (error == 0)
+			error = do_config(p, l);
 		break;
 	}