diff mbox series

[03/19] global: convert intentionally-leaking config strings to consts

Message ID 8f3decbb762916a536ec7a8d319c5903bd8f30c1.1716983704.git.ps@pks.im (mailing list archive)
State Superseded
Headers show
Series Compile with `-Wwrite-strings` | expand

Commit Message

Patrick Steinhardt May 29, 2024, 12:44 p.m. UTC
There are multiple cases where we intentionally leak config strings:

  - `struct gpg_format` is used to track programs that can be used for
    signing commits, either via gpg(1), gpgsm(1) or ssh-keygen(1). The
    user can override the commands via several config variables. As the
    array is populated once, only, and will never be free'd, it is fine
    to treat the program as a quasi-constant.

  - `struct ll_merge_driver` is used to track merge drivers. Same as
    with the GPG format, these drivers are populated once and then
    reused. Its data is never free'd, either.

  - `struct userdiff_funcname` and `struct userdiff_driver` can be
    configured via `diff.<driver>.*` to add additional drivers. Again,
    these have a global lifetime and are never free'd.

All of these are intentionally kept alive and never free'd. Let's mark
the respective fields as `const char *` and cast away the constness when
assigning those values.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 gpg-interface.c |  4 ++--
 merge-ll.c      | 11 ++++++++---
 userdiff.c      | 10 +++++-----
 userdiff.h      | 12 ++++++------
 4 files changed, 21 insertions(+), 16 deletions(-)

Comments

Junio C Hamano May 29, 2024, 5:28 p.m. UTC | #1
Patrick Steinhardt <ps@pks.im> writes:

> There are multiple cases where we intentionally leak config strings:
>
>   - `struct gpg_format` is used to track programs that can be used for
>     signing commits, either via gpg(1), gpgsm(1) or ssh-keygen(1). The
>     user can override the commands via several config variables. As the
>     array is populated once, only, and will never be free'd, it is fine
>     to treat the program as a quasi-constant.
>
>   - `struct ll_merge_driver` is used to track merge drivers. Same as
>     with the GPG format, these drivers are populated once and then
>     reused. Its data is never free'd, either.
>
>   - `struct userdiff_funcname` and `struct userdiff_driver` can be
>     configured via `diff.<driver>.*` to add additional drivers. Again,
>     these have a global lifetime and are never free'd.
>
> All of these are intentionally kept alive and never free'd. Let's mark
> the respective fields as `const char *` and cast away the constness when
> assigning those values.

It is not unclear where the linkage between "not freed" and "must be
const" comes from.  What am I missing?

Thanks.
Patrick Steinhardt May 30, 2024, 11:30 a.m. UTC | #2
On Wed, May 29, 2024 at 10:28:05AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > There are multiple cases where we intentionally leak config strings:
> >
> >   - `struct gpg_format` is used to track programs that can be used for
> >     signing commits, either via gpg(1), gpgsm(1) or ssh-keygen(1). The
> >     user can override the commands via several config variables. As the
> >     array is populated once, only, and will never be free'd, it is fine
> >     to treat the program as a quasi-constant.
> >
> >   - `struct ll_merge_driver` is used to track merge drivers. Same as
> >     with the GPG format, these drivers are populated once and then
> >     reused. Its data is never free'd, either.
> >
> >   - `struct userdiff_funcname` and `struct userdiff_driver` can be
> >     configured via `diff.<driver>.*` to add additional drivers. Again,
> >     these have a global lifetime and are never free'd.
> >
> > All of these are intentionally kept alive and never free'd. Let's mark
> > the respective fields as `const char *` and cast away the constness when
> > assigning those values.
> 
> It is not unclear where the linkage between "not freed" and "must be
> const" comes from.  What am I missing?

It comes from `-Wwrite-strings`, which will mark string constants as
`const char *`. This will cause warnings in all of the above cases
because the fields are being assigned constants, but those fields are
currently `char *`. Will clarify.

Patrick
Junio C Hamano May 30, 2024, 4 p.m. UTC | #3
Patrick Steinhardt <ps@pks.im> writes:

> On Wed, May 29, 2024 at 10:28:05AM -0700, Junio C Hamano wrote:
>> Patrick Steinhardt <ps@pks.im> writes:
>> 
>> > There are multiple cases where we intentionally leak config strings:
>> >
>> >   - `struct gpg_format` is used to track programs that can be used for
>> >     signing commits, either via gpg(1), gpgsm(1) or ssh-keygen(1). The
>> >     user can override the commands via several config variables. As the
>> >     array is populated once, only, and will never be free'd, it is fine
>> >     to treat the program as a quasi-constant.
>> >
>> >   - `struct ll_merge_driver` is used to track merge drivers. Same as
>> >     with the GPG format, these drivers are populated once and then
>> >     reused. Its data is never free'd, either.
>> >
>> >   - `struct userdiff_funcname` and `struct userdiff_driver` can be
>> >     configured via `diff.<driver>.*` to add additional drivers. Again,
>> >     these have a global lifetime and are never free'd.
>> >
>> > All of these are intentionally kept alive and never free'd. Let's mark
>> > the respective fields as `const char *` and cast away the constness when
>> > assigning those values.
>> 
>> It is not unclear where the linkage between "not freed" and "must be
>> const" comes from.  What am I missing?
>
> It comes from `-Wwrite-strings`, which will mark string constants as
> `const char *`. This will cause warnings in all of the above cases
> because the fields are being assigned constants, but those fields are
> currently `char *`. Will clarify.

In short, these warnings have nothing to do with the pointee by
these variables or struct members are eventually freed or not, no?
diff mbox series

Patch

diff --git a/gpg-interface.c b/gpg-interface.c
index 71a9382a61..5c824aeb25 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -34,7 +34,7 @@  static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
 
 struct gpg_format {
 	const char *name;
-	char *program;
+	const char *program;
 	const char **verify_args;
 	const char **sigs;
 	int (*verify_signed_buffer)(struct signature_check *sigc,
@@ -783,7 +783,7 @@  static int git_gpg_config(const char *var, const char *value,
 
 	if (fmtname) {
 		fmt = get_format_by_name(fmtname);
-		return git_config_string(&fmt->program, var, value);
+		return git_config_string((char **) &fmt->program, var, value);
 	}
 
 	return 0;
diff --git a/merge-ll.c b/merge-ll.c
index e29b15fa4a..180c19df67 100644
--- a/merge-ll.c
+++ b/merge-ll.c
@@ -27,7 +27,7 @@  typedef enum ll_merge_result (*ll_merge_fn)(const struct ll_merge_driver *,
 
 struct ll_merge_driver {
 	const char *name;
-	char *description;
+	const char *description;
 	ll_merge_fn fn;
 	char *recursive;
 	struct ll_merge_driver *next;
@@ -304,8 +304,13 @@  static int read_merge_config(const char *var, const char *value,
 		ll_user_merge_tail = &(fn->next);
 	}
 
-	if (!strcmp("name", key))
-		return git_config_string(&fn->description, var, value);
+	if (!strcmp("name", key)) {
+		/*
+		 * The description is leaking, but that's okay as we want to
+		 * keep around the merge drivers anyway.
+		 */
+		return git_config_string((char **) &fn->description, var, value);
+	}
 
 	if (!strcmp("driver", key)) {
 		if (!value)
diff --git a/userdiff.c b/userdiff.c
index 82bc76b910..371032a413 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -399,7 +399,7 @@  static struct userdiff_driver *userdiff_find_by_namelen(const char *name, size_t
 static int parse_funcname(struct userdiff_funcname *f, const char *k,
 		const char *v, int cflags)
 {
-	if (git_config_string(&f->pattern, k, v) < 0)
+	if (git_config_string((char **) &f->pattern, k, v) < 0)
 		return -1;
 	f->cflags = cflags;
 	return 0;
@@ -445,15 +445,15 @@  int userdiff_config(const char *k, const char *v)
 	if (!strcmp(type, "binary"))
 		return parse_tristate(&drv->binary, k, v);
 	if (!strcmp(type, "command"))
-		return git_config_string(&drv->external, k, v);
+		return git_config_string((char **) &drv->external, k, v);
 	if (!strcmp(type, "textconv"))
-		return git_config_string(&drv->textconv, k, v);
+		return git_config_string((char **) &drv->textconv, k, v);
 	if (!strcmp(type, "cachetextconv"))
 		return parse_bool(&drv->textconv_want_cache, k, v);
 	if (!strcmp(type, "wordregex"))
-		return git_config_string(&drv->word_regex, k, v);
+		return git_config_string((char **) &drv->word_regex, k, v);
 	if (!strcmp(type, "algorithm"))
-		return git_config_string(&drv->algorithm, k, v);
+		return git_config_string((char **) &drv->algorithm, k, v);
 
 	return 0;
 }
diff --git a/userdiff.h b/userdiff.h
index cc8e5abfef..d726804c3e 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -7,19 +7,19 @@  struct index_state;
 struct repository;
 
 struct userdiff_funcname {
-	char *pattern;
+	const char *pattern;
 	int cflags;
 };
 
 struct userdiff_driver {
 	const char *name;
-	char *external;
-	char *algorithm;
+	const char *external;
+	const char *algorithm;
 	int binary;
 	struct userdiff_funcname funcname;
-	char *word_regex;
-	char *word_regex_multi_byte;
-	char *textconv;
+	const char *word_regex;
+	const char *word_regex_multi_byte;
+	const char *textconv;
 	struct notes_cache *textconv_cache;
 	int textconv_want_cache;
 };