Re: [PATCH] tests/9p: fix potential leak in v9fs_rreaddir()

2023-05-04 Thread Christian Schoenebeck
On Saturday, April 29, 2023 11:25:33 AM CEST Christian Schoenebeck wrote:
> Free allocated directory entries in v9fs_rreaddir() if argument
> `entries` was passed as NULL, to avoid a memory leak. It is
> explicitly allowed by design for `entries` to be NULL. [1]
> 
> [1] https://lore.kernel.org/all/1690923.g4PEXVpXuU@silver
> 
> Reported-by: Coverity (CID 1487558)
> Signed-off-by: Christian Schoenebeck 
> ---

Queued on 9p.next:
https://github.com/cschoenebeck/qemu/commits/9p.next

Thanks!

Best regards,
Christian Schoenebeck

>  tests/qtest/libqos/virtio-9p-client.c | 5 +
>  1 file changed, 5 insertions(+)
> 
> diff --git a/tests/qtest/libqos/virtio-9p-client.c 
> b/tests/qtest/libqos/virtio-9p-client.c
> index e4a368e036..b8adc8d4b9 100644
> --- a/tests/qtest/libqos/virtio-9p-client.c
> +++ b/tests/qtest/libqos/virtio-9p-client.c
> @@ -594,6 +594,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
> *nentries,
>  {
>  uint32_t local_count;
>  struct V9fsDirent *e = NULL;
> +/* only used to avoid a leak if entries was NULL */
> +struct V9fsDirent *unused_entries = NULL;
>  uint16_t slen;
>  uint32_t n = 0;
>  
> @@ -612,6 +614,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
> *nentries,
>  e = g_new(struct V9fsDirent, 1);
>  if (entries) {
>  *entries = e;
> +} else {
> +unused_entries = e;
>  }
>  } else {
>  e = e->next = g_new(struct V9fsDirent, 1);
> @@ -628,6 +632,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
> *nentries,
>  *nentries = n;
>  }
>  
> +v9fs_free_dirents(unused_entries);
>  v9fs_req_free(req);
>  }
>  
> 





Re: [PATCH] tests/9p: fix potential leak in v9fs_rreaddir()

2023-05-01 Thread Greg Kurz
On Sat, 29 Apr 2023 15:20:12 +0200
Christian Schoenebeck  wrote:

