Some tests introduce memory leaks by not freeing all the pkt_stream
objects they're creating.
Fix these memory leaks.
Signed-off-by: Bastien Curutchet (eBPF Foundation) <bastien.curutchet@bootlin.com>
---
tools/testing/selftests/bpf/test_xsk.c | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/test_xsk.c b/tools/testing/selftests/bpf/test_xsk.c
index 37752b2dad651b36d155051931d7b3b902fac403..e4059c7d0a289449a6b73669fa87f7440b7f55c0 100644
--- a/tools/testing/selftests/bpf/test_xsk.c
+++ b/tools/testing/selftests/bpf/test_xsk.c
@@ -546,6 +546,13 @@ static void pkt_stream_receive_half(struct test_spec *test)
struct pkt_stream *pkt_stream = test->ifobj_tx->xsk->pkt_stream;
u32 i;
+ if (test->ifobj_rx->xsk->pkt_stream != test->rx_pkt_stream_default)
+ /* Packet stream has already been replaced so we have to release this one.
+ * The newly created one will be freed by the restore_default() at the
+ * end of the test
+ */
+ pkt_stream_delete(test->ifobj_rx->xsk->pkt_stream);
+
test->ifobj_rx->xsk->pkt_stream = pkt_stream_generate(pkt_stream->nb_pkts,
pkt_stream->pkts[0].len);
pkt_stream = test->ifobj_rx->xsk->pkt_stream;
@@ -573,6 +580,22 @@ static void pkt_stream_even_odd_sequence(struct test_spec *test)
}
}
+static void release_even_odd_sequence(struct test_spec *test)
+{
+ struct pkt_stream *later_free_tx = test->ifobj_tx->xsk->pkt_stream;
+ struct pkt_stream *later_free_rx = test->ifobj_rx->xsk->pkt_stream;
+ int i;
+
+ for (i = 0; i < test->nb_sockets; i++) {
+ /* later_free_{rx/tx} will be freed by restore_default() */
+ if (test->ifobj_tx->xsk_arr[i].pkt_stream != later_free_tx)
+ pkt_stream_delete(test->ifobj_tx->xsk_arr[i].pkt_stream);
+ if (test->ifobj_rx->xsk_arr[i].pkt_stream != later_free_rx)
+ pkt_stream_delete(test->ifobj_rx->xsk_arr[i].pkt_stream);
+ }
+
+}
+
static u64 pkt_get_addr(struct pkt *pkt, struct xsk_umem_info *umem)
{
if (!pkt->valid)
@@ -1874,6 +1897,7 @@ int testapp_stats_tx_invalid_descs(struct test_spec *test)
int testapp_stats_rx_full(struct test_spec *test)
{
pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_PKT_SIZE);
+ pkt_stream_delete(test->ifobj_rx->xsk->pkt_stream);
test->ifobj_rx->xsk->pkt_stream = pkt_stream_generate(DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE);
test->ifobj_rx->xsk->rxqsize = DEFAULT_UMEM_BUFFERS;
@@ -1885,6 +1909,7 @@ int testapp_stats_rx_full(struct test_spec *test)
int testapp_stats_fill_empty(struct test_spec *test)
{
pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_PKT_SIZE);
+ pkt_stream_delete(test->ifobj_rx->xsk->pkt_stream);
test->ifobj_rx->xsk->pkt_stream = pkt_stream_generate(DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE);
test->ifobj_rx->use_fill_ring = false;
@@ -2043,6 +2068,7 @@ int testapp_xdp_shared_umem(struct test_spec *test)
{
struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs;
struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs;
+ int ret;
test->total_steps = 1;
test->nb_sockets = 2;
@@ -2053,7 +2079,11 @@ int testapp_xdp_shared_umem(struct test_spec *test)
pkt_stream_even_odd_sequence(test);
- return testapp_validate_traffic(test);
+ ret = testapp_validate_traffic(test);
+
+ release_even_odd_sequence(test);
+
+ return ret;
}
int testapp_poll_txq_tmout(struct test_spec *test)
--
2.50.1
On Thu, Sep 04, 2025 at 12:10:18PM +0200, Bastien Curutchet (eBPF Foundation) wrote: > Some tests introduce memory leaks by not freeing all the pkt_stream > objects they're creating. > > Fix these memory leaks. I would appreciate being more explicit here as I've been scratching my head here. From what I see the problem is with testapp_stats_rx_dropped() as it's the one case that uses replace and receive half of pkt streams, both of which overwrite the default pkt stream. So we lose a pointer to one of pkt streams and leak it eventually. Rest of cases should be ok with pkt_stream_restore_default() being called after each ->test_func() in run_pkt_test(). Anyways let me hear more on your reasoning, thanks! I'm gonna have a second look tomorrow, I might be missing something. > > Signed-off-by: Bastien Curutchet (eBPF Foundation) <bastien.curutchet@bootlin.com> > --- > tools/testing/selftests/bpf/test_xsk.c | 32 +++++++++++++++++++++++++++++++- > 1 file changed, 31 insertions(+), 1 deletion(-) > > diff --git a/tools/testing/selftests/bpf/test_xsk.c b/tools/testing/selftests/bpf/test_xsk.c > index 37752b2dad651b36d155051931d7b3b902fac403..e4059c7d0a289449a6b73669fa87f7440b7f55c0 100644 > --- a/tools/testing/selftests/bpf/test_xsk.c > +++ b/tools/testing/selftests/bpf/test_xsk.c > @@ -546,6 +546,13 @@ static void pkt_stream_receive_half(struct test_spec *test) > struct pkt_stream *pkt_stream = test->ifobj_tx->xsk->pkt_stream; > u32 i; > > + if (test->ifobj_rx->xsk->pkt_stream != test->rx_pkt_stream_default) > + /* Packet stream has already been replaced so we have to release this one. > + * The newly created one will be freed by the restore_default() at the > + * end of the test > + */ > + pkt_stream_delete(test->ifobj_rx->xsk->pkt_stream); > + > test->ifobj_rx->xsk->pkt_stream = pkt_stream_generate(pkt_stream->nb_pkts, > pkt_stream->pkts[0].len); > pkt_stream = test->ifobj_rx->xsk->pkt_stream; > @@ -573,6 +580,22 @@ static void pkt_stream_even_odd_sequence(struct test_spec *test) > } > } > > +static void release_even_odd_sequence(struct test_spec *test) > +{ > + struct pkt_stream *later_free_tx = test->ifobj_tx->xsk->pkt_stream; > + struct pkt_stream *later_free_rx = test->ifobj_rx->xsk->pkt_stream; > + int i; > + > + for (i = 0; i < test->nb_sockets; i++) { > + /* later_free_{rx/tx} will be freed by restore_default() */ > + if (test->ifobj_tx->xsk_arr[i].pkt_stream != later_free_tx) > + pkt_stream_delete(test->ifobj_tx->xsk_arr[i].pkt_stream); > + if (test->ifobj_rx->xsk_arr[i].pkt_stream != later_free_rx) > + pkt_stream_delete(test->ifobj_rx->xsk_arr[i].pkt_stream); > + } > + > +} > + > static u64 pkt_get_addr(struct pkt *pkt, struct xsk_umem_info *umem) > { > if (!pkt->valid) > @@ -1874,6 +1897,7 @@ int testapp_stats_tx_invalid_descs(struct test_spec *test) > int testapp_stats_rx_full(struct test_spec *test) > { > pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_PKT_SIZE); > + pkt_stream_delete(test->ifobj_rx->xsk->pkt_stream); > test->ifobj_rx->xsk->pkt_stream = pkt_stream_generate(DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE); > > test->ifobj_rx->xsk->rxqsize = DEFAULT_UMEM_BUFFERS; > @@ -1885,6 +1909,7 @@ int testapp_stats_rx_full(struct test_spec *test) > int testapp_stats_fill_empty(struct test_spec *test) > { > pkt_stream_replace(test, DEFAULT_UMEM_BUFFERS + DEFAULT_UMEM_BUFFERS / 2, MIN_PKT_SIZE); > + pkt_stream_delete(test->ifobj_rx->xsk->pkt_stream); > test->ifobj_rx->xsk->pkt_stream = pkt_stream_generate(DEFAULT_UMEM_BUFFERS, MIN_PKT_SIZE); > > test->ifobj_rx->use_fill_ring = false; > @@ -2043,6 +2068,7 @@ int testapp_xdp_shared_umem(struct test_spec *test) > { > struct xsk_xdp_progs *skel_rx = test->ifobj_rx->xdp_progs; > struct xsk_xdp_progs *skel_tx = test->ifobj_tx->xdp_progs; > + int ret; > > test->total_steps = 1; > test->nb_sockets = 2; > @@ -2053,7 +2079,11 @@ int testapp_xdp_shared_umem(struct test_spec *test) > > pkt_stream_even_odd_sequence(test); > > - return testapp_validate_traffic(test); > + ret = testapp_validate_traffic(test); > + > + release_even_odd_sequence(test); > + > + return ret; > } > > int testapp_poll_txq_tmout(struct test_spec *test) > > -- > 2.50.1 >
Hi Maciej On 9/16/25 7:58 PM, Maciej Fijalkowski wrote: > On Thu, Sep 04, 2025 at 12:10:18PM +0200, Bastien Curutchet (eBPF Foundation) wrote: >> Some tests introduce memory leaks by not freeing all the pkt_stream >> objects they're creating. >> >> Fix these memory leaks. > > I would appreciate being more explicit here as I've been scratching my > head here. > Indeed it lacks details sorry. IIRC I spotted these with valgrind, maybe I can add valgrind's output to the commit log in next iteration. > From what I see the problem is with testapp_stats_rx_dropped() as it's the > one case that uses replace and receive half of pkt streams, both of which > overwrite the default pkt stream. So we lose a pointer to one of pkt > streams and leak it eventually. > Exactly, we lose pointers in some cases when xsk->pkt_stream gets replaced by a new stream. testapp_stats_rx_dropped() is the most convoluted of these cases. Best regards, -- Bastien Curutchet, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
On Wed, Sep 17, 2025 at 05:32:55PM +0200, Bastien Curutchet wrote: > Hi Maciej > > On 9/16/25 7:58 PM, Maciej Fijalkowski wrote: > > On Thu, Sep 04, 2025 at 12:10:18PM +0200, Bastien Curutchet (eBPF Foundation) wrote: > > > Some tests introduce memory leaks by not freeing all the pkt_stream > > > objects they're creating. > > > > > > Fix these memory leaks. > > > > I would appreciate being more explicit here as I've been scratching my > > head here. > > > > Indeed it lacks details sorry. IIRC I spotted these with valgrind, maybe I > can add valgrind's output to the commit log in next iteration. > > > From what I see the problem is with testapp_stats_rx_dropped() as it's the > > one case that uses replace and receive half of pkt streams, both of which > > overwrite the default pkt stream. So we lose a pointer to one of pkt > > streams and leak it eventually. > > > > Exactly, we lose pointers in some cases when xsk->pkt_stream gets replaced > by a new stream. testapp_stats_rx_dropped() is the most convoluted of these > cases. pkt_stream_restore_default() is supposed to delete overwritten pkt_stream and set ::pkt_stream to default one, explicit pkt_stream_delete() in bunch of tests is redundant IMHO. Per my understanding testapp_stats_rx_dropped() and testapp_xdp_shared_umem() need fixing. First generate pkt_stream twice and second generates pkt_stream on each xsk from xsk_arr, where normally xsk_arr[0] gets pkt_streams and xsk_arr[1] have them NULLed. > > Best regards, > -- > Bastien Curutchet, Bootlin > Embedded Linux and Kernel engineering > https://bootlin.com >
On 9/17/25 8:33 PM, Maciej Fijalkowski wrote: > On Wed, Sep 17, 2025 at 05:32:55PM +0200, Bastien Curutchet wrote: >> Hi Maciej >> >> On 9/16/25 7:58 PM, Maciej Fijalkowski wrote: >>> On Thu, Sep 04, 2025 at 12:10:18PM +0200, Bastien Curutchet (eBPF Foundation) wrote: >>>> Some tests introduce memory leaks by not freeing all the pkt_stream >>>> objects they're creating. >>>> >>>> Fix these memory leaks. >>> >>> I would appreciate being more explicit here as I've been scratching my >>> head here. >>> >> >> Indeed it lacks details sorry. IIRC I spotted these with valgrind, maybe I >> can add valgrind's output to the commit log in next iteration. >> >>> From what I see the problem is with testapp_stats_rx_dropped() as it's the >>> one case that uses replace and receive half of pkt streams, both of which >>> overwrite the default pkt stream. So we lose a pointer to one of pkt >>> streams and leak it eventually. >>> >> >> Exactly, we lose pointers in some cases when xsk->pkt_stream gets replaced >> by a new stream. testapp_stats_rx_dropped() is the most convoluted of these >> cases. > > pkt_stream_restore_default() is supposed to delete overwritten pkt_stream > and set ::pkt_stream to default one, explicit pkt_stream_delete() in bunch > of tests is redundant IMHO. > > Per my understanding testapp_stats_rx_dropped() and > testapp_xdp_shared_umem() need fixing. First generate pkt_stream twice and > second generates pkt_stream on each xsk from xsk_arr, where normally > xsk_arr[0] gets pkt_streams and xsk_arr[1] have them NULLed. > I took another look at it, and I agree with you: the pkt_stream_delete() calls I added in testapp_stats_rx_full() and testapp_stats_fill_empty() don't seem necessary. It still feels a bit strange to overwrite a pointer without freeing it right away, but I don't have a strong opinion on this. I'm fine with only fixing testapp_stats_rx_dropped() and testapp_xdp_shared_umem() in the next iteration. Best regards, -- Bastien Curutchet, Bootlin Embedded Linux and Kernel engineering https://bootlin.com
© 2016 - 2025 Red Hat, Inc.