Hi Shahriar,

On 11/08/2026 20:48, Shahriyar Jalayeri wrote:
> wget_do_request() keeps the transfer context on the stack and passes its
> address to the lwIP httpc callbacks. If the user interrupts the transfer
> with Ctrl-C while the host name is still resolving, wget_handle_request()
> leaves its receive loop without tearing the connection down. The pending
> DNS lookup (which lwIP cannot cancel) later resolves, the connection is
> established, and httpc_recv_cb() runs against a stack frame that no longer
> exists. It writes attacker-controlled TCP data through store_block() to
> map_sysmem(ctx->daddr), with ctx read from reused stack.
> 
> Allocate the context on the heap and add an 'abandoned' flag. On Ctrl-C
> with a request still in flight, mark it abandoned and hand ownership to
> the lwIP callback, which frees the context when the connection finally
> tears down; the receive, headers-done and result callbacks return early
> so a stale callback neither stores data nor touches wget_info.
> 
> Fixes: 3c656c928bd7 ("net: lwip: add wget command")
> Signed-off-by: Shahriyar Jalayeri <[email protected]>
> ---
>  net/lwip/wget.c | 71 
> ++++++++++++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 55 insertions(+), 16 deletions(-)
> 
> diff --git a/net/lwip/wget.c b/net/lwip/wget.c
> index 9e93765926d..b81509dff3f 100644
> --- a/net/lwip/wget.c
> +++ b/net/lwip/wget.c
> @@ -12,6 +12,7 @@
>  #include <lwip/errno.h>
>  #include <lwip/timeouts.h>
>  #include <rng.h>
> +#include <malloc.h>
>  #include <mapmem.h>
>  #include <net.h>
>  #include <time.h>
> @@ -39,6 +40,7 @@ struct wget_ctx {
>       ulong content_len;
>       ulong hash_count;
>       enum done_state done;
> +     bool abandoned;
>  };
>  
>  static void wget_lwip_fill_info(struct pbuf *hdr, u16_t hdr_len, u32_t 
> hdr_cont_len)
> @@ -200,6 +202,13 @@ static err_t httpc_recv_cb(void *arg, struct altcp_pcb 
> *pcb, struct pbuf *pbuf,
>       if (!pbuf)
>               return ERR_BUF;
>  
> +     /* The caller gave up on this request; drop the connection. */
> +     if (ctx->abandoned) {
> +             altcp_abort(pcb);
> +             pbuf_free(pbuf);
> +             return ERR_ABRT;
> +     }
> +
>       if (!ctx->start_time)
>               ctx->start_time = get_timer(0);
>  
> @@ -227,6 +236,12 @@ static void httpc_result_cb(void *arg, httpc_result_t 
> httpc_result,
>       struct wget_ctx *ctx = arg;
>       ulong elapsed;
>  
> +     /* Last callback for an abandoned request: reclaim ctx and stop. */
> +     if (ctx->abandoned) {
> +             free(ctx);
> +             return;
> +     }
> +
>       wget_info->status_code = (u32)srv_res;
>  
>       if (err == ERR_BUF) {
> @@ -282,6 +297,9 @@ static err_t httpc_headers_done_cb(httpc_state_t 
> *connection, void *arg, struct
>  {
>       struct wget_ctx *ctx = arg;
>  
> +     if (ctx->abandoned)
> +             return ERR_BUF;
> +
>       wget_lwip_fill_info(hdr, hdr_len, content_len);
>  
>       if (wget_info->check_buffer_size && (ulong)content_len > 
> wget_info->buffer_size)
> @@ -376,8 +394,17 @@ static int wget_handle_request(struct wget_ctx *ctx, 
> bool is_https,
>  
>       while (!ctx->done) {
>               net_lwip_rx(udev, netif);
> -             if (ctrlc())
> +             if (ctrlc()) {
> +                     /*
> +                      * A request may still be in flight (e.g. the name is
> +                      * still resolving). Hand ctx to the lwIP callback, 
> which
> +                      * frees it once the connection tears down, instead of
> +                      * freeing it here under a live callback.
> +                      */
> +                     if (!ctx->done)
> +                             ctx->abandoned = true;

One more object seems to have the same lifetime issue here: httpc_connection_t 
conn is
stack-allocated in wget_handle_request(), but httpc_get_file_dns() keeps a 
pointer to it
in the HTTP client state and later dereferences it for the headers/result 
callbacks.

So if Ctrl-C makes wget_handle_request() return while DNS/connect is still 
pending, ctx
remains valid but conn does not. Could conn be moved into struct wget_ctx (or 
otherwise
made to live as long as the request) as well?

>                       break;
> +             }
>       }
>  
>       if (ctx->done == SUCCESS)
> @@ -392,27 +419,26 @@ static int wget_handle_request(struct wget_ctx *ctx, 
> bool is_https,
>  int wget_do_request(ulong dst_addr, char *uri)
>  {
>       struct udevice *udev;
> -     struct wget_ctx ctx;
> +     struct wget_ctx *ctx;
>       struct netif *netif;
> -     bool is_https;
> +     bool is_https, abandoned;
>       int ret;
>  
> -     ctx.daddr = dst_addr;
> -     ctx.saved_daddr = dst_addr;
> -     ctx.done = NOT_DONE;
> -     ctx.size = 0;
> -     ctx.prevsize = 0;
> -     ctx.start_time = 0;
> -     ctx.content_len = 0;
> -     ctx.hash_count = 0;
> +     ctx = calloc(1, sizeof(*ctx));
> +     if (!ctx)
> +             return -ENOMEM;
> +
> +     ctx->daddr = dst_addr;
> +     ctx->saved_daddr = dst_addr;
> +     ctx->done = NOT_DONE;
>  
> -     ret = parse_url(uri, ctx.server_name, &ctx.port, &ctx.path, &is_https);
> +     ret = parse_url(uri, ctx->server_name, &ctx->port, &ctx->path, 
> &is_https);
>       if (ret)
> -             return ret;
> +             goto out;
>  
>       ret = net_lwip_eth_start();
>       if (ret)
> -             return ret;
> +             goto out;
>  
>       if (!wget_info)
>               wget_info = &default_wget_info;
> @@ -422,14 +448,27 @@ int wget_do_request(ulong dst_addr, char *uri)
>       netif = net_lwip_new_netif(udev);
>       if (!netif) {
>               net_lwip_eth_stop();
> -             return -ENODEV;
> +             ret = -ENODEV;
> +             goto out;
>       }
>  
> -     ret = wget_handle_request(&ctx, is_https, udev, netif);
> +     ret = wget_handle_request(ctx, is_https, udev, netif);
> +
> +     /*
> +      * If the request was abandoned the lwIP callback still owns ctx and
> +      * frees it when the connection tears down; do not free it here.
> +      */
> +     abandoned = ctx->abandoned;
>  
>       net_lwip_remove_netif(netif);
>       net_lwip_eth_stop();
>  
> +     if (!abandoned)
> +             free(ctx);
> +
> +     return ret;
> +out:
> +     free(ctx);
>       return ret;
>  }
Thanks,
-- 
Jerome

Reply via email to