git: 368a1bfcd493 - main - dhclient: Avoid out-of-bounds reads in check_classless_option()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 15 Oct 2026 15:24:20 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=368a1bfcd49315e8dd3e56ca6744ce5d74ef737b
commit 368a1bfcd49315e8dd3e56ca6744ce5d74ef737b
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-10-14 21:12:42 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-15 14:50:33 +0000
dhclient: Avoid out-of-bounds reads in check_classless_option()
The loop did not verify that the option buffer is large enough before
reading an address.
Reported by: Andrew Griffiths <andrew@calif.io>
MFC after: 2 weeks
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D60484
---
sbin/dhclient/dhclient.c | 64 ++++++++++++++++++++++++++++--------------------
1 file changed, 38 insertions(+), 26 deletions(-)
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index bef956645b85..8fccac182c16 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -139,7 +139,7 @@ struct sockaddr *get_ifa(char *, int);
void routehandler(struct protocol *);
void usage(void);
int check_option(struct client_lease *l, int option);
-int check_classless_option(unsigned char *data, int len);
+int check_classless_option(unsigned char *data, size_t len);
int ipv4addrs(const char * buf);
int res_hnok(const char *dn);
int check_search(const char *srch);
@@ -2846,43 +2846,56 @@ check_option(struct client_lease *l, int option)
/* RFC 3442 The Classless Static Routes option checks */
int
-check_classless_option(unsigned char *data, int len)
+check_classless_option(unsigned char *data, size_t len)
{
- int i = 0;
- unsigned char width;
+ unsigned int i;
in_addr_t addr, mask;
+ unsigned char width;
if (len < 5) {
- warning("Too small length: %d", len);
+ warning("Too small length: %zu", len);
return (0);
}
- while(i < len) {
+ for (i = 0; i < len; i += 4) {
+ unsigned int octets;
+
width = data[i++];
- if (width == 0) {
- i += 4;
+ octets = (width + 7) / 8;
+ if (len < i + octets) {
+ warning("Incorrect data length: %zu", len);
+ return (0);
+ }
+
+ switch (octets) {
+ case 0:
continue;
- } else if (width < 9) {
- addr = (in_addr_t)(data[i] << 24);
+ case 1:
+ addr = ((in_addr_t)data[i] << 24);
i += 1;
- } else if (width < 17) {
- addr = (in_addr_t)(data[i] << 24) +
- (in_addr_t)(data[i + 1] << 16);
+ break;
+ case 2:
+ addr = ((in_addr_t)data[i] << 24) +
+ ((in_addr_t)data[i + 1] << 16);
i += 2;
- } else if (width < 25) {
- addr = (in_addr_t)(data[i] << 24) +
- (in_addr_t)(data[i + 1] << 16) +
- (in_addr_t)(data[i + 2] << 8);
+ break;
+ case 3:
+ addr = ((in_addr_t)data[i] << 24) +
+ ((in_addr_t)data[i + 1] << 16) +
+ ((in_addr_t)data[i + 2] << 8);
i += 3;
- } else if (width < 33) {
- addr = (in_addr_t)(data[i] << 24) +
- (in_addr_t)(data[i + 1] << 16) +
- (in_addr_t)(data[i + 2] << 8) +
- data[i + 3];
+ break;
+ case 4:
+ addr = ((in_addr_t)data[i] << 24) +
+ ((in_addr_t)data[i + 1] << 16) +
+ ((in_addr_t)data[i + 2] << 8) +
+ data[i + 3];
i += 4;
- } else {
- warning("Incorrect subnet width: %d", width);
+ break;
+ default:
+ warning("Incorrect subnet width: %u", width);
return (0);
}
+
mask = (in_addr_t)(~0) << (32 - width);
addr = ntohl(addr);
mask = ntohl(mask);
@@ -2899,10 +2912,9 @@ check_classless_option(unsigned char *data, int len)
data[i - 1] = (unsigned char)(
(addr >> (((32 - width)/8)*8)) & 0xFF);
}
- i += 4;
}
if (i > len) {
- warning("Incorrect data length: %d (must be %d)", len, i);
+ warning("Incorrect data length: %zu (must be %u)", len, i);
return (0);
}
return (1);