If you pass a newly-initialized or newly-cleared `string_list` to
`for_each_string_list_item()`, then the latter does

    for (
            item = (list)->items; /* note, this is NULL */
            item < (list)->items + (list)->nr; /* note: NULL + 0 */
            ++item)

Even though this probably works almost everywhere, it is undefined
behavior, and it could plausibly cause highly-optimizing compilers to
misbehave.

It would be a pain to have to change the signature of this macro, and
we'd prefer not to add overhead to each iteration of the loop. So
instead, whenever `list->items` is NULL, initialize `item` to point at
a dummy `string_list_item` created for the purpose.

This problem was noticed by Coverity.

Signed-off-by: Michael Haggerty <mhag...@alum.mit.edu>
---
Just a little thing I noticed in a Coverity report. This macro has
been broken since it was first introduced, in 2010.

This patch applies against maint. It is also available from my Git
fork [1] as branch `iter-empty-string-list`.

Michael

[1] https://github.com/mhagger/git

 string-list.c | 2 ++
 string-list.h | 7 +++++--
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/string-list.c b/string-list.c
index 806b4c8723..7eacf6037f 100644
--- a/string-list.c
+++ b/string-list.c
@@ -1,6 +1,8 @@
 #include "cache.h"
 #include "string-list.h"
 
+struct string_list_item dummy_string_list_item;
+
 void string_list_init(struct string_list *list, int strdup_strings)
 {
        memset(list, 0, sizeof(*list));
diff --git a/string-list.h b/string-list.h
index 29bfb7ae45..79bb78d80a 100644
--- a/string-list.h
+++ b/string-list.h
@@ -32,8 +32,11 @@ void string_list_clear_func(struct string_list *list, 
string_list_clear_func_t c
 typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t, void *cb_data);
-#define for_each_string_list_item(item,list) \
-       for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
+extern struct string_list_item dummy_string_list_item;
+#define for_each_string_list_item(item,list)                                 \
+       for (item = (list)->items ? (list)->items : &dummy_string_list_item; \
+            item < (list)->items + (list)->nr;                              \
+            ++item)
 
 /*
  * Apply want to each item in list, retaining only the ones for which
-- 
2.14.1

Reply via email to