[PATCH] for_each_string_list_item(): behave correctly for empty list
- From
Michael Haggerty <mhagger@alum.mit.edu>
- Date
- Sep 15, 2017, 16:00 UTC
- Message-ID
- <cb2d4d71c7c1db452b86c8076c153cabe7384e28.1505490776.git.mhagger@alum.mit.edu>
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 <mhagger@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