[PATCH v2 3/3] net: lwip: dns: free the callback context after an aborted lookup

Shahriyar Jalayeri <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <20260813-lwip-httpc-uaf-v2-3-1c3f0b3aa1eb__35336.9379577757$1786643987$gmane$org@byteray.co.uk>
do_dns() keeps its dns_cb_arg on the stack and registers its address as
the dns_gethostbyname() callback argument. lwIP has no way to cancel a
pending lookup, so if the command is interrupted or times out while the
name is still resolving, dns_cb() fires later and writes the result into
the stack frame that has since been reused.

Allocate dns_cb_arg on the heap and add an 'abandoned' flag. If the
lookup did not complete, hand ownership to dns_cb(), which frees the
context when it eventually fires; otherwise free it before returning.

Fixes: aedcfec9ed78 ("net: lwip: add dns command")
Signed-off-by: Shahriyar Jalayeri <[email protected]>
---
 net/lwip/dns.c | 38 +++++++++++++++++++++++++++++++-------
 1 file changed, 31 insertions(+), 7 deletions(-)

diff --git a/net/lwip/dns.c b/net/lwip/dns.c
index b620b0611d6..e93720963b8 100644
--- a/net/lwip/dns.c
+++ b/net/lwip/dns.c
@@ -4,6 +4,7 @@
 #include <command.h>
 #include <console.h>
 #include <env.h>
+#include <malloc.h>
 #include <lwip/dns.h>
 #include <lwip/timeouts.h>
 #include <net.h>
@@ -15,6 +16,7 @@
 struct dns_cb_arg {
 	ip_addr_t host_ipaddr;
 	bool done;
+	bool abandoned;
 };
 
 static void do_dns_tmr(void *arg)
@@ -26,6 +28,12 @@ static void dns_cb(const char *name, const ip_addr_t *ipaddr, void *arg)
 {
 	struct dns_cb_arg *dns_cb_arg = arg;
 
+	/* Late callback for an abandoned lookup: reclaim and stop. */
+	if (dns_cb_arg->abandoned) {
+		free(dns_cb_arg);
+		return;
+	}
+
 	dns_cb_arg->done = true;
 
 	if (!ipaddr)
@@ -36,7 +44,7 @@ static void dns_cb(const char *name, const ip_addr_t *ipaddr, void *arg)
 
 static int dns_loop(struct udevice *udev, const char *name, const char *var)
 {
-	struct dns_cb_arg dns_cb_arg = { };
+	struct dns_cb_arg *dns_cb_arg;
 	struct netif *netif;
 	const char *ipstr;
 	ip_addr_t ipaddr;
@@ -52,18 +60,22 @@ static int dns_loop(struct udevice *udev, const char *name, const char *var)
 		return CMD_RET_FAILURE;
 	}
 
-	dns_cb_arg.done = false;
+	dns_cb_arg = calloc(1, sizeof(*dns_cb_arg));
+	if (!dns_cb_arg) {
+		net_lwip_remove_netif(netif);
+		return CMD_RET_FAILURE;
+	}
 
-	ret = dns_gethostbyname(name, &ipaddr, dns_cb, &dns_cb_arg);
+	ret = dns_gethostbyname(name, &ipaddr, dns_cb, dns_cb_arg);
 
 	if (ret == ERR_OK) {
-		dns_cb(name, &ipaddr, &dns_cb_arg);
+		dns_cb(name, &ipaddr, dns_cb_arg);
 	} else if (ret == ERR_INPROGRESS) {
 		start = get_timer(0);
 		sys_timeout(DNS_RESEND_MS, do_dns_tmr, NULL);
 		do {
 			net_lwip_rx(udev, netif);
-			if (dns_cb_arg.done)
+			if (dns_cb_arg->done)
 				break;
 			if (ctrlc()) {
 				printf("\nAbort\n");
@@ -75,15 +87,27 @@ static int dns_loop(struct udevice *udev, const char *name, const char *var)
 
 	net_lwip_remove_netif(netif);
 
-	if (dns_cb_arg.done && !ip_addr_isany(&dns_cb_arg.host_ipaddr)) {
-		ipstr = ipaddr_ntoa(&dns_cb_arg.host_ipaddr);
+	/*
+	 * A started lookup that never completed may still be pending in lwIP
+	 * and reference dns_cb_arg. Hand ownership to dns_cb, which frees it
+	 * when it eventually fires, instead of freeing it here.
+	 */
+	if (ret == ERR_INPROGRESS && !dns_cb_arg->done) {
+		dns_cb_arg->abandoned = true;
+		return CMD_RET_FAILURE;
+	}
+
+	if (dns_cb_arg->done && !ip_addr_isany(&dns_cb_arg->host_ipaddr)) {
+		ipstr = ipaddr_ntoa(&dns_cb_arg->host_ipaddr);
 		if (var)
 			env_set(var, ipstr);
 		else
 			printf("%s\n", ipstr);
+		free(dns_cb_arg);
 		return CMD_RET_SUCCESS;
 	}
 
+	free(dns_cb_arg);
 	return CMD_RET_FAILURE;
 }
 

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.