[Mesa-dev] [PATCH 1/2] linker: fix strdup memory leak
Pekka Paalanen
ppaalanen at gmail.com
Tue Dec 20 01:56:10 PST 2011
On Mon, 19 Dec 2011 13:24:20 -0800
Ian Romanick <idr at freedesktop.org> wrote:
> On 12/19/2011 05:23 AM, Pekka Paalanen wrote:
> > On Fri, 16 Dec 2011 10:46:11 -0800
> > Ian Romanick<idr at freedesktop.org> wrote:
> >
> >> On 12/14/2011 11:26 PM, Pekka Paalanen wrote:
> >>> string_to_uint_map::put() already does a strdup() for the key
> >>> argument, so we leak the memory allocated by strdup() in
> >>> link_uniforms.cpp.
> >>>
> >>> Remove the extra strdup(), fixes a few Valgrind detected leaks.
> >>
> >> Have you run piglit on this? I seem to recall adding the extra
> >> strdup for a reason. The hash table keeps a copy of the key
> >> pointer passed to it, and the underlying object may be deleted
> >> before the hash table is deleted. This can happen if the back-end
> >> optimizes some uniforms away after the linker has generated the
> >> list of "active" uniforms. I'm pretty sure there were one or two
> >> test cases that hit this on i965.
> >
> > Sorry, I didn't. Finally got piglit running, though readPixSanity
> > failed on stencil values. Anyway it's running all.tests now for the
> > upstream Mesa master, and then I'll run it with my patches.
> >
> > Oh, this is Sandybridge, btw.
> >
> >>> --- a/src/glsl/link_uniforms.cpp
> >>> +++ b/src/glsl/link_uniforms.cpp
> >>> @@ -174,8 +174,7 @@ private:
> >>> if (this->map->get(id, name))
> >>> return;
> >>>
> >>> - char *key = strdup(name);
> >>> - this->map->put(this->num_active_uniforms, key);
> >>> + this->map->put(this->num_active_uniforms, name);
> >>>
> >>> /* Each leaf uniform occupies one entry in the list of
> >>> active
> >>> * uniforms.
> >
> > The whole visit_field() function is this:
> >
> > virtual void visit_field(const glsl_type *type, const char
> > *name) {
> > assert(!type->is_record());
> > assert(!(type->is_array()&&
> > type->fields.array->is_record()));
> >
> > /* Count the number of samplers regardless of whether the
> > uniform is
> > * already in the hash table. The hash table prevents
> > adding the same
> > * uniform for multiple shader targets, but in this case we
> > want to
> > * count it for each shader target.
> > */
> > const unsigned values = values_for_type(type);
> > if (type->contains_sampler()) {
> > this->num_shader_samplers +=
> > type->is_array() ? type->array_size() : 1;
> > } else {
> > /* Accumulate the total number of uniform slots used by
> > this shader.
> > * Note that samplers do not count against this limit
> > because they
> > * don't use any storage on current hardware.
> > */
> > this->num_shader_uniform_components += values;
> > }
> >
> > /* If the uniform is already in the map, there's nothing
> > more to do. */
> > unsigned id;
> > if (this->map->get(id, name))
> > return;
> >
> > char *key = strdup(name);
> > this->map->put(this->num_active_uniforms, key);
> >
> > /* Each leaf uniform occupies one entry in the list of active
> > * uniforms.
> > */
> > this->num_active_uniforms++;
> > this->num_values += values;
> > }
> >
> > The variable 'key' is not used anywhere else but passed to
> > string_to_uint_map::put(). That function in its whole is:
> >
> > void put(unsigned value, const char *key)
> > {
> > /* The low-level hash table structure returns NULL if key is
> > not in the
> > * hash table. However, users of this map might want to
> > store zero as a
> > * valid value in the table. Bias the value by +1 so that a
> > * user-specified zero is stored as 1. This enables ::get
> > to tell the
> > * difference between a user-specified zero (returned as 1 by
> > * hash_table_find) and the key not in the table (returned
> > as 0 by
> > * hash_table_find).
> > *
> > * The net effect is that we can't store UINT_MAX in the
> > table. This is
> > * because UINT_MAX+1 = 0.
> > */
> > assert(value != UINT_MAX);
> > hash_table_replace(this->ht,
> > (void *) (intptr_t) (value + 1),
> > strdup(key));
> > }
> >
> > Therefore, as far as I see, 'key' is not used for anything else than
> > passed again through strdup(), which would effectively be:
>
> Ah yes, I forgot that I added that in string_to_uint_map::put. You
> are quite correct.
>
> Okay, the original patch is:
>
> Reviewed-by: Ian Romanick <ian.d.romanick at intel.com>
You were right, it does regress in piglit.
This particular patch causes varray-disabled to fail in quick.tests.
From 4 runs of rebuilt Mesa, 3 fail in varray-disabled, and the
remaining one fails in EXT_TIMER_QUERY:time-elapsed instead.
Some of the rebuilds were from clean checkout (git clean -dxf), some
simply by adding the patch on top of a previous build that produced the
reference piglit results.
Unfortunately I have to move on for now, so I'm not pushing this patch.
Thanks.
> > hash_table_replace(..., ..., strdup(strdup(name)));
> >
> > I don't see any case where this could not be a leak, or prevent a
> > bug.
> >
> > What do I miss? Is there somewhere another put() that overwrites or
> > overloads this version of put()?
> >
> >
> > Thanks,
> > pq
> >
> > ps. As for the other patch, thanks for the advice on how to fix it
> > properly. I don't think I can do that this year, though.
More information about the mesa-dev
mailing list