> On Saturday, April 29, 2023 2:04:30 PM CEST Greg Kurz wrote:
> > Hi Christian !
> 
> Hi there, it's been a while! :)
> 
> > On Sat, 29 Apr 2023 11:25:33 +0200
> > Christian Schoenebeck  wrote:
> > 
> > > Free allocated directory entries in v9fs_rreaddir() if argument
> > > `entries` was passed as NULL, to avoid a memory leak. It is
> > > explicitly allowed by design for `entries` to be NULL. [1]
> > > 
> > > [1] https://lore.kernel.org/all/1690923.g4PEXVpXuU@silver
> > > 
> > > Reported-by: Coverity (CID 1487558)
> > > Signed-off-by: Christian Schoenebeck 
> > > ---
> > 
> > Good catch Coverity ! :-)
> 
> Yeah, this Coverity report is actually from March and I ignored it so far,
> because the reported leak could never happen with current test code. But Paolo
> brought it up this week, so ...
> 
> > Reviewed-by: Greg Kurz 
> > 
> > I still have a suggestion. See below.
> > 
> > >  tests/qtest/libqos/virtio-9p-client.c | 5 +
> > >  1 file changed, 5 insertions(+)
> > > 
> > > diff --git a/tests/qtest/libqos/virtio-9p-client.c 
> > > b/tests/qtest/libqos/virtio-9p-client.c
> > > index e4a368e036..b8adc8d4b9 100644
> > > --- a/tests/qtest/libqos/virtio-9p-client.c
> > > +++ b/tests/qtest/libqos/virtio-9p-client.c
> > > @@ -594,6 +594,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > > uint32_t *nentries,
> > >  {
> > >  uint32_t local_count;
> > >  struct V9fsDirent *e = NULL;
> > > +/* only used to avoid a leak if entries was NULL */
> > > +struct V9fsDirent *unused_entries = NULL;
> > >  uint16_t slen;
> > >  uint32_t n = 0;
> > >  
> > > @@ -612,6 +614,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > > uint32_t *nentries,
> > >  e = g_new(struct V9fsDirent, 1);
> > >  if (entries) {
> > >  *entries = e;
> > > +} else {
> > > +unused_entries = e;
> > >  }
> > >  } else {
> > >  e = e->next = g_new(struct V9fsDirent, 1);
> > 
> > This is always allocating and chaining a new entry even
> > though it isn't needed in the entries == NULL case.
> > 
> > > @@ -628,6 +632,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > > uint32_t *nentries,
> > >  *nentries = n;
> > >  }
> > >  
> > > +v9fs_free_dirents(unused_entries);
> > 
> > This is going to loop again on all entries to free them.
> > 
> > >  v9fs_req_free(req);
> > >  }
> > >  
> > 
> > If this function is to be called one day with an enormous
> > number of entries and entries == NULL case, this might
> > not scale well.
> > 
> > What about only allocating a single entry in this case ?
> > 
> > E.g.
> > 
> > @@ -593,7 +593,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > uint32_t *nentries,
> > struct V9fsDirent **entries)
> >  {
> >  uint32_t local_count;
> > -struct V9fsDirent *e = NULL;
> > +g_autofree struct V9fsDirent *e = NULL;
> >  uint16_t slen;
> >  uint32_t n = 0;
> >  
> > @@ -611,10 +611,12 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > uint32_t *nentries,
> >  if (!e) {
> >  e = g_new(struct V9fsDirent, 1);
> >  if (entries) {
> > -*entries = e;
> > +*entries = g_steal_pointer(e);
> 
> g_steal_pointer(e) just sets `e` to NULL and returns its old value, so ...
> 
> >  }
> >  } else {
> > -e = e->next = g_new(struct V9fsDirent, 1);
> > +if (entries) {
> > +e = e->next = g_new(struct V9fsDirent, 1);
> > +}
> 
> ... this `else` block would never be reached and no list assembled.
> 
> >  }
> >  e->next = NULL;
> >  /* qid[13] offset[8] type[1] name[s] */
> 
> And even if above's issue was fixed, then it would cause a use-after-free for
> the last element in the list if entries != NULL and caller trying to access
> the last element afterwards. So you would still need a separate g_autofree
> pointer instead of tagging `e` directly, or something like this after loop
> end:
> 
>   if (entries)
> g_steal_pointer(e);
> 
> Which would somehow defeat the purpose of using g_autofree though.
> 
> I mean, yes this could be addressed, but is it worth it? I don't know. Even
> this reported leak is a purely theoretical one, but I understand if people
> want to silence a warning.
> 

Yeah you're right.

Cheers,

--
Greg

> Best regards,
> Christian Schoenebeck
> 
> 




Re: [PATCH] tests/9p: fix potential leak in v9fs_rreaddir()

2023-04-29 Thread Christian Schoenebeck
On Saturday, April 29, 2023 2:04:30 PM CEST Greg Kurz wrote:
> Hi Christian !

Hi there, it's been a while! :)

> On Sat, 29 Apr 2023 11:25:33 +0200
> Christian Schoenebeck  wrote:
> 
> > Free allocated directory entries in v9fs_rreaddir() if argument
> > `entries` was passed as NULL, to avoid a memory leak. It is
> > explicitly allowed by design for `entries` to be NULL. [1]
> > 
> > [1] https://lore.kernel.org/all/1690923.g4PEXVpXuU@silver
> > 
> > Reported-by: Coverity (CID 1487558)
> > Signed-off-by: Christian Schoenebeck 
> > ---
> 
> Good catch Coverity ! :-)

Yeah, this Coverity report is actually from March and I ignored it so far,
because the reported leak could never happen with current test code. But Paolo
brought it up this week, so ...

