Subject: [drivers/net/wwan] Question about possible memory leaks in t7xx_dpmaif_rx_buf_alloc() and t7xx_dpmaif_rx_frag_alloc()

From: Chenyuan Yang <[email-masked]>
Date: Thu, 14 Mar 2024 20:16:09 -0500

Dear WWAN Driver Developers,

We are curious whether the functions `t7xx_dpmaif_rx_buf_alloc()` and `t7xx_dpmaif_rx_frag_alloc` might have memory leaks.

#1. The function `t7xx_dpmaif_rx_buf_alloc` is https://elixir.bootlin.com/linux/v6.8/source/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c#L164
and the relevant code is
```
int t7xx_dpmaif_rx_buf_alloc(struct dpmaif_ctrl *dpmaif_ctrl,
			     const struct dpmaif_bat_request *bat_req,
			     const unsigned int q_num, const unsigned int buf_cnt,
			     const bool initial)
{
	unsigned int i, bat_cnt, bat_max_cnt, bat_start_idx;
	int ret;
	...
	for (i = 0; i < buf_cnt; i++) {
		unsigned int cur_bat_idx = bat_start_idx + i;
		struct dpmaif_bat_skb *cur_skb;
		struct dpmaif_bat *cur_bat;

		if (cur_bat_idx >= bat_max_cnt)
			cur_bat_idx -= bat_max_cnt;

		cur_skb = (struct dpmaif_bat_skb *)bat_req->bat_skb + cur_bat_idx;
		if (!cur_skb->skb &&
		    !t7xx_alloc_and_map_skb_info(dpmaif_ctrl, bat_req->pkt_buf_sz, cur_skb))
			break;

		cur_bat = (struct dpmaif_bat *)bat_req->bat_base + cur_bat_idx;
	}
	...
	ret = t7xx_dpmaif_update_bat_wr_idx(dpmaif_ctrl, q_num, i);
	if (ret)
		goto err_unmap_skbs;
	...
err_unmap_skbs:
	while (--i > 0)
		t7xx_unmap_bat_skb(dpmaif_ctrl->dev, bat_req->bat_skb, i);
}
```

In the label `err_unmap_skbs`, the function will unmap the allocated memory for `bat_req->bat_skb` by checking `while (--i > 0)`. However, the first element (`i=0`) of `bat_req->bat_skb` is not unmapped since `i` is decremented before the check. 
By contrast, in the function `t7xx_dpmaif_bat_free` (https://elixir.bootlin.com/linux/v6.8/source/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c#L984), the first element of `bat_req->bat_skb` is unmapped.

Based on our understanding, a possible fix would be
```
-      while (--i > 0)
+      while (--i >= 0)
```

#2. For another function `t7xx_dpmaif_rx_frag_alloc`, the function is https://elixir.bootlin.com/linux/v6.8/source/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c#L319

```
int t7xx_dpmaif_rx_frag_alloc(struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_bat_request *bat_req,
			      const unsigned int buf_cnt, const bool initial)
{
	unsigned int buf_space, cur_bat_idx = bat_req->bat_wr_idx;
	struct dpmaif_bat_page *bat_skb = bat_req->bat_skb;
	int ret = 0, i;

	if (!buf_cnt || buf_cnt > bat_req->bat_size_cnt)
		return -EINVAL;
	...
	for (i = 0; i < buf_cnt; i++) {
		struct dpmaif_bat_page *cur_page = bat_skb + cur_bat_idx;
		struct dpmaif_bat *cur_bat;
		dma_addr_t data_base_addr;
		...
		cur_bat = (struct dpmaif_bat *)bat_req->bat_base + cur_bat_idx;
		cur_bat->buffer_addr_ext = upper_32_bits(data_base_addr);
		cur_bat->p_buffer_addr = lower_32_bits(data_base_addr);
		cur_bat_idx = t7xx_ring_buf_get_next_wr_idx(bat_req->bat_size_cnt, cur_bat_idx);
	}
	...
	if (i < buf_cnt) {
		ret = -ENOMEM;
		if (initial) {
			while (--i > 0)
				t7xx_unmap_bat_page(dpmaif_ctrl->dev, bat_req->bat_skb, i);
		}
	}

	return ret;
}
```

Similarly, the function will unmap the allocated memory for `bat_req->bat_skb` by checking `while (--i > 0)`. However, the first element (`i=0`) of `bat_req->bat_skb` is not unmapped since `i` is decremented before the check.

Please kindly correct us if we missed any key information. Looking forward to your response!

Best,
Chenyuan