[libvirt PATCH] virThreadPoolExpand: Prevent expanding worker pool by zero
Peter Krempa
pkrempa at redhat.com
Mon Jul 12 14:40:17 UTC 2021
On Mon, Jul 12, 2021 at 12:57:31 +0200, Tim Wiederhake wrote:
> On Mon, 2021-07-12 at 10:09 +0200, Peter Krempa wrote:
> > On Fri, Jul 09, 2021 at 15:43:06 +0200, Tim Wiederhake wrote:
> > > `virThreadPoolNewFull` may call `virThreadPoolExpand` with
> > > `prioWorkers` = 0.
> >
> > Could you elaborate in which situations this happens?
>
> Sure!
>
> On libvirtd startup, the list of priority worker threads is
> uninitialized (`pool->prioWorkers` is NULL in `virThreadPoolNewFull`),
> and then "expanded" to zero (`prioWorkers`) entries.
Please mention this in the commit message that it's an actually occuring
problem.
>
> This can be triggered by:
>
> $ meson -Dbuildtype=debug -Db_lundef=false -
> Db_sanitize=address,undefined build
> $ ninja -C build
> $ ./build/run build/src/libvirtd
> ../src/util/viralloc.c:79:5: runtime error: null pointer passed as
> argument 1, which is declared to never be null
>
> Note that this happens directly on startup, even before the "libvirt
> version" and "hostname" output, and no client connection is required.
>
> > > This causes `virThreadPoolExpand` to call `VIR_EXPAND_N` on a null
> > > pointer
> > > and an increment of zero. The zero increment triggers `virReallocN`
> > > to not
> > > actually allocate any memory and leave the pointer NULL, which,
> > > eventually,
> > > causes `memset(NULL, 0, 0)` to be called in `virExpandN`.
> > >
> > > `memset` is declared `__attribute__ ((__nonnull__ 1))`, which
> > > triggers the
> > > following warning when libvirt is compiled with address sanitizing
> > > enabled:
> > >
> > > src/util/viralloc.c:82:5: runtime error: null pointer passed as
> > > argument 1, which is declared to never be null
> > >
> > > Signed-off-by: Tim Wiederhake <twiederh at redhat.com>
> > > ---
> > > src/util/virthreadpool.c | 3 +++
> > > 1 file changed, 3 insertions(+)
> > >
> > > diff --git a/src/util/virthreadpool.c b/src/util/virthreadpool.c
> > > index 9ddd86a679..c9d2a17ff4 100644
> > > --- a/src/util/virthreadpool.c
> > > +++ b/src/util/virthreadpool.c
> > > @@ -179,6 +179,9 @@ virThreadPoolExpand(virThreadPool *pool, size_t
> > > gain, bool priority)
> > > size_t i = 0;
> > > struct virThreadPoolWorkerData *data = NULL;
> > >
> > > + if (gain == 0)
> > > + return 0;
> >
> > IMO this is fixing a symptom rather than a root cause unless you
> > justify
> > it.
>
> Sorry, I don't follow. This guards against calling `VIR_EXPAND_N` on an
> uninitialized list with 0 increment...
>
> > > + VIR_EXPAND_N(*workers, *curWorkers, gain);
>
> ... here, in the very next line.
>
> I would be happy to fix this in virExpandN ...
>
> diff --git a/src/util/viralloc.c b/src/util/viralloc.c
> index cd7ae9e7d1..41c7cf22a6 100644
> --- a/src/util/viralloc.c
> +++ b/src/util/viralloc.c
> @@ -76,7 +76,8 @@ void virExpandN(void *ptrptr,
> abort();
>
> virReallocN(ptrptr, size, *countptr + add);
> - memset(*(char **)ptrptr + (size * *countptr), 0, size * add);
> + if (*(char **)ptrptr)
> + memset(*(char **)ptrptr + (size * *countptr), 0, size *
> add);
> *countptr += add;
> }
>
> ... but that introduces a branch (that is almost always taken) into a
> code path that is executed way more often than the one time the worker
> pool is set up. A check for `add == 0` would be overdoing things, as a
> zero `add` is not problematic if the list is initialized.
>
> Another way to fix this would be adding a guard in
> `virThreadPoolNewFull`...
>
> diff --git a/src/util/virthreadpool.c b/src/util/virthreadpool.c
> index 9ddd86a679..f04d5a3f0a 100644
> --- a/src/util/virthreadpool.c
> +++ b/src/util/virthreadpool.c
> @@ -247,10 +247,10 @@ virThreadPoolNewFull(size_t minWorkers,
> pool->maxWorkers = maxWorkers;
> pool->maxPrioWorkers = prioWorkers;
>
> - if (virThreadPoolExpand(pool, minWorkers, false) < 0)
> + if (minWorkers && virThreadPoolExpand(pool, minWorkers, false)
> < 0)
> goto error;
>
> - if (virThreadPoolExpand(pool, prioWorkers, true) < 0)
> + if (prioWorkers && virThreadPoolExpand(pool, prioWorkers,
> true) < 0)
> goto error;
>
> return pool;
>
> ... but this does not guard against potential later "zero expansions"
> on the still NULL array.
I meant this. A zero expansion doesn't make much sense in the first
place so adding code to special case it and do nothing feels weird.
More information about the libvir-list
mailing list