Hi Shahriyar,

On 13/08/2026 18:28, Shahriyar Jalayeri wrote:
> 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.

Similar to the wget case, this relies on a later DNS callback to reclaim
the heap context, but that callback is not guaranteed to run.

Here too I suggest cancelling the queued DNS callback before returning
from dns, while the stack object is still valid. With a small DNS
cancellation helper, dns_cb_arg can stay on the stack and no heap object
has to be left for a later callback to free.

Thanks,
-- 
Jerome

> 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;
>  }
>  
> 

Reply via email to