[libvirt] [PATCH] util: Forbid calling hash APIs from iterator callback

Daniel Veillard veillard at redhat.com
Thu Mar 10 11:11:00 UTC 2011


On Wed, Mar 09, 2011 at 02:20:37PM +0100, Jiri Denemark wrote:
> Calling most hash APIs is not safe from inside of an iterator callback.
> Exceptions are APIs that do not modify the hash table and removing
> current hash entry from virHashFroEach callback.
> 
> This patch make all APIs which are not safe fail instead of just relying
> on the callback being nice not calling any unsafe APIs.
> ---
>  src/util/hash.c |   39 +++++++++++++++++++++++++++++++--------
>  1 files changed, 31 insertions(+), 8 deletions(-)
> 
> diff --git a/src/util/hash.c b/src/util/hash.c
> index 2a9a9cf..1a300f6 100644
> --- a/src/util/hash.c
> +++ b/src/util/hash.c
> @@ -54,6 +54,10 @@ struct _virHashTable {
>      struct _virHashEntry *table;
>      int size;
>      int nbElems;
> +    /* True iff we are iterating over hash entries. */
> +    bool iterating;
> +    /* Pointer to the current entry during iteration. */
> +    virHashEntryPtr current;
>      virHashDataFree dataFree;
>      virHashKeyCode keyCode;
>      virHashKeyEqual keyEqual;
> @@ -313,7 +317,7 @@ virHashAddOrUpdateEntry(virHashTablePtr table, const void *name,
>      char *new_name;
>      bool found;
>  
> -    if ((table == NULL) || (name == NULL))
> +    if ((table == NULL) || (name == NULL) || table->iterating)
>          return (-1);
>  
>      /*
> @@ -513,6 +517,8 @@ virHashRemoveEntry(virHashTablePtr table, const void *name)
>          for (entry = &(table->table[key]); entry != NULL;
>               entry = entry->next) {
>              if (table->keyEqual(entry->name, name)) {
> +                if (table->iterating && table->current != entry)
> +                    return -1;

I think we should emit an error message to indicate why it failed !

>                  if (table->dataFree && (entry->payload != NULL))
>                      table->dataFree(entry->payload, entry->name);
>                  entry->payload = NULL;
> @@ -548,28 +554,35 @@ virHashRemoveEntry(virHashTablePtr table, const void *name)
>   * @data: opaque data to pass to the iterator
>   *
>   * Iterates over every element in the hash table, invoking the
> - * 'iter' callback. The callback is allowed to remove the element using
> - * virHashRemoveEntry but calling other virHash* functions is prohibited.
> + * 'iter' callback. The callback is allowed to remove the current element
> + * using virHashRemoveEntry but calling other virHash* functions is prohibited.
>   *
>   * Returns number of items iterated over upon completion, -1 on failure
>   */
> -int virHashForEach(virHashTablePtr table, virHashIterator iter, void *data) {
> +int virHashForEach(virHashTablePtr table, virHashIterator iter, void *data)
> +{
>      int i, count = 0;
>  
> -    if (table == NULL || iter == NULL)
> +    if (table == NULL || iter == NULL || table->iterating)
>          return (-1);
>  
> +    table->iterating = true;
> +    table->current = NULL;
>      for (i = 0 ; i < table->size ; i++) {
>          virHashEntryPtr entry = table->table + i;
>          while (entry) {
>              virHashEntryPtr next = entry->next;
>              if (entry->valid) {
> +                table->current = entry;
>                  iter(entry->payload, entry->name, data);
> +                table->current = NULL;
>                  count++;
>              }
>              entry = next;
>          }
>      }
> +    table->iterating = false;
> +
>      return (count);
>  }
>  
> @@ -590,9 +603,11 @@ int virHashForEach(virHashTablePtr table, virHashIterator iter, void *data) {
>  int virHashRemoveSet(virHashTablePtr table, virHashSearcher iter, const void *data) {
>      int i, count = 0;
>  
> -    if (table == NULL || iter == NULL)
> +    if (table == NULL || iter == NULL || table->iterating)
>          return (-1);
>  
> +    table->iterating = true;
> +    table->current = NULL;
>      for (i = 0 ; i < table->size ; i++) {
>          virHashEntryPtr prev = NULL;
>          virHashEntryPtr entry = &(table->table[i]);
> @@ -629,6 +644,8 @@ int virHashRemoveSet(virHashTablePtr table, virHashSearcher iter, const void *da
>              }
>          }
>      }
> +    table->iterating = false;
> +
>      return (count);
>  }
>  
> @@ -646,18 +663,24 @@ int virHashRemoveSet(virHashTablePtr table, virHashSearcher iter, const void *da
>  void *virHashSearch(virHashTablePtr table, virHashSearcher iter, const void *data) {
>      int i;
>  
> -    if (table == NULL || iter == NULL)
> +    if (table == NULL || iter == NULL || table->iterating)

  Same thing an error message must be provided

>          return (NULL);
>  
> +    table->iterating = true;
> +    table->current = NULL;
>      for (i = 0 ; i < table->size ; i++) {
>          virHashEntryPtr entry = table->table + i;
>          while (entry) {
>              if (entry->valid) {
> -                if (iter(entry->payload, entry->name, data))
> +                if (iter(entry->payload, entry->name, data)) {
> +                    table->iterating = false;
>                      return entry->payload;
> +                }
>              }
>              entry = entry->next;
>          }
>      }
> +    table->iterating = false;
> +
>      return (NULL);
>  }

  I would really prefer those to be added if needed after the initial
  commit,

    thanks,

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list