{"thread":{"id":"64607","subject":"[PATCH 0/2] Add MEMZERO_ARRAY() macro and use it in coccinelle","startedAt":"2025-12-10T13:13:28Z","lastAt":"2025-12-19T09:17:59Z","messageCount":10,"participants":["Toon Claes","Junio C Hamano","Patrick Steinhardt","René Scharfe"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"531971","messageId":"20251210-toon-cocci-memzero-v1-0-ae916a79065b@iotcl.com","threadId":"64607","inReplyTo":null,"subject":"[PATCH 0/2] Add MEMZERO_ARRAY() macro and use it in coccinelle","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2025-12-10T13:13:00Z","receivedAt":"2025-12-10T13:13:28Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"A bug was found[1] in git-last-modified(1), caused by uninitialized\nmemory. While the bug is fixed, in the discussion after that[2] the\nsuggestion was made to introduce a macro that simplifies zeroing a\ndynamically allocated array.\n\nIn the first patch I'm addressing the outcome of the discussion on the\npatch, and in the second patch I'm fixing an edge-case I've encountered\nwhile using coccinelle.\n\nThere's one /oddball/ in add-patch.c that doesn't get caught by the\ncoccinelle rules, around line 960:\n\n    memset(hunk + 1, 0, (splittable_into - 1) * sizeof(*hunk));\n\nBecause there's some quirky pointer math going on, it think it's better\nto keep it like it is.\n\nThere were some mixed opinions about naming it either CLEAR_ARRAY() or\nMEMZERO_ARRAY(). I choose the latter because I wanted to avoid confusion\nthat \"clear\" would shrink the array to zero elements.\n\n[1]: <4dc4c8cd-c0cc-4784-8fcf-defa3a051087@mit.edu>\n[2]: <20251208201501.GA216526@coredump.intra.peff.net>\n\nSigned-off-by: Toon Claes <toon@iotcl.com>\n---\nToon Claes (2):\n      git-compat-util: introduce MEMZERO_ARRAY() macro\n      contrib/coccinelle: pass include paths to spatch(1)\n\n Makefile                       |  2 +-\n builtin/last-modified.c        |  2 +-\n compat/simple-ipc/ipc-win32.c  |  2 +-\n contrib/coccinelle/array.cocci | 20 ++++++++++++++++++++\n contrib/coccinelle/meson.build |  6 ++++++\n diff-delta.c                   |  2 +-\n ewah/bitmap.c                  |  7 +++----\n git-compat-util.h              |  1 +\n hashmap.c                      |  2 +-\n pack-revindex.c                |  2 +-\n 10 files changed, 36 insertions(+), 10 deletions(-)\n\n\n\n---\nbase-commit: 011ce54c26318d725db1d8971d157656eb965d88\nchange-id: 20251210-toon-cocci-memzero-2b6185e08ac4\n\n"},{"id":"531972","messageId":"20251210-toon-cocci-memzero-v1-1-ae916a79065b@iotcl.com","threadId":"64607","inReplyTo":"20251210-toon-cocci-memzero-v1-0-ae916a79065b@iotcl.com","subject":"[PATCH 1/2] git-compat-util: introduce MEMZERO_ARRAY() macro","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2025-12-10T13:13:01Z","receivedAt":"2025-12-10T13:13:30Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"Introduce a new macro MEMZERO_ARRAY() that zeroes the memory allocated\nby ALLOC_ARRAY() and friends. And add coccinelle rule to enforce the use\nof this macro.\n\nSigned-off-by: Toon Claes <toon@iotcl.com>\n---\n builtin/last-modified.c        |  2 +-\n compat/simple-ipc/ipc-win32.c  |  2 +-\n contrib/coccinelle/array.cocci | 20 ++++++++++++++++++++\n diff-delta.c                   |  2 +-\n ewah/bitmap.c                  |  7 +++----\n git-compat-util.h              |  1 +\n hashmap.c                      |  2 +-\n pack-revindex.c                |  2 +-\n 8 files changed, 29 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/last-modified.c b/builtin/last-modified.c\nindex cc5fd2e795..ac5387e861 100644\n--- a/builtin/last-modified.c\n+++ b/builtin/last-modified.c\n@@ -327,7 +327,7 @@ static void process_parent(struct last_modified *lm,\n \tif (!(parent->object.flags & PARENT1))\n \t\tactive_paths_free(lm, parent);\n \n-\tmemset(lm->scratch->words, 0x0, lm->scratch->word_alloc * sizeof(eword_t));\n+\tMEMZERO_ARRAY(lm->scratch->words, lm->scratch->word_alloc);\n \tdiff_queue_clear(&diff_queued_diff);\n }\n \ndiff --git a/compat/simple-ipc/ipc-win32.c b/compat/simple-ipc/ipc-win32.c\nindex a8fc812adf..4a3e7df9c7 100644\n--- a/compat/simple-ipc/ipc-win32.c\n+++ b/compat/simple-ipc/ipc-win32.c\n@@ -686,7 +686,7 @@ static LPSECURITY_ATTRIBUTES get_sa(struct my_sa_data *d)\n \t\tgoto fail;\n \t}\n \n-\tmemset(ea, 0, NR_EA * sizeof(EXPLICIT_ACCESS));\n+\tMEMZERO_ARRAY(ea, NR_EA);\n \n \tea[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;\n \tea[0].grfAccessMode = SET_ACCESS;\ndiff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci\nindex 27a3b479c9..d306f6a21e 100644\n--- a/contrib/coccinelle/array.cocci\n+++ b/contrib/coccinelle/array.cocci\n@@ -101,3 +101,23 @@ expression dst, src, n;\n -ALLOC_ARRAY(dst, n);\n -COPY_ARRAY(dst, src, n);\n +DUP_ARRAY(dst, src, n);\n+\n+@@\n+type T;\n+T *ptr;\n+expression n;\n+@@\n+- memset(ptr, \\( 0x0 \\| 0 \\), n * \\( sizeof(T)\n+-                                 \\| sizeof(*ptr)\n+-                                 \\) )\n++ MEMZERO_ARRAY(ptr, n)\n+\n+@@\n+type T;\n+T[] ptr;\n+expression n;\n+@@\n+- memset(ptr, \\( 0x0 \\| 0 \\), n * \\( sizeof(T)\n+-                                 \\| sizeof(*ptr)\n+-                                 \\) )\n++ MEMZERO_ARRAY(ptr, n)\ndiff --git a/diff-delta.c b/diff-delta.c\nindex 71d37368d6..43c339f010 100644\n--- a/diff-delta.c\n+++ b/diff-delta.c\n@@ -171,7 +171,7 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)\n \tmem = hash + hsize;\n \tentry = mem;\n \n-\tmemset(hash, 0, hsize * sizeof(*hash));\n+\tMEMZERO_ARRAY(hash, hsize);\n \n \t/* allocate an array to count hash entries */\n \thash_count = calloc(hsize, sizeof(*hash_count));\ndiff --git a/ewah/bitmap.c b/ewah/bitmap.c\nindex 55928dada8..bf878bf876 100644\n--- a/ewah/bitmap.c\n+++ b/ewah/bitmap.c\n@@ -46,8 +46,7 @@ static void bitmap_grow(struct bitmap *self, size_t word_alloc)\n {\n \tsize_t old_size = self->word_alloc;\n \tALLOC_GROW(self->words, word_alloc, self->word_alloc);\n-\tmemset(self->words + old_size, 0x0,\n-\t       (self->word_alloc - old_size) * sizeof(eword_t));\n+\tMEMZERO_ARRAY(self->words + old_size, (self->word_alloc - old_size));\n }\n \n void bitmap_set(struct bitmap *self, size_t pos)\n@@ -192,8 +191,8 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)\n \tif (self->word_alloc < other_final) {\n \t\tself->word_alloc = other_final;\n \t\tREALLOC_ARRAY(self->words, self->word_alloc);\n-\t\tmemset(self->words + original_size, 0x0,\n-\t\t\t(self->word_alloc - original_size) * sizeof(eword_t));\n+\t\tMEMZERO_ARRAY(self->words + original_size,\n+\t\t              (self->word_alloc - original_size));\n \t}\n \n \tewah_iterator_init(&it, other);\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 398e0fac4f..2b8192fd2e 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -726,6 +726,7 @@ static inline uint64_t u64_add(uint64_t a, uint64_t b)\n #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))\n #define CALLOC_ARRAY(x, alloc) (x) = xcalloc((alloc), sizeof(*(x)))\n #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))\n+#define MEMZERO_ARRAY(x, alloc) memset((x), 0x0, st_mult(sizeof(*(x)), (alloc)))\n \n #define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \\\n \tBARF_UNLESS_COPYABLE((dst), (src)))\ndiff --git a/hashmap.c b/hashmap.c\nindex a711377853..3b5d6f14bc 100644\n--- a/hashmap.c\n+++ b/hashmap.c\n@@ -194,7 +194,7 @@ void hashmap_partial_clear_(struct hashmap *map, ssize_t entry_offset)\n \t\treturn;\n \tif (entry_offset >= 0)  /* called by hashmap_clear_entries */\n \t\tfree_individual_entries(map, entry_offset);\n-\tmemset(map->table, 0, map->tablesize * sizeof(struct hashmap_entry *));\n+\tMEMZERO_ARRAY(map->table, map->tablesize);\n \tmap->shrink_at = 0;\n \tmap->private_size = 0;\n }\ndiff --git a/pack-revindex.c b/pack-revindex.c\nindex d0791cc493..8598b941c8 100644\n--- a/pack-revindex.c\n+++ b/pack-revindex.c\n@@ -75,7 +75,7 @@ static void sort_revindex(struct revindex_entry *entries, unsigned n, off_t max)\n \tfor (bits = 0; max >> bits; bits += DIGIT_SIZE) {\n \t\tunsigned i;\n \n-\t\tmemset(pos, 0, BUCKETS * sizeof(*pos));\n+\t\tMEMZERO_ARRAY(pos, BUCKETS);\n \n \t\t/*\n \t\t * We want pos[i] to store the index of the last element that\n\n-- \n2.52.0\n\n"},{"id":"531973","messageId":"20251210-toon-cocci-memzero-v1-2-ae916a79065b@iotcl.com","threadId":"64607","inReplyTo":"20251210-toon-cocci-memzero-v1-0-ae916a79065b@iotcl.com","subject":"[PATCH 2/2] contrib/coccinelle: pass include paths to spatch(1)","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2025-12-10T13:13:02Z","receivedAt":"2025-12-10T13:13:54Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"In the previous commit a new coccinelle rule is added. But neiter\n`make coccicheck` nor `meson compile coccicheck` did detect a case in\nbuiltin/last-modified.c.\n\nThis case involves the field `scratch` in `struct last_modified`. This\nfield is of type `struct bitmap` and that struct has a member\n`eword_t *words`. Both are defined in `ewah/ewok.h`. Now, while\nbuiltin/last-modified.c does include that header (with the subdir in the\n#include directive), it seems coccinelle does not process it. So it's\nunaware of the type of `words` in the bitmap, and it doesn't recognize\nthe rule from previous commit that uses:\n\n    type T;\n    T *ptr;\n\nFix coccicheck by passing all possible include paths inside the Git\nproject so spatch(1) can find the headers and can determine the types.\n\nSigned-off-by: Toon Claes <toon@iotcl.com>\n---\n Makefile                       | 2 +-\n contrib/coccinelle/meson.build | 6 ++++++\n 2 files changed, 7 insertions(+), 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex 6fc322ff88..46d07b2d52 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -981,7 +981,7 @@ SANITIZE_LEAK =\n SANITIZE_ADDRESS =\n \n # For the 'coccicheck' target\n-SPATCH_INCLUDE_FLAGS = --all-includes\n+SPATCH_INCLUDE_FLAGS = --all-includes $(addprefix -I ,compat ewah refs sha256 trace2 win32 xdiff)\n SPATCH_FLAGS =\n SPATCH_TEST_FLAGS =\n \ndiff --git a/contrib/coccinelle/meson.build b/contrib/coccinelle/meson.build\nindex dc3f73c2e7..ae7f5b5460 100644\n--- a/contrib/coccinelle/meson.build\n+++ b/contrib/coccinelle/meson.build\n@@ -50,6 +50,11 @@ foreach header : headers_to_check\n   coccinelle_headers += meson.project_source_root() / header\n endforeach\n \n+coccinelle_includes = []\n+foreach path : ['compat', 'ewah', 'refs', 'sha256', 'trace2', 'win32', 'xdiff']\n+  coccinelle_includes += ['-I', meson.project_source_root() / path]\n+endforeach\n+\n patches = [ ]\n foreach source : coccinelle_sources\n   patches += custom_target(\n@@ -58,6 +63,7 @@ foreach source : coccinelle_sources\n       '--all-includes',\n       '--sp-file', concatenated_rules,\n       '--patch', meson.project_source_root(),\n+      coccinelle_includes,\n       '@INPUT@',\n     ],\n     input: meson.project_source_root() / source,\n\n-- \n2.52.0\n\n"},{"id":"532011","messageId":"xmqqtsxxg0z4.fsf@gitster.g","threadId":"64607","inReplyTo":"20251210-toon-cocci-memzero-v1-1-ae916a79065b@iotcl.com","subject":"Re: [PATCH 1/2] git-compat-util: introduce MEMZERO_ARRAY() macro","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-11T03:18:39Z","receivedAt":"2025-12-11T03:18:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Toon Claes <toon@iotcl.com> writes:\n\n> +@@\n> +- memset(ptr, \\( 0x0 \\| 0 \\), n * \\( sizeof(T)\n> +-                                 \\| sizeof(*ptr)\n> +-                                 \\) )\n> ++ MEMZERO_ARRAY(ptr, n)\n\nShouldn't we be also catching\n\n\tmemset(array, '\\0', sizeof(array[0]) * ARRAY_SIZE(array));\n\nin addition to \"0\" and \"0x0\"?\n"},{"id":"532022","messageId":"aTpieqFoMmZiSzWS@pks.im","threadId":"64607","inReplyTo":"20251210-toon-cocci-memzero-v1-2-ae916a79065b@iotcl.com","subject":"Re: [PATCH 2/2] contrib/coccinelle: pass include paths to spatch(1)","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-12-11T06:19:38Z","receivedAt":"2025-12-11T06:19:45Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Wed, Dec 10, 2025 at 02:13:02PM +0100, Toon Claes wrote:\n> In the previous commit a new coccinelle rule is added. But neiter\n> `make coccicheck` nor `meson compile coccicheck` did detect a case in\n> builtin/last-modified.c.\n> \n> This case involves the field `scratch` in `struct last_modified`. This\n> field is of type `struct bitmap` and that struct has a member\n> `eword_t *words`. Both are defined in `ewah/ewok.h`. Now, while\n> builtin/last-modified.c does include that header (with the subdir in the\n> #include directive), it seems coccinelle does not process it. So it's\n> unaware of the type of `words` in the bitmap, and it doesn't recognize\n> the rule from previous commit that uses:\n> \n>     type T;\n>     T *ptr;\n> \n> Fix coccicheck by passing all possible include paths inside the Git\n> project so spatch(1) can find the headers and can determine the types.\n> \n> Signed-off-by: Toon Claes <toon@iotcl.com>\n> ---\n>  Makefile                       | 2 +-\n>  contrib/coccinelle/meson.build | 6 ++++++\n>  2 files changed, 7 insertions(+), 1 deletion(-)\n> \n> diff --git a/Makefile b/Makefile\n> index 6fc322ff88..46d07b2d52 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -981,7 +981,7 @@ SANITIZE_LEAK =\n>  SANITIZE_ADDRESS =\n>  \n>  # For the 'coccicheck' target\n> -SPATCH_INCLUDE_FLAGS = --all-includes\n> +SPATCH_INCLUDE_FLAGS = --all-includes $(addprefix -I ,compat ewah refs sha256 trace2 win32 xdiff)\n\nThis feels weird to me. We never pass any of these includes to the\ncompiler, either. So why should Coccinelle require them?\n\nComing back to your example of `eword_t`, Git knows to always include\n\"ewah/ewok.h\", and that include is relative to the root directory of Git\nitself. And as the header doesn't have any includes itself, this cannot\nbe the root cause, either.\n\nSo I'm a bit puzzled why this patch would fix the observed issue.\n\nPatrick\n"},{"id":"532059","messageId":"xmqqecp0cmth.fsf@gitster.g","threadId":"64607","inReplyTo":"aTpieqFoMmZiSzWS@pks.im","subject":"Re: [PATCH 2/2] contrib/coccinelle: pass include paths to spatch(1)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-12T05:04:58Z","receivedAt":"2025-12-12T05:05:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>>  # For the 'coccicheck' target\n>> -SPATCH_INCLUDE_FLAGS = --all-includes\n>> +SPATCH_INCLUDE_FLAGS = --all-includes $(addprefix -I ,compat ewah refs sha256 trace2 win32 xdiff)\n>\n> This feels weird to me. We never pass any of these includes to the\n> compiler, either. So why should Coccinelle require them?\n>\n> Coming back to your example of `eword_t`, Git knows to always include\n> \"ewah/ewok.h\", and that include is relative to the root directory of Git\n> itself. And as the header doesn't have any includes itself, this cannot\n> be the root cause, either.\n>\n> So I'm a bit puzzled why this patch would fix the observed issue.\n\nIndeed it is puzzling..\n"},{"id":"532069","messageId":"f02b628f-b9d7-4436-88ee-3255e02cb0f3@web.de","threadId":"64607","inReplyTo":"xmqqtsxxg0z4.fsf@gitster.g","subject":"Re: [PATCH 1/2] git-compat-util: introduce MEMZERO_ARRAY() macro","fromName":"René Scharfe","fromEmail":"l.s.r@web.de","sentAt":"2025-12-12T13:02:17Z","receivedAt":"2025-12-12T13:02:35Z","isPatch":true,"sender":{"key":"l.s.r@web.de","avatar":"https://avatars.githubusercontent.com/u/26122331?v=4"},"body":"On 12/11/25 4:18 AM, Junio C Hamano wrote:\n> Toon Claes <toon@iotcl.com> writes:\n> \n>> +@@\n>> +- memset(ptr, \\( 0x0 \\| 0 \\), n * \\( sizeof(T)\n>> +-                                 \\| sizeof(*ptr)\n>> +-                                 \\) )\n>> ++ MEMZERO_ARRAY(ptr, n)\n> \n> Shouldn't we be also catching\n> \n> \tmemset(array, '\\0', sizeof(array[0]) * ARRAY_SIZE(array));\n> \n> in addition to \"0\" and \"0x0\"?\n\nGood idea to match \"sizeof(ptr[...])\", even though we currently don't have\nmatching code.\n\nGood idea also to match \"'\\0'\".  There's code with that pattern in\ncompat/regex/.\n\nYou can drop \"0x0\", though, \"0\" matches it already (at least for me, I have\n\"spatch version 1.3-dirty compiled with OCaml version 5.1.1\" from Homebrew).\n\nIf you put parentheses around \"n\" in the pre-image then Coccinelle will\nremove them if present and still match code without them.  They are no\nlonger needed without the multiplication.  Their removal would improve the\nresult for ewah/bitmap.c.\n\nRené\n\n"},{"id":"532108","messageId":"xmqq5xabb5fg.fsf@gitster.g","threadId":"64607","inReplyTo":"20251210-toon-cocci-memzero-v1-0-ae916a79065b@iotcl.com","subject":"Re: [PATCH 0/2] Add MEMZERO_ARRAY() macro and use it in coccinelle","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-13T00:18:11Z","receivedAt":"2025-12-13T00:18:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Toon Claes <toon@iotcl.com> writes:\n\n> In the first patch I'm addressing the outcome of the discussion on the\n> patch, and in the second patch I'm fixing an edge-case I've encountered\n> while using coccinelle.\n\n\nWithout the attached, the failure at CI is impossible to diagnose.\nA test that emits \"you got some error messages\", without showing\nwhat they are, is useless.\n\n Makefile | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git i/Makefile w/Makefile\nindex 0bd502dc01..52e413f59a 100644\n--- i/Makefile\n+++ w/Makefile\n@@ -3534,7 +3534,7 @@ else\n COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = $(COCCICHECK_PATCHES_INTREE)\n endif\n coccicheck: $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES)\n-\t! grep -q ^ $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) /dev/null\n+\t! grep ^ $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) /dev/null\n \n # See contrib/coccinelle/README\n coccicheck-pending: coccicheck-test\n\n\nAnd FYI, with this series merged in, I get the following from\n'seen'.  I did not check if they contain any false positives (in\nwhich case the new coccinelle rules for this rewrite may have to be\nmarked as \"pending\", not for real checking), or they are real\nimprovements we should adopt.\n\nThanks.\n\n\ndiff -u -p a/diffcore-delta.c b/diffcore-delta.c\n--- a/diffcore-delta.c\n+++ b/diffcore-delta.c\n@@ -56,7 +56,7 @@ static struct spanhash_top *spanhash_reh\n \t\t\t     st_mult(sizeof(struct spanhash), sz)));\n \tnew_spanhash->alloc_log2 = orig->alloc_log2 + 1;\n \tnew_spanhash->free = INITIAL_FREE(new_spanhash->alloc_log2);\n-\tmemset(new_spanhash->data, 0, sizeof(struct spanhash) * sz);\n+\tMEMZERO_ARRAY(new_spanhash->data, sz);\n \tfor (i = 0; i < osz; i++) {\n \t\tstruct spanhash *o = &(orig->data[i]);\n \t\tint bucket;\n@@ -135,7 +135,7 @@ static struct spanhash_top *hash_chars(s\n \t\t\t      st_mult(sizeof(struct spanhash), (size_t)1 << i)));\n \thash->alloc_log2 = i;\n \thash->free = INITIAL_FREE(i);\n-\tmemset(hash->data, 0, sizeof(struct spanhash) * ((size_t)1 << i));\n+\tMEMZERO_ARRAY(hash->data, ((size_t)1 << i));\n \n \tn = 0;\n \taccum1 = accum2 = 0;\ndiff -u -p a/linear-assignment.c b/linear-assignment.c\n--- a/linear-assignment.c\n+++ b/linear-assignment.c\n@@ -20,8 +20,8 @@ void compute_assignment(int column_count\n \tint i, j, phase;\n \n \tif (column_count < 2) {\n-\t\tmemset(column2row, 0, sizeof(int) * column_count);\n-\t\tmemset(row2column, 0, sizeof(int) * row_count);\n+\t\tMEMZERO_ARRAY(column2row, column_count);\n+\t\tMEMZERO_ARRAY(row2column, row_count);\n \t\treturn;\n \t}\n \ndiff -u -p a/shallow.c b/shallow.c\n--- a/shallow.c\n+++ b/shallow.c\n@@ -745,7 +745,7 @@ void assign_shallow_commits_to_refs(stru\n \n \tif (used) {\n \t\tint bitmap_size = DIV_ROUND_UP(pi.nr_bits, 32) * sizeof(uint32_t);\n-\t\tmemset(used, 0, sizeof(*used) * info->shallow->nr);\n+\t\tMEMZERO_ARRAY(used, info->shallow->nr);\n \t\tfor (i = 0; i < nr_shallow; i++) {\n \t\t\tconst struct commit *c = lookup_commit(the_repository,\n \t\t\t\t\t\t\t       &oid[shallow[i]]);\n@@ -810,7 +810,7 @@ static void post_assign_shallow(struct s\n \n \ttrace_printf_key(&trace_shallow, \"shallow: post_assign_shallow\\n\");\n \tif (ref_status)\n-\t\tmemset(ref_status, 0, sizeof(*ref_status) * info->ref->nr);\n+\t\tMEMZERO_ARRAY(ref_status, info->ref->nr);\n \n \t/* Remove unreachable shallow commits from \"theirs\" */\n \tfor (i = dst = 0; i < info->nr_theirs; i++) {\n"},{"id":"532109","messageId":"xmqqwm2r9nv7.fsf@gitster.g","threadId":"64607","inReplyTo":"20251210-toon-cocci-memzero-v1-2-ae916a79065b@iotcl.com","subject":"Re: [PATCH 2/2] contrib/coccinelle: pass include paths to spatch(1)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-12-13T01:22:52Z","receivedAt":"2025-12-13T01:22:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Toon Claes <toon@iotcl.com> writes:\n\n> In the previous commit a new coccinelle rule is added. But neiter\n> `make coccicheck` nor `meson compile coccicheck` did detect a case in\n> builtin/last-modified.c.\n\nI can reproduce this.  I started with only git-compat-util.h and\ncontrib/coccinelle/array.cocci from your [1/2] and without [2/2],\nand \"make coccicheck\" produced all other changes contained in [1/2]\nand the leftover changes to diffcore-delta.c, linear-assignment.c\nand shallow.c I reported earlier in a separate message, but the one\nin last-modified.c is left intact.  There are successful rewrites\nthat involve eword_t in other files, so I am not sure what the\nproblem is.\n\nI used the following instead of your [1/2], as suggested by René in\nan earlier exchange.  I did not see any changes but I did not expect\nto, either.\n\n\n contrib/coccinelle/array.cocci | 22 ++++++++++++++++++++++\n git-compat-util.h              |  1 +\n 2 files changed, 23 insertions(+)\n\ndiff --git a/contrib/coccinelle/array.cocci b/contrib/coccinelle/array.cocci\nindex 27a3b479c9..ae23114b68 100644\n--- a/contrib/coccinelle/array.cocci\n+++ b/contrib/coccinelle/array.cocci\n@@ -101,3 +101,25 @@ expression dst, src, n;\n -ALLOC_ARRAY(dst, n);\n -COPY_ARRAY(dst, src, n);\n +DUP_ARRAY(dst, src, n);\n+\n+@@\n+type T;\n+T *ptr;\n+expression n;\n+@@\n+- memset(ptr, \\( '\\0' \\| 0 \\), n * \\( sizeof(T)\n+-                                  \\| sizeof(*ptr)\n+-                                  \\| sizeof(ptr[0])\n+-                                  \\) )\n++ MEMZERO_ARRAY(ptr, n)\n+\n+@@\n+type T;\n+T[] ptr;\n+expression n;\n+@@\n+- memset(ptr, \\( '\\0' \\| 0 \\), n * \\( sizeof(T)\n+-                                  \\| sizeof(*ptr)\n+-                                  \\| sizeof(ptr[0])\n+-                                  \\) )\n++ MEMZERO_ARRAY(ptr, n)\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 398e0fac4f..2b8192fd2e 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -726,6 +726,7 @@ static inline uint64_t u64_add(uint64_t a, uint64_t b)\n #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))\n #define CALLOC_ARRAY(x, alloc) (x) = xcalloc((alloc), sizeof(*(x)))\n #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))\n+#define MEMZERO_ARRAY(x, alloc) memset((x), 0x0, st_mult(sizeof(*(x)), (alloc)))\n \n #define COPY_ARRAY(dst, src, n) copy_array((dst), (src), (n), sizeof(*(dst)) + \\\n \tBARF_UNLESS_COPYABLE((dst), (src)))\n\n\n"},{"id":"532532","messageId":"878qeyhlua.fsf@iotcl.com","threadId":"64607","inReplyTo":"f02b628f-b9d7-4436-88ee-3255e02cb0f3@web.de","subject":"Re: [PATCH 1/2] git-compat-util: introduce MEMZERO_ARRAY() macro","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2025-12-19T09:17:33Z","receivedAt":"2025-12-19T09:17:59Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"René Scharfe <l.s.r@web.de> writes:\n\n> On 12/11/25 4:18 AM, Junio C Hamano wrote:\n>> Toon Claes <toon@iotcl.com> writes:\n>> \n>>> +@@\n>>> +- memset(ptr, \\( 0x0 \\| 0 \\), n * \\( sizeof(T)\n>>> +-                                 \\| sizeof(*ptr)\n>>> +-                                 \\) )\n>>> ++ MEMZERO_ARRAY(ptr, n)\n>> \n>> Shouldn't we be also catching\n>> \n>> \tmemset(array, '\\0', sizeof(array[0]) * ARRAY_SIZE(array));\n>> \n>> in addition to \"0\" and \"0x0\"?\n>\n> Good idea to match \"sizeof(ptr[...])\", even though we currently don't have\n> matching code.\n\nI didn't include that because I didn't see any case that would be\ncovered by that. But it's good to include it anyway to capture future\ncode.\n\n> Good idea also to match \"'\\0'\".  There's code with that pattern in\n> compat/regex/.\n\nGood find, I didn't think about that.\n\n> You can drop \"0x0\", though, \"0\" matches it already (at least for me, I have\n> \"spatch version 1.3-dirty compiled with OCaml version 5.1.1\" from\n> Homebrew).\n\nNice!\n\n> If you put parentheses around \"n\" in the pre-image then Coccinelle will\n> remove them if present and still match code without them.\n\nWell, that's not what I am seeing. With parentheses around \"n\", it\ndidn't match the case in builtin/last-modified.c on my machine. I'm\nusing spatch v1.3\n\n> They are no longer needed without the multiplication. Their removal\n> would improve the result for ewah/bitmap.c.\n\nI agree it would be an improvement. Using `\\( (n) \\| n \\)` does the\ntrick for all cases I've found.\n\n\n-- \nCheers,\nToon\n"}]}