> Reviewed-by: Greg Kurz 
> 
> I still have a suggestion. See below.
> 
> >  tests/qtest/libqos/virtio-9p-client.c | 5 +
> >  1 file changed, 5 insertions(+)
> > 
> > diff --git a/tests/qtest/libqos/virtio-9p-client.c 
> > b/tests/qtest/libqos/virtio-9p-client.c
> > index e4a368e036..b8adc8d4b9 100644
> > --- a/tests/qtest/libqos/virtio-9p-client.c
> > +++ b/tests/qtest/libqos/virtio-9p-client.c
> > @@ -594,6 +594,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > uint32_t *nentries,
> >  {
> >  uint32_t local_count;
> >  struct V9fsDirent *e = NULL;
> > +/* only used to avoid a leak if entries was NULL */
> > +struct V9fsDirent *unused_entries = NULL;
> >  uint16_t slen;
> >  uint32_t n = 0;
> >  
> > @@ -612,6 +614,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > uint32_t *nentries,
> >  e = g_new(struct V9fsDirent, 1);
> >  if (entries) {
> >  *entries = e;
> > +} else {
> > +unused_entries = e;
> >  }
> >  } else {
> >  e = e->next = g_new(struct V9fsDirent, 1);
> 
> This is always allocating and chaining a new entry even
> though it isn't needed in the entries == NULL case.
> 
> > @@ -628,6 +632,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> > uint32_t *nentries,
> >  *nentries = n;
> >  }
> >  
> > +v9fs_free_dirents(unused_entries);
> 
> This is going to loop again on all entries to free them.
> 
> >  v9fs_req_free(req);
> >  }
> >  
> 
> If this function is to be called one day with an enormous
> number of entries and entries == NULL case, this might
> not scale well.
> 
> What about only allocating a single entry in this case ?
> 
> E.g.
> 
> @@ -593,7 +593,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
> *nentries,
> struct V9fsDirent **entries)
>  {
>  uint32_t local_count;
> -struct V9fsDirent *e = NULL;
> +g_autofree struct V9fsDirent *e = NULL;
>  uint16_t slen;
>  uint32_t n = 0;
>  
> @@ -611,10 +611,12 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, 
> uint32_t *nentries,
>  if (!e) {
>  e = g_new(struct V9fsDirent, 1);
>  if (entries) {
> -*entries = e;
> +*entries = g_steal_pointer(e);

g_steal_pointer(e) just sets `e` to NULL and returns its old value, so ...

>  }
>  } else {
> -e = e->next = g_new(struct V9fsDirent, 1);
> +if (entries) {
> +e = e->next = g_new(struct V9fsDirent, 1);
> +}

... this `else` block would never be reached and no list assembled.

>  }
>  e->next = NULL;
>  /* qid[13] offset[8] type[1] name[s] */

And even if above's issue was fixed, then it would cause a use-after-free for
the last element in the list if entries != NULL and caller trying to access
the last element afterwards. So you would still need a separate g_autofree
pointer instead of tagging `e` directly, or something like this after loop
end:

  if (entries)
g_steal_pointer(e);

Which would somehow defeat the purpose of using g_autofree though.

I mean, yes this could be addressed, but is it worth it? I don't know. Even
this reported leak is a purely theoretical one, but I understand if people
want to silence a warning.

Best regards,
Christian Schoenebeck





Re: [PATCH] tests/9p: fix potential leak in v9fs_rreaddir()

2023-04-29 Thread Greg Kurz
Hi Christian !

On Sat, 29 Apr 2023 11:25:33 +0200
Christian Schoenebeck  wrote:

> Free allocated directory entries in v9fs_rreaddir() if argument
> `entries` was passed as NULL, to avoid a memory leak. It is
> explicitly allowed by design for `entries` to be NULL. [1]
> 
> [1] https://lore.kernel.org/all/1690923.g4PEXVpXuU@silver
> 
> Reported-by: Coverity (CID 1487558)
> Signed-off-by: Christian Schoenebeck 
> ---

Good catch Coverity ! :-)

Reviewed-by: Greg Kurz 

I still have a suggestion. See below.

>  tests/qtest/libqos/virtio-9p-client.c | 5 +
>  1 file changed, 5 insertions(+)
> 
> diff --git a/tests/qtest/libqos/virtio-9p-client.c 
> b/tests/qtest/libqos/virtio-9p-client.c
> index e4a368e036..b8adc8d4b9 100644
> --- a/tests/qtest/libqos/virtio-9p-client.c
> +++ b/tests/qtest/libqos/virtio-9p-client.c
> @@ -594,6 +594,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
> *nentries,
>  {
>  uint32_t local_count;
>  struct V9fsDirent *e = NULL;
> +/* only used to avoid a leak if entries was NULL */
> +struct V9fsDirent *unused_entries = NULL;
>  uint16_t slen;
>  uint32_t n = 0;
>  
> @@ -612,6 +614,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
> *nentries,
>  e = g_new(struct V9fsDirent, 1);
>  if (entries) {
>  *entries = e;
> +} else {
> +unused_entries = e;
>  }
>  } else {
>  e = e->next = g_new(struct V9fsDirent, 1);

This is always allocating and chaining a new entry even
though it isn't needed in the entries == NULL case.

> @@ -628,6 +632,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
> *nentries,
>  *nentries = n;
>  }
>  
> +v9fs_free_dirents(unused_entries);

This is going to loop again on all entries to free them.

>  v9fs_req_free(req);
>  }
>  

If this function is to be called one day with an enormous
number of entries and entries == NULL case, this might
not scale well.

What about only allocating a single entry in this case ?

E.g.

@@ -593,7 +593,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
*nentries,
struct V9fsDirent **entries)
 {
 uint32_t local_count;
-struct V9fsDirent *e = NULL;
+g_autofree struct V9fsDirent *e = NULL;
 uint16_t slen;
 uint32_t n = 0;
 
@@ -611,10 +611,12 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
*nentries,
 if (!e) {
 e = g_new(struct V9fsDirent, 1);
 if (entries) {
-*entries = e;
+*entries = g_steal_pointer(e);
 }
 } else {
-e = e->next = g_new(struct V9fsDirent, 1);
+if (entries) {
+e = e->next = g_new(struct V9fsDirent, 1);
+}
 }
 e->next = NULL;
 /* qid[13] offset[8] type[1] name[s] */




[PATCH] tests/9p: fix potential leak in v9fs_rreaddir()

2023-04-29 Thread Christian Schoenebeck
Free allocated directory entries in v9fs_rreaddir() if argument
`entries` was passed as NULL, to avoid a memory leak. It is
explicitly allowed by design for `entries` to be NULL. [1]

[1] https://lore.kernel.org/all/1690923.g4PEXVpXuU@silver

Reported-by: Coverity (CID 1487558)
Signed-off-by: Christian Schoenebeck 
---
 tests/qtest/libqos/virtio-9p-client.c | 5 +
 1 file changed, 5 insertions(+)

diff --git a/tests/qtest/libqos/virtio-9p-client.c 
b/tests/qtest/libqos/virtio-9p-client.c
index e4a368e036..b8adc8d4b9 100644
--- a/tests/qtest/libqos/virtio-9p-client.c
+++ b/tests/qtest/libqos/virtio-9p-client.c
@@ -594,6 +594,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
*nentries,
 {
 uint32_t local_count;
 struct V9fsDirent *e = NULL;
+/* only used to avoid a leak if entries was NULL */
+struct V9fsDirent *unused_entries = NULL;
 uint16_t slen;
 uint32_t n = 0;
 
@@ -612,6 +614,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
*nentries,
 e = g_new(struct V9fsDirent, 1);
 if (entries) {
 *entries = e;
+} else {
+unused_entries = e;
 }
 } else {
 e = e->next = g_new(struct V9fsDirent, 1);
@@ -628,6 +632,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t 
*nentries,
 *nentries = n;
 }
 
+v9fs_free_dirents(unused_entries);
 v9fs_req_free(req);
 }
 
-- 
2.30.2