alloc: fix dangling pointer in alloc_state cleanup

All callers of clear_alloc_state() immediately free what they cleared, so currently it does not hurt anybody that the alloc_state is left in an unreusable state, but it is an error-prone API. Replace it with a new function that clears but in addition frees the structure, as well as NULLing the pointer that points at it and adjust existing callers. As it is a moral equivalent of FREE_AND_NULL(), except that what it frees has internal structure that needs to be cleaned, allow the helper to be called twice in a row, by making a call with a pointer to a pointer variable that already is NULLed. While at it, rename allocate_alloc_state() and name the new function alloc_state_free_and_null(), to follow more closely the function naming convention specified in the CodingGuidelines (namely, functions about S are named with S_ prefix and then verb). Signed-off-by: ノウラ | Flare <nouraellm@gmail.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

ノウラ | Flare committed Sep 4, 2025 at 17:44 UTC 5e2feb5ca692c5c4d39b11e1ffa056911dd7dfd3
3 files changed +20 -20
alloc.c
+8 -2
@@ -36,19 +36,25 @@ struct alloc_state {
36 int slab_nr, slab_alloc;
37 };
38
39 -struct alloc_state *allocate_alloc_state(void)
39 +struct alloc_state *alloc_state_alloc(void)
40 {
41 return xcalloc(1, sizeof(struct alloc_state));
42 }
43
44 -void clear_alloc_state(struct alloc_state *s)
44 +void alloc_state_free_and_null(struct alloc_state **s_)
45 {
46 + struct alloc_state *s = *s_;
47 +
48 + if (!s)
49 + return;
50 +
51 while (s->slab_nr > 0) {
52 s->slab_nr--;
53 free(s->slabs[s->slab_nr]);
54 }
55
56 FREE_AND_NULL(s->slabs);
57 + FREE_AND_NULL(*s_);
58 }
59
60 static inline void *alloc_node(struct alloc_state *s, size_t node_size)
alloc.h
+2 -2
@@ -14,7 +14,7 @@ void *alloc_commit_node(struct repository *r);
14 void *alloc_tag_node(struct repository *r);
15 void *alloc_object_node(struct repository *r);
16
17 -struct alloc_state *allocate_alloc_state(void);
18 -void clear_alloc_state(struct alloc_state *s);
17 +struct alloc_state *alloc_state_alloc(void);
18 +void alloc_state_free_and_null(struct alloc_state **s_);
19
20 #endif
object.c
+10 -16
@@ -517,12 +517,11 @@ struct parsed_object_pool *parsed_object_pool_new(struct repository *repo)
517 memset(o, 0, sizeof(*o));
518
519 o->repo = repo;
520 - o->blob_state = allocate_alloc_state();
521 - o->tree_state = allocate_alloc_state();
522 - o->commit_state = allocate_alloc_state();
523 - o->tag_state = allocate_alloc_state();
524 - o->object_state = allocate_alloc_state();
525 -
520 + o->blob_state = alloc_state_alloc();
521 + o->tree_state = alloc_state_alloc();
522 + o->commit_state = alloc_state_alloc();
523 + o->tag_state = alloc_state_alloc();
524 + o->object_state = alloc_state_alloc();
525 o->is_shallow = -1;
526 CALLOC_ARRAY(o->shallow_stat, 1);
527
@@ -573,16 +572,11 @@ void parsed_object_pool_clear(struct parsed_object_pool *o)
572 o->buffer_slab = NULL;
573
574 parsed_object_pool_reset_commit_grafts(o);
576 - clear_alloc_state(o->blob_state);
577 - clear_alloc_state(o->tree_state);
578 - clear_alloc_state(o->commit_state);
579 - clear_alloc_state(o->tag_state);
580 - clear_alloc_state(o->object_state);
575 + alloc_state_free_and_null(&o->blob_state);
576 + alloc_state_free_and_null(&o->tree_state);
577 + alloc_state_free_and_null(&o->commit_state);
578 + alloc_state_free_and_null(&o->tag_state);
579 + alloc_state_free_and_null(&o->object_state);
580 stat_validity_clear(o->shallow_stat);
582 - FREE_AND_NULL(o->blob_state);
583 - FREE_AND_NULL(o->tree_state);
584 - FREE_AND_NULL(o->commit_state);
585 - FREE_AND_NULL(o->tag_state);
586 - FREE_AND_NULL(o->object_state);
581 FREE_AND_NULL(o->shallow_stat);
582 }