diff mbox series

[08/22] config: fix leaking comment character config

Message ID a34c90a5527cb45ec89a0ad44dbca1d61705a0ea.1722933642.git.ps@pks.im (mailing list archive)
State Superseded
Headers show
Series Memory leak fixes (pt.4) | expand

Commit Message

Patrick Steinhardt Aug. 6, 2024, 9 a.m. UTC
When the comment line character has been specified multiple times in the
configuration, then `git_default_core_config()` will cause a memory leak
because it unconditionally copies the string into `comment_line_str`
without free'ing the previous value. In fact, it can't easily free the
value in the first place because it may contain a string constant.

Refactor the code so that we initialize the value with another array.
This allows us to free the value in case the string is not pointing to
that constant array anymore.

This memory leak is being hit in t3404. As there are still other memory
leaks in that file we cannot yet mark it as passing with leak checking
enabled.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.c      | 2 ++
 environment.c | 3 ++-
 environment.h | 1 +
 3 files changed, 5 insertions(+), 1 deletion(-)

Comments

James Liu Aug. 7, 2024, 7:11 a.m. UTC | #1
On Tue Aug 6, 2024 at 7:00 PM AEST, Patrick Steinhardt wrote:
> Refactor the code so that we initialize the value with another array.
> This allows us to free the value in case the string is not pointing to
> that constant array anymore.
>
> diff --git a/environment.c b/environment.c
> index 5cea2c9f54..8297c6e37b 100644
> --- a/environment.c
> +++ b/environment.c
> @@ -113,7 +113,8 @@ int protect_ntfs = PROTECT_NTFS_DEFAULT;
>   * The character that begins a commented line in user-editable file
>   * that is subject to stripspace.
>   */
> -const char *comment_line_str = "#";
> +const char comment_line_str_default[] = "#";
> +const char *comment_line_str = comment_line_str_default;
>  int auto_comment_line_char;
>  
>  /* Parallel index stat data preload? */

Is my understanding correct that `comment_line_str` is now just a
pointer to the `comment_line_str_default` array, and thus can be freed
once we're done with it?
Patrick Steinhardt Aug. 8, 2024, 5:04 a.m. UTC | #2
On Wed, Aug 07, 2024 at 05:11:28PM +1000, James Liu wrote:
> On Tue Aug 6, 2024 at 7:00 PM AEST, Patrick Steinhardt wrote:
> > Refactor the code so that we initialize the value with another array.
> > This allows us to free the value in case the string is not pointing to
> > that constant array anymore.
> >
> > diff --git a/environment.c b/environment.c
> > index 5cea2c9f54..8297c6e37b 100644
> > --- a/environment.c
> > +++ b/environment.c
> > @@ -113,7 +113,8 @@ int protect_ntfs = PROTECT_NTFS_DEFAULT;
> >   * The character that begins a commented line in user-editable file
> >   * that is subject to stripspace.
> >   */
> > -const char *comment_line_str = "#";
> > +const char comment_line_str_default[] = "#";
> > +const char *comment_line_str = comment_line_str_default;
> >  int auto_comment_line_char;
> >  
> >  /* Parallel index stat data preload? */
> 
> Is my understanding correct that `comment_line_str` is now just a
> pointer to the `comment_line_str_default` array, and thus can be freed
> once we're done with it?

Not quite. By default, `comment_line_str` also points to
comment_line_str_default`, which is a string constant and thus neither
of these variables can be free'd. But what this split allows us to do is
to check whether `comment_line_str` has changed from the default, and
thus we can conditionall free it when it does not point to the default
value anymore.

Now that I revisit this commit I'm not quite happy with it anymore. We
still need to have the cast, which is somewhat awkward. I think the
better solution is to instead have a `comment_line_str_allocated`
variable that is non-constant. I'll adapt the code accordingly.

An even better solution would be to have `struct strbuf` provide an
initializer that populates it with a string constant. But that feels
like a larger undertaking, so I'll leave that for the future.

Patrick
Junio C Hamano Aug. 8, 2024, 3:54 p.m. UTC | #3
Patrick Steinhardt <ps@pks.im> writes:

> Now that I revisit this commit I'm not quite happy with it anymore. We
> still need to have the cast, which is somewhat awkward. I think the
> better solution is to instead have a `comment_line_str_allocated`
> variable that is non-constant. I'll adapt the code accordingly.
>
> An even better solution would be to have `struct strbuf` provide an
> initializer that populates it with a string constant. But that feels
> like a larger undertaking, so I'll leave that for the future.

FWIW, I found the "now we have a variable to refer to the address of
the string constant, we can compare to detect if we allocated and
need to free" in this round is a good place to stop.

I view the approach to use an auxiliary variable *_allocated is a
regression compared to what we see here.  The approach makes it easy
to forget to futz it when an allocated piece of memory is assigned
to the main variable.
diff mbox series

Patch

diff --git a/config.c b/config.c
index 6421894614..63e0211c7d 100644
--- a/config.c
+++ b/config.c
@@ -1596,6 +1596,8 @@  static int git_default_core_config(const char *var, const char *value,
 		else if (value[0]) {
 			if (strchr(value, '\n'))
 				return error(_("%s cannot contain newline"), var);
+			if (comment_line_str != comment_line_str_default)
+				free((char *) comment_line_str);
 			comment_line_str = xstrdup(value);
 			auto_comment_line_char = 0;
 		} else
diff --git a/environment.c b/environment.c
index 5cea2c9f54..8297c6e37b 100644
--- a/environment.c
+++ b/environment.c
@@ -113,7 +113,8 @@  int protect_ntfs = PROTECT_NTFS_DEFAULT;
  * The character that begins a commented line in user-editable file
  * that is subject to stripspace.
  */
-const char *comment_line_str = "#";
+const char comment_line_str_default[] = "#";
+const char *comment_line_str = comment_line_str_default;
 int auto_comment_line_char;
 
 /* Parallel index stat data preload? */
diff --git a/environment.h b/environment.h
index e9f01d4d11..5e5d9a8045 100644
--- a/environment.h
+++ b/environment.h
@@ -8,6 +8,7 @@  struct strvec;
  * The character that begins a commented line in user-editable file
  * that is subject to stripspace.
  */
+extern const char comment_line_str_default[];
 extern const char *comment_line_str;
 extern int auto_comment_line_char;