[Mesa-dev] [PATCH 1/2] linker: fix strdup memory leak
Ian Romanick
idr at freedesktop.org
Mon Dec 19 13:24:20 PST 2011
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>
> 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