]> git.ipfire.org Git - thirdparty/git.git/commitdiff
clean: do not pass strbuf by value
authorJunio C Hamano <gitster@pobox.com>
Thu, 31 Jul 2025 22:54:24 +0000 (15:54 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 3 Aug 2025 05:37:06 +0000 (22:37 -0700)
When you pass a structure by value, the callee can modify the
contents of the structure that was passed in without having to worry
about changing the structure the caller has.  Passing structure by
value sometimes (but not very often) can be a valid way to give
callee a temporary variable it can freely modify.

But not a structure with members that are pointers, like a strbuf.

builtin/clean.c:list_and_choose() reads a line interactively from
the user, and passes the line (in a strbuf) to parse_choice() by
value, which then munges by replacing ',' with ' ' (to accept both
comma and space separated list of choices).  But because the strbuf
passed by value still shares the underlying character array buf[],
this ends up munging the caller's strbuf contents.

This is a catastrophe waiting to happen.  If the callee causes the
strbuf to be reallocated, the buf[] the caller has will become
dangling, and when the caller does strbuf_release(), it would result
in double-free.

Stop calling the function with misleading call-by-value with strbuf.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/clean.c

index 053c94fc6bd12ac535a779b496f52b9547f95765..224551537e3b6721e0cb2c5b94f0ac39308537d0 100644 (file)
@@ -477,7 +477,7 @@ static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
  */
 static int parse_choice(struct menu_stuff *menu_stuff,
                        int is_single,
-                       struct strbuf input,
+                       struct strbuf *input,
                        int **chosen)
 {
        struct strbuf **choice_list, **ptr;
@@ -485,14 +485,14 @@ static int parse_choice(struct menu_stuff *menu_stuff,
        int i;
 
        if (is_single) {
-               choice_list = strbuf_split_max(&input, '\n', 0);
+               choice_list = strbuf_split_max(input, '\n', 0);
        } else {
-               char *p = input.buf;
+               char *p = input->buf;
                do {
                        if (*p == ',')
                                *p = ' ';
                } while (*p++);
-               choice_list = strbuf_split_max(&input, ' ', 0);
+               choice_list = strbuf_split_max(input, ' ', 0);
        }
 
        for (ptr = choice_list; *ptr; ptr++) {
@@ -630,7 +630,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
 
                nr = parse_choice(stuff,
                                  opts->flags & MENU_OPTS_SINGLETON,
-                                 choice,
+                                 &choice,
                                  &chosen);
 
                if (opts->flags & MENU_OPTS_SINGLETON) {