git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/19] Compile with `-Wwrite-strings`
@ 2024-05-29 12:44 Patrick Steinhardt
  2024-05-29 12:44 ` [PATCH 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
                   ` (24 more replies)
  0 siblings, 25 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 6502 bytes --]

Hi,

there were some recent discussions about compiler warnings and how to
stay on top of breaking changes in compilers in general [1] and about
string constants in particular [2]. This made me look into what kind of
warnings we should reasonably enable, which led me to the following
list of warnings that may be sensible:

  - `-Wformat-nonliteral` to warn about non-constant strings being
    passed as format string.

  - `-Wwrite-strings` to warn about string constants being assigned to a
    non-constant variable.

  - `-Wredundant-decls` to warn about redundant declarations.

  - `-Wconversion` to warn about implicit integer casts when they may
    alter the value.

This patch series adapts our code to compile with `-Wwrite-strings`.
This option will change the type of string constants from `char []` to
`const char []` such that it is now invalid to assign it to non-const
variables without a cast. The intent is to avoid undefined behaviour
when accedintally writing to such strings and to avoid free'ing such a
variable.

There are quite some cases where we mishandle this. Oftentimes we just
didn't bother to free any memory at all, which made it a non-issue in
the first place. Other times we had some special logic that prevents
writing or freeing such strings. But in most cases it was an accident
waiting to happen.

Even though the changes are quite invasive, I think that this is a step
into the right direction. Many of the constructs feel quite fragile, and
most of those get fixed in this series. Some others I just paper over,
for example when assigning to structures with global lifetime where we
know that they are never released at all.

I also have a patch series cooking for `-Wredundant-decls`. But while
that warning detects some redundant declarations indeed, it creates a
problem with `extern char **environ`. There is no header for it and
programs are asked to declare it by themselves. But of course, some libc
implementations disagree and declare it. I haven't found a nice way to
work around this issue, but may send the patches that drop the redundant
declarations nonetheless.

The other two warnings I haven't yet looked into.

I ran some test jobs on both GitHub [3] and GitLab [4] to verify that
the result is sane.

Thanks!

Patrick

[1]: <xmqqbk5bim2n.fsf@gitster.g>
[2]: <20240525043347.GA1895047@coredump.intra.peff.net>
[3]: https://github.com/git/git/pull/1730
[4]: https://gitlab.com/gitlab-org/git/-/pipelines/1310156791

Patrick Steinhardt (19):
  global: improve const correctness when assigning string constants
  global: assign non-const strings as required
  global: convert intentionally-leaking config strings to consts
  compat/win32: fix const-correctness with string constants
  reftable: improve const correctness when assigning string constants
  refspec: remove global tag refspec structure
  http: do not assign string constant to non-const field
  line-log: always allocate the output prefix
  object-file: make `buf` parameter of `index_mem()` a constant
  parse-options: cast long name for OPTION_ALIAS
  send-pack: always allocate receive status
  remote-curl: avoid assigning string constant to non-const variable
  revision: always store allocated strings in output encoding
  mailmap: always store allocated strings in mailmap blob
  imap-send: drop global `imap_server_conf` variable
  imap-send: fix leaking memory in `imap_server_conf`
  builtin/rebase: adapt code to not assign string constants to non-const
  builtin/merge: always store allocated strings in `pull_twohead`
  config.mak.dev: enable `-Wwrite-strings` warning

 builtin/bisect.c             |   3 +-
 builtin/blame.c              |   2 +-
 builtin/bugreport.c          |   2 +-
 builtin/check-ignore.c       |   4 +-
 builtin/clone.c              |  14 ++--
 builtin/commit.c             |   6 +-
 builtin/diagnose.c           |   2 +-
 builtin/fetch.c              |  11 ++-
 builtin/log.c                |   2 +-
 builtin/mailsplit.c          |   4 +-
 builtin/merge.c              |  18 +++--
 builtin/pull.c               |  52 +++++++-------
 builtin/rebase.c             |  16 +++--
 builtin/receive-pack.c       |   4 +-
 builtin/remote.c             |   3 +-
 builtin/revert.c             |   2 +-
 builtin/send-pack.c          |   2 +
 compat/basename.c            |  15 +++-
 compat/mingw.c               |  25 +++----
 compat/regex/regcomp.c       |   2 +-
 compat/winansi.c             |   2 +-
 config.mak.dev               |   1 +
 diff.c                       |   7 +-
 diffcore-rename.c            |   6 +-
 entry.c                      |   7 +-
 fmt-merge-msg.c              |   2 +-
 fsck.c                       |   2 +-
 fsck.h                       |   2 +-
 gpg-interface.c              |   6 +-
 http-backend.c               |   2 +-
 http.c                       |   5 +-
 ident.c                      |   9 ++-
 imap-send.c                  | 133 ++++++++++++++++++++---------------
 line-log.c                   |  21 +++---
 mailmap.c                    |   2 +-
 merge-ll.c                   |  11 ++-
 object-file.c                |  17 ++---
 parse-options.h              |   2 +-
 pretty.c                     |   7 +-
 refs.c                       |   2 +-
 refs.h                       |   2 +-
 refs/reftable-backend.c      |   5 +-
 refspec.c                    |  13 ----
 refspec.h                    |   1 -
 reftable/basics_test.c       |   4 +-
 reftable/block_test.c        |   2 +-
 reftable/merged_test.c       |  45 ++++++------
 reftable/readwrite_test.c    |  47 +++++++------
 reftable/record.c            |   6 +-
 reftable/stack_test.c        |  65 ++++++++---------
 remote-curl.c                |  58 ++++++++-------
 revision.c                   |   3 +-
 run-command.c                |   2 +-
 send-pack.c                  |   2 +-
 t/helper/test-hashmap.c      |   3 +-
 t/helper/test-json-writer.c  |  10 +--
 t/helper/test-regex.c        |   4 +-
 t/helper/test-rot13-filter.c |   5 +-
 t/t3900-i18n-commit.sh       |   1 +
 t/t3901-i18n-patch.sh        |   1 +
 t/unit-tests/t-strbuf.c      |  10 +--
 trailer.c                    |   2 +-
 userdiff.c                   |  10 +--
 userdiff.h                   |  12 ++--
 wt-status.c                  |   2 +-
 65 files changed, 410 insertions(+), 340 deletions(-)

-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH 01/19] global: improve const correctness when assigning string constants
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 16:58   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 02/19] global: assign non-const strings as required Patrick Steinhardt
                   ` (23 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 23139 bytes --]

We're about to enable `-Wwrite-strings`, which changes the type of
string constants to `const char[]`. Fix various sites where we assign
such constants to non-const variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/bisect.c             |  3 ++-
 builtin/blame.c              |  2 +-
 builtin/bugreport.c          |  2 +-
 builtin/check-ignore.c       |  4 +--
 builtin/clone.c              |  6 ++---
 builtin/commit.c             |  6 ++---
 builtin/diagnose.c           |  2 +-
 builtin/log.c                |  2 +-
 builtin/mailsplit.c          |  4 +--
 builtin/pull.c               | 52 ++++++++++++++++++------------------
 builtin/receive-pack.c       |  4 +--
 builtin/revert.c             |  2 +-
 compat/regex/regcomp.c       |  2 +-
 diff.c                       |  4 +--
 diffcore-rename.c            |  6 ++---
 fmt-merge-msg.c              |  2 +-
 fsck.c                       |  2 +-
 fsck.h                       |  2 +-
 gpg-interface.c              |  2 +-
 http-backend.c               |  2 +-
 imap-send.c                  |  6 ++---
 pretty.c                     |  2 +-
 refs.c                       |  2 +-
 refs.h                       |  2 +-
 reftable/record.c            |  6 ++---
 run-command.c                |  2 +-
 t/helper/test-hashmap.c      |  3 ++-
 t/helper/test-json-writer.c  | 10 +++----
 t/helper/test-regex.c        |  4 +--
 t/helper/test-rot13-filter.c |  5 ++--
 t/unit-tests/t-strbuf.c      | 10 ++++---
 trailer.c                    |  2 +-
 wt-status.c                  |  2 +-
 33 files changed, 86 insertions(+), 81 deletions(-)

diff --git a/builtin/bisect.c b/builtin/bisect.c
index a58432b9d9..dabce9b542 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
 	return bisect_clean_state();
 }
 
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+		       const char *fmt, const char *state,
 		       struct commit *commit)
 {
 	struct pretty_print_context pp = {0};
diff --git a/builtin/blame.c b/builtin/blame.c
index 838cd476be..98c7629b6a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
-	char *tmp, *endp;
+	const char *tmp, *endp;
 	const char *namebuf, *mailbuf;
 
 	tmp = strstr(inbuf, what);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 25f860a0d9..b3cc77af53 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode diagnose = DIAGNOSE_NONE;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	const char *user_relative_path = NULL;
 	char *prefixed_filename;
 	size_t output_path_len;
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 6c43430ec4..2bda6a1d46 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -35,8 +35,8 @@ static const struct option check_ignore_options[] = {
 
 static void output_pattern(const char *path, struct path_pattern *pattern)
 {
-	char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
-	char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+	const char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
+	const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
 	if (!nul_term_line) {
 		if (!verbose) {
 			write_name_quoted(path, stdout, '\n');
diff --git a/builtin/clone.c b/builtin/clone.c
index 23993b905b..92ab7d7165 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -71,7 +71,7 @@ static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
 static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
 static int option_sparse_checkout;
@@ -177,8 +177,8 @@ static struct option builtin_clone_options[] = {
 
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
-	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
-	static char *bundle_suffix[] = { ".bundle", "" };
+	static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static const char *bundle_suffix[] = { ".bundle", "" };
 	size_t baselen = path->len;
 	struct stat st;
 	int i;
diff --git a/builtin/commit.c b/builtin/commit.c
index f53e7e86ff..75c741173e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,7 +113,7 @@ static char *template_file;
  * the commit message and/or authorship.
  */
 static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
 static char *fixup_message, *fixup_commit, *squash_message;
 static const char *fixup_prefix;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +121,8 @@ static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
 static struct strvec trailer_args = STRVEC_INIT;
 
 /*
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2b55..4857a4395b 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode mode = DIAGNOSE_STATS;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	char *prefixed_filename;
 
 	const struct option diagnose_options[] = {
diff --git a/builtin/log.c b/builtin/log.c
index 78a247d8a9..b8846a9458 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1283,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 	o2->flags = flags2;
 }
 
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb8ae..fe6dbc5d05 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
 	DIR *dir;
 	struct dirent *dent;
 	char *name = NULL;
-	char *subs[] = { "cur", "new", NULL };
-	char **sub;
+	const char *subs[] = { "cur", "new", NULL };
+	const char **sub;
 	int ret = -1;
 
 	for (sub = subs; *sub; ++sub) {
diff --git a/builtin/pull.c b/builtin/pull.c
index d622202bce..2d0429f14f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
 
 /* Shared options */
 static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
 
 /* Options passed to git-merge or git-rebase */
 static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
 static int opt_autostash = -1;
 static int config_autostash;
 static int check_trust_level = 1;
 static struct strvec opt_strategies = STRVEC_INIT;
 static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
 static int opt_allow_unrelated_histories;
 
 /* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
 static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
 static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
 static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
 static struct strvec opt_fetch = STRVEC_INIT;
 
 static struct option pull_options[] = {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 01c1f04ece..c8d12ee0a7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1249,7 +1249,7 @@ static int run_proc_receive_hook(struct command *commands,
 	return code;
 }
 
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
 	N_("By default, updating the current branch in a non-bare repository\n"
 	   "is denied, because it will make the index and work tree inconsistent\n"
 	   "with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
 	rp_error("%s", _(refuse_unconfigured_deny_msg));
 }
 
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
 	N_("By default, deleting the current branch is denied, because the next\n"
 	   "'git clone' won't result in any file checked out, causing confusion.\n"
 	   "\n"
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2c68..7bf2b4e11d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 
 	/* Check for incompatible command line arguments */
 	if (cmd) {
-		char *this_operation;
+		const char *this_operation;
 		if (cmd == 'q')
 			this_operation = "--quit";
 		else if (cmd == 'c')
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 2bc0f1187a..6c5d455e92 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
   unsigned int table_size;
 #ifndef _LIBC
-  char *codeset_name;
+  const char *codeset_name;
 #endif
 
   memset (dfa, '\0', sizeof (re_dfa_t));
diff --git a/diff.c b/diff.c
index e70301df76..ffd867ef6c 100644
--- a/diff.c
+++ b/diff.c
@@ -3764,7 +3764,7 @@ static void builtin_diff(const char *name_a,
 	return;
 }
 
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 {
 	if (!is_renamed) {
 		if (p->status == DIFF_STATUS_ADDED) {
@@ -4076,7 +4076,7 @@ static int reuse_worktree_file(struct index_state *istate,
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
 	struct strbuf buf = STRBUF_INIT;
-	char *dirty = "";
+	const char *dirty = "";
 
 	/* Are we looking at the work tree? */
 	if (s->dirty_submodule)
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bcac7..0e1adb87df 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -406,7 +406,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
 	return highest_destination_dir;
 }
 
-static char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
 
 static int dir_rename_already_determinable(struct strintmap *counts)
 {
@@ -429,8 +429,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
 }
 
 static void increment_count(struct dir_rename_info *info,
-			    char *old_dir,
-			    char *new_dir)
+			    const char *old_dir,
+			    const char *new_dir)
 {
 	struct strintmap *counts;
 	struct strmap_entry *e;
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 7d144b803a..5af63ab5ab 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -447,7 +447,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
 				const char *current_branch)
 {
 	int i = 0;
-	char *sep = "";
+	const char *sep = "";
 
 	strbuf_addstr(out, "Merge ");
 	for (i = 0; i < srcs.nr; i++) {
diff --git a/fsck.c b/fsck.c
index 7dff41413e..61cd48aa25 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1231,7 +1231,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 }
 
 int fsck_buffer(const struct object_id *oid, enum object_type type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options)
 {
 	if (type == OBJ_BLOB)
diff --git a/fsck.h b/fsck.h
index 17fa2dda5d..4f0c4e6479 100644
--- a/fsck.h
+++ b/fsck.h
@@ -202,7 +202,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
  * struct.
  */
 int fsck_buffer(const struct object_id *oid, enum object_type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options);
 
 /*
diff --git a/gpg-interface.c b/gpg-interface.c
index 5193223714..71a9382a61 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -727,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
 			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
-	char *fmtname = NULL;
+	const char *fmtname = NULL;
 	char *trust;
 	int ret;
 
diff --git a/http-backend.c b/http-backend.c
index 5b65287ac9..5b4dca65ed 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -753,7 +753,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
 
 int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
-	char *method = getenv("REQUEST_METHOD");
+	const char *method = getenv("REQUEST_METHOD");
 	const char *proto_header;
 	char *dir;
 	struct service_cmd *cmd = NULL;
diff --git a/imap-send.c b/imap-send.c
index a5d1510180..8b723b34a5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1215,9 +1215,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
 static void wrap_in_html(struct strbuf *msg)
 {
 	struct strbuf buf = STRBUF_INIT;
-	static char *content_type = "Content-Type: text/html;\n";
-	static char *pre_open = "<pre>\n";
-	static char *pre_close = "</pre>\n";
+	static const char *content_type = "Content-Type: text/html;\n";
+	static const char *pre_open = "<pre>\n";
+	static const char *pre_close = "</pre>\n";
 	const char *body = strstr(msg->buf, "\n\n");
 
 	if (!body)
diff --git a/pretty.c b/pretty.c
index 22a81506b7..ec05db5655 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1325,7 +1325,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
 static size_t parse_describe_args(const char *start, struct strvec *args)
 {
 	struct {
-		char *name;
+		const char *name;
 		enum {
 			DESCRIBE_ARG_BOOL,
 			DESCRIBE_ARG_INTEGER,
diff --git a/refs.c b/refs.c
index 8260c27cde..292e8d947e 100644
--- a/refs.c
+++ b/refs.c
@@ -159,7 +159,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
 {
 	struct ref_namespace_info *info = &ref_namespace[namespace];
 	if (info->ref_updated)
-		free(info->ref);
+		free((char *)info->ref);
 	info->ref = ref;
 	info->ref_updated = 1;
 }
diff --git a/refs.h b/refs.h
index 34568ee1fb..923f751d18 100644
--- a/refs.h
+++ b/refs.h
@@ -975,7 +975,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
  */
 
 struct ref_namespace_info {
-	char *ref;
+	const char *ref;
 	enum decoration_type decoration;
 
 	/*
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e913..a2cba5ef74 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
 	return start_len - in.len;
 }
 
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
 {
 	struct string_view start = s;
 	int l = strlen(str);
@@ -969,9 +969,9 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
 	return REFTABLE_FORMAT_ERROR;
 }
 
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
 {
-	char *empty = "";
+	const char *empty = "";
 	if (!a)
 		a = empty;
 
diff --git a/run-command.c b/run-command.c
index 1b821042b4..7600531fb6 100644
--- a/run-command.c
+++ b/run-command.c
@@ -663,7 +663,7 @@ int start_command(struct child_process *cmd)
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
 	int failed_errno;
-	char *str;
+	const char *str;
 
 	/*
 	 * In case of errors we must keep the promise to close FDs
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d49c..2912899558 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
 }
 
 static struct test_entry *alloc_test_entry(unsigned int hash,
-					   char *key, char *value)
+					   const char *key,
+					   const char *value)
 {
 	size_t klen = strlen(key);
 	size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f597..ed52eb76bf 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
 	jw_end(&arr4);
 }
 
-static char *expect_nest1 =
+static const char *expect_nest1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
 static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
 	jw_release(&arr1);
 }
 
-static char *expect_inline1 =
+static const char *expect_inline1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
 	("{\n"
 	 "  \"obj1\": {\n"
 	 "    \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
 	jw_end(&inline1);
 }
 
-static char *expect_inline2 =
+static const char *expect_inline2 =
 	"[[1,2],[3,4],{\"a\":\"abc\"}]";
 
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
 	("[\n"
 	 "  [\n"
 	 "    1,\n"
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042eafc2..366bd70976 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
 
 static int test_regex_bug(void)
 {
-	char *pat = "[^={} \t]+";
-	char *str = "={}\nfred";
+	const char *pat = "[^={} \t]+";
+	const char *str = "={}\nfred";
 	regex_t r;
 	regmatch_t m[1];
 
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c622..7e1d9e0ee4 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
 	strmap_clear(&delay, 0);
 }
 
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
 {
 	struct delay_entry *entry = xcalloc(1, sizeof(*entry));
 	entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
 static void command_loop(void)
 {
 	for (;;) {
-		char *buf, *output;
+		char *buf;
+		const char *output;
 		char *pathname;
 		struct delay_entry *entry;
 		struct strbuf input = STRBUF_INIT;
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4441..6027dafef7 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
 #include "strbuf.h"
 
 /* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+		  const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
 }
 
 /* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+			    const char *init_str, const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
 	strbuf_release(&buf);
 }
 
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
 {
 	const char *p_ch = data;
 	const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
 	check_char(buf->buf[buf->len], ==, '\0');
 }
 
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
 {
 	const char *text = data;
 	size_t len = strlen(text);
diff --git a/trailer.c b/trailer.c
index 2bcb9ba8f7..72e5136c73 100644
--- a/trailer.c
+++ b/trailer.c
@@ -63,7 +63,7 @@ struct arg_item {
 
 static LIST_HEAD(conf_head);
 
-static char *separators = ":";
+static const char *separators = ":";
 
 static int configured;
 
diff --git a/wt-status.c b/wt-status.c
index ff4be071ca..7912545e4e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2408,7 +2408,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
 		int mode;
 		struct object_id oid;
 	} stages[3];
-	char *key;
+	const char *key;
 	char submodule_token[5];
 	char unmerged_prefix = 'u';
 	char eol_char = s->null_termination ? '\0' : '\n';
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 02/19] global: assign non-const strings as required
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
  2024-05-29 12:44 ` [PATCH 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 17:25   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 03/19] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 6556 bytes --]

There are several cases where we initialize non-const fields with string
constants. This is invalid and will cause warnings once we enable the
`-Wwrite-strings` warning. Adapt those cases to instead use string
arrays.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/remote.c        | 3 ++-
 diff.c                  | 3 ++-
 entry.c                 | 7 ++++---
 ident.c                 | 9 +++++++--
 line-log.c              | 3 ++-
 object-file.c           | 3 ++-
 pretty.c                | 5 +++--
 refs/reftable-backend.c | 5 +++--
 8 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/builtin/remote.c b/builtin/remote.c
index d52b1c0e10..0324a5d48d 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -494,11 +494,12 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 	struct ref *ref, *matches;
 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
 	struct refspec_item refspec;
+	char refspec_str[] = "refs/heads/*";
 
 	memset(&refspec, 0, sizeof(refspec));
 	refspec.force = 0;
 	refspec.pattern = 1;
-	refspec.src = refspec.dst = "refs/heads/*";
+	refspec.src = refspec.dst = refspec_str;
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 				    fetch_map, 1);
diff --git a/diff.c b/diff.c
index ffd867ef6c..1439a5a01d 100644
--- a/diff.c
+++ b/diff.c
@@ -7231,11 +7231,12 @@ size_t fill_textconv(struct repository *r,
 		     struct diff_filespec *df,
 		     char **outbuf)
 {
+	static char empty_str[] = "";
 	size_t size;
 
 	if (!driver) {
 		if (!DIFF_FILE_VALID(df)) {
-			*outbuf = "";
+			*outbuf = empty_str;
 			return 0;
 		}
 		if (diff_populate_filespec(r, df, NULL))
diff --git a/entry.c b/entry.c
index b8c257f6f9..2fc06ac90c 100644
--- a/entry.c
+++ b/entry.c
@@ -175,6 +175,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 	struct string_list_item *filter, *path;
 	struct progress *progress = NULL;
 	struct delayed_checkout *dco = state->delayed_checkout;
+	char empty_str[] = "";
 
 	if (!state->delayed_checkout)
 		return errs;
@@ -189,7 +190,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 			if (!async_query_available_blobs(filter->string, &available_paths)) {
 				/* Filter reported an error */
 				errs = 1;
-				filter->string = "";
+				filter->string = empty_str;
 				continue;
 			}
 			if (available_paths.nr <= 0) {
@@ -199,7 +200,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 				 * filter from the list (see
 				 * "string_list_remove_empty_items" call below).
 				 */
-				filter->string = "";
+				filter->string = empty_str;
 				continue;
 			}
 
@@ -225,7 +226,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					 * Do not ask the filter for available blobs,
 					 * again, as the filter is likely buggy.
 					 */
-					filter->string = "";
+					filter->string = empty_str;
 					continue;
 				}
 				ce = index_file_exists(state->istate, path->string,
diff --git a/ident.c b/ident.c
index cc7afdbf81..df7aa42802 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,14 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
 	pw = getpwuid(getuid());
 	if (!pw) {
 		static struct passwd fallback;
-		fallback.pw_name = "unknown";
+		static char fallback_name[] = "unknown";
 #ifndef NO_GECOS_IN_PWENT
-		fallback.pw_gecos = "Unknown";
+		static char fallback_gcos[] = "Unknown";
+#endif
+
+		fallback.pw_name = fallback_name;
+#ifndef NO_GECOS_IN_PWENT
+		fallback.pw_gecos = fallback_gcos;
 #endif
 		pw = &fallback;
 		if (is_bogus)
diff --git a/line-log.c b/line-log.c
index 8ff6ccb772..d9bf2c8120 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1032,6 +1032,7 @@ static int process_diff_filepair(struct rev_info *rev,
 	struct range_set tmp;
 	struct diff_ranges diff;
 	mmfile_t file_parent, file_target;
+	char empty_str[] = "";
 
 	assert(pair->two->path);
 	while (rg) {
@@ -1056,7 +1057,7 @@ static int process_diff_filepair(struct rev_info *rev,
 		file_parent.ptr = pair->one->data;
 		file_parent.size = pair->one->size;
 	} else {
-		file_parent.ptr = "";
+		file_parent.ptr = empty_str;
 		file_parent.size = 0;
 	}
 
diff --git a/object-file.c b/object-file.c
index 610b1f465c..c9e374e57e 100644
--- a/object-file.c
+++ b/object-file.c
@@ -282,12 +282,13 @@ static struct cached_object {
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
 
+static char empty_tree_buf[] = "";
 static struct cached_object empty_tree = {
 	.oid = {
 		.hash = EMPTY_TREE_SHA1_BIN_LITERAL,
 	},
 	.type = OBJ_TREE,
-	.buf = "",
+	.buf = empty_tree_buf,
 };
 
 static struct cached_object *find_cached_object(const struct object_id *oid)
diff --git a/pretty.c b/pretty.c
index ec05db5655..1a0030b32a 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1583,9 +1583,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 		return 1;
 	case 'D':
 		{
+			char empty_str[] = "";
 			const struct decoration_options opts = {
-				.prefix = "",
-				.suffix = ""
+				.prefix = empty_str,
+				.suffix = empty_str,
 			};
 
 			format_decorations(sb, commit, c->auto_color, &opts);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1af86bbdec..1908e74dea 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1285,6 +1285,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	struct strbuf errbuf = STRBUF_INIT;
 	size_t logs_nr = 0, logs_alloc = 0, i;
 	const char *committer_info;
+	char head[] = "HEAD";
 	int ret;
 
 	committer_info = git_committer_info(0);
@@ -1387,7 +1388,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (append_head_reflog) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			logs[logs_nr] = logs[logs_nr - 1];
-			logs[logs_nr].refname = "HEAD";
+			logs[logs_nr].refname = head;
 			logs_nr++;
 		}
 	}
@@ -1463,7 +1464,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	string_list_clear(&skip, 0);
 	strbuf_release(&errbuf);
 	for (i = 0; i < logs_nr; i++) {
-		if (!strcmp(logs[i].refname, "HEAD"))
+		if (logs[i].refname == head)
 			continue;
 		logs[i].refname = NULL;
 		reftable_log_record_release(&logs[i]);
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 03/19] global: convert intentionally-leaking config strings to consts
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
  2024-05-29 12:44 ` [PATCH 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
  2024-05-29 12:44 ` [PATCH 02/19] global: assign non-const strings as required Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 17:28   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 04/19] compat/win32: fix const-correctness with string constants Patrick Steinhardt
                   ` (21 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 4886 bytes --]

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(-)

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;
 };
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 04/19] compat/win32: fix const-correctness with string constants
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (2 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 03/19] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 12:44 ` [PATCH 05/19] reftable: improve const correctness when assigning " Patrick Steinhardt
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 4104 bytes --]

Adjust various places in our Win32 compatibility layer where we are not
assigning string constants to `const char *` variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 compat/basename.c | 15 +++++++++++++--
 compat/mingw.c    | 25 +++++++++++++------------
 compat/winansi.c  |  2 +-
 3 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/compat/basename.c b/compat/basename.c
index 96bd9533b4..c3c9d65fac 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -1,6 +1,13 @@
 #include "../git-compat-util.h"
 #include "../strbuf.h"
 
+/*
+ * Both basename(3P) and dirname(3P) are mis-specified because they return a
+ * non-constant pointer even though it is specified that they may return a
+ * pointer to internal memory. This variable here is a result of that.
+ */
+static char current_directory[] = ".";
+
 /* Adapted from libiberty's basename.c.  */
 char *gitbasename (char *path)
 {
@@ -10,7 +17,7 @@ char *gitbasename (char *path)
 		skip_dos_drive_prefix(&path);
 
 	if (!path || !*path)
-		return ".";
+		return current_directory;
 
 	for (base = path; *path; path++) {
 		if (!is_dir_sep(*path))
@@ -33,8 +40,12 @@ char *gitdirname(char *path)
 	char *p = path, *slash = NULL, c;
 	int dos_drive_prefix;
 
+	/*
+	 * Same here, dirname(3P) is broken because it returns a non-constant
+	 * pointer that may point to internal memory.
+	 */
 	if (!p)
-		return ".";
+		return current_directory;
 
 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
 		goto dot;
diff --git a/compat/mingw.c b/compat/mingw.c
index 6b06ea540f..60f0986f76 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2257,6 +2257,7 @@ struct passwd *getpwuid(int uid)
 {
 	static unsigned initialized;
 	static char user_name[100];
+	static char unknown[] = "unknown";
 	static struct passwd *p;
 	wchar_t buf[100];
 	DWORD len;
@@ -2279,7 +2280,7 @@ struct passwd *getpwuid(int uid)
 	p->pw_name = user_name;
 	p->pw_gecos = get_extended_user_info(NameDisplay);
 	if (!p->pw_gecos)
-		p->pw_gecos = "unknown";
+		p->pw_gecos = unknown;
 	p->pw_dir = NULL;
 
 	initialized = 1;
@@ -2800,16 +2801,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 			strbuf_addf(report, "'%s' is on a file system that does "
 				    "not record ownership\n", path);
 		} else if (report) {
-			LPSTR str1, str2, str3, str4, to_free1 = NULL,
-			    to_free3 = NULL, to_local_free2 = NULL,
-			    to_local_free4 = NULL;
+			PCSTR str1, str2, str3, str4;
+			LPSTR to_free1 = NULL, to_free3 = NULL,
+			    to_local_free2 = NULL, to_local_free4 = NULL;
 
-			if (user_sid_to_user_name(sid, &str1))
-				to_free1 = str1;
+			if (user_sid_to_user_name(sid, &to_free1))
+				str1 = to_free1;
 			else
 				str1 = "(inconvertible)";
-			if (ConvertSidToStringSidA(sid, &str2))
-				to_local_free2 = str2;
+			if (ConvertSidToStringSidA(sid, &to_local_free2))
+				str2 = to_local_free2;
 			else
 				str2 = "(inconvertible)";
 
@@ -2822,13 +2823,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 				str4 = "(invalid)";
 			} else {
 				if (user_sid_to_user_name(current_user_sid,
-							  &str3))
-					to_free3 = str3;
+							  &to_free3))
+					str3 = to_free3;
 				else
 					str3 = "(inconvertible)";
 				if (ConvertSidToStringSidA(current_user_sid,
-							   &str4))
-					to_local_free4 = str4;
+							   &to_local_free4))
+					str4 = to_local_free4;
 				else
 					str4 = "(inconvertible)";
 			}
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f684..575813bde8 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
 	/* convert utf-8 to utf-16 */
 	int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
 	if (wlen < 0) {
-		wchar_t *err = L"[invalid]";
+		const wchar_t *err = L"[invalid]";
 		WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
 		return;
 	}
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 05/19] reftable: improve const correctness when assigning string constants
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (3 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 04/19] compat/win32: fix const-correctness with string constants Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 17:43   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 06/19] refspec: remove global tag refspec structure Patrick Steinhardt
                   ` (19 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 15336 bytes --]

There are many cases in the reftable tests where we assign string
constants to non-const fields. This is harmless because we know that
those fields are only used for reading access, but will break once we
enable `-Wwrite-strings`. Add explicit casts to prepare for this.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 reftable/basics_test.c    |  4 +--
 reftable/block_test.c     |  2 +-
 reftable/merged_test.c    | 45 +++++++++++++--------------
 reftable/readwrite_test.c | 47 ++++++++++++++--------------
 reftable/stack_test.c     | 65 ++++++++++++++++++++-------------------
 5 files changed, 82 insertions(+), 81 deletions(-)

diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 997c4d9e01..af9209d535 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -58,8 +58,8 @@ static void test_binsearch(void)
 
 static void test_names_length(void)
 {
-	char *a[] = { "a", "b", NULL };
-	EXPECT(names_length(a) == 2);
+	char *names[] = { (char *)"a", (char *)"b", NULL };
+	EXPECT(names_length(names) == 2);
 }
 
 static void test_parse_names_normal(void)
diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfbc83..34c48c8091 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
 			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
-	rec.u.ref.refname = "";
+	rec.u.ref.refname = (char *)"";
 	rec.u.ref.value_type = REFTABLE_REF_DELETION;
 	n = block_writer_add(&bw, &rec);
 	EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82d1c..6ab8ea2858 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -124,13 +124,13 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 static void test_merged_between(void)
 {
 	struct reftable_ref_record r1[] = { {
-		.refname = "b",
+		.refname = (char *)"b",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *)"a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
@@ -165,38 +165,38 @@ static void test_merged(void)
 {
 	struct reftable_ref_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *)"a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "b",
+			.refname = (char *)"b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "c",
+			.refname = (char *)"c",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *)"a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
 	struct reftable_ref_record r3[] = {
 		{
-			.refname = "c",
+			.refname = (char *)"c",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 2 },
 		},
 		{
-			.refname = "d",
+			.refname = (char *)"d",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
@@ -291,46 +291,46 @@ static void test_merged_logs(void)
 {
 	struct reftable_log_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *)"a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 2 },
 				/* deletion */
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message2",
+				.name = (char *)"jane doe",
+				.email = (char *)"jane@invalid",
+				.message = (char *)"message2",
 			}
 		},
 		{
-			.refname = "a",
+			.refname = (char *)"a",
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message1",
+				.name = (char *)"jane doe",
+				.email = (char *)"jane@invalid",
+				.message = (char *)"message1",
 			}
 		},
 	};
 	struct reftable_log_record r2[] = {
 		{
-			.refname = "a",
+			.refname = (char *)"a",
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.new_hash = { 3 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message3",
+				.name = (char *)"jane doe",
+				.email = (char *)"jane@invalid",
+				.message = (char *)"message3",
 			}
 		},
 	};
 	struct reftable_log_record r3[] = {
 		{
-			.refname = "a",
+			.refname = (char *)"a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_DELETION,
 		},
@@ -404,9 +404,8 @@ static void test_default_write_opts(void)
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-
 	struct reftable_ref_record rec = {
-		.refname = "master",
+		.refname = (char *)"master",
 		.update_index = 1,
 	};
 	int err;
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd214c5..72f66507ba 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -86,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
 		set_test_hash(log.value.update.new_hash, i);
-		log.value.update.message = "message";
+		log.value.update.message = (char *)"message";
 
 		n = reftable_writer_add_log(w, &log);
 		EXPECT(n == 0);
@@ -117,17 +117,18 @@ static void test_log_buffer_size(void)
 	};
 	int err;
 	int i;
-	struct reftable_log_record
-		log = { .refname = "refs/heads/master",
-			.update_index = 0xa,
-			.value_type = REFTABLE_LOG_UPDATE,
-			.value = { .update = {
-					   .name = "Han-Wen Nienhuys",
-					   .email = "hanwen@google.com",
-					   .tz_offset = 100,
-					   .time = 0x5e430672,
-					   .message = "commit: 9\n",
-				   } } };
+	struct reftable_log_record log = {
+		.refname = (char *)"refs/heads/master",
+		.update_index = 0xa,
+		.value_type = REFTABLE_LOG_UPDATE,
+		.value.update = {
+			.name = (char *)"Han-Wen Nienhuys",
+			.email = (char *)"hanwen@google.com",
+			.tz_offset = 100,
+			.time = 0x5e430672,
+			.message = (char *)"commit: 9\n",
+		},
+	};
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
@@ -156,15 +157,15 @@ static void test_log_overflow(void)
 	};
 	int err;
 	struct reftable_log_record log = {
-		.refname = "refs/heads/master",
+		.refname = (char *)"refs/heads/master",
 		.update_index = 0xa,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "Han-Wen Nienhuys",
-				.email = "hanwen@google.com",
+				.name = (char *)"Han-Wen Nienhuys",
+				.email = (char *)"hanwen@google.com",
 				.tz_offset = 100,
 				.time = 0x5e430672,
 				.message = msg,
@@ -293,14 +294,14 @@ static void test_log_zlib_corruption(void)
 	char message[100] = { 0 };
 	int err, i, n;
 	struct reftable_log_record log = {
-		.refname = "refname",
+		.refname = (char *)"refname",
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.new_hash = { 1 },
 				.old_hash = { 2 },
-				.name = "My Name",
-				.email = "myname@invalid",
+				.name = (char *)"My Name",
+				.email = (char *)"myname@invalid",
 				.message = message,
 			},
 		},
@@ -728,7 +729,7 @@ static void test_write_empty_key(void)
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
-		.refname = "",
+		.refname = (char *)"",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_DELETION,
 	};
@@ -752,18 +753,18 @@ static void test_write_key_order(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record refs[2] = {
 		{
-			.refname = "b",
+			.refname = (char *)"b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *)"target",
 			},
 		}, {
-			.refname = "a",
+			.refname = (char *)"a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *)"target",
 			},
 		}
 	};
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 7889f818d1..0994f6bcda 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
 	char out[1024] = "line1\n\nline2\nline3";
 	int n, err;
 	char **names = NULL;
-	char *want[] = { "line1", "line2", "line3" };
+	const char *want[] = { "line1", "line2", "line3" };
 	int i = 0;
 
 	EXPECT(fd > 0);
@@ -116,13 +116,14 @@ static void test_parse_names(void)
 
 static void test_names_equal(void)
 {
-	char *a[] = { "a", "b", "c", NULL };
-	char *b[] = { "a", "b", "d", NULL };
-	char *c[] = { "a", "b", NULL };
-
-	EXPECT(names_equal(a, a));
-	EXPECT(!names_equal(a, b));
-	EXPECT(!names_equal(a, c));
+	char a[] = "a", b[] = "b", c[] = "c", d[] = "d";
+	char *abc[] = { a, b, c, NULL };
+	char *abd[] = { a, b, d, NULL };
+	char *ab[] = { a, b, NULL };
+
+	EXPECT(names_equal(abc, abc));
+	EXPECT(!names_equal(abc, abd));
+	EXPECT(!names_equal(abc, ab));
 }
 
 static int write_test_ref(struct reftable_writer *wr, void *arg)
@@ -156,10 +157,10 @@ static void test_reftable_stack_add_one(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *)"HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *)"master",
 	};
 	struct reftable_ref_record dest = { NULL };
 	struct stat stat_result = { 0 };
@@ -216,16 +217,16 @@ static void test_reftable_stack_uptodate(void)
 
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "HEAD",
+		.refname = (char *)"HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *)"master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "branch2",
+		.refname = (char *)"branch2",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *)"master",
 	};
 
 
@@ -264,10 +265,10 @@ static void test_reftable_stack_transaction_api(void)
 	struct reftable_addition *add = NULL;
 
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *)"HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *)"master",
 	};
 	struct reftable_ref_record dest = { NULL };
 
@@ -313,7 +314,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *)"master",
 		};
 		char name[100];
 
@@ -356,7 +357,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
 {
 	struct reftable_ref_record ref = {
-		.refname = "refs/heads/master",
+		.refname = (char *)"refs/heads/master",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = {0x01},
@@ -409,16 +410,16 @@ static void test_reftable_stack_update_index_check(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "name1",
+		.refname = (char *)"name1",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *)"master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "name2",
+		.refname = (char *)"name2",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *)"master",
 	};
 
 	err = reftable_new_stack(&st, dir, cfg);
@@ -561,7 +562,7 @@ static void test_reftable_stack_log_normalize(void)
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
 	struct reftable_log_record input = {
-		.refname = "branch",
+		.refname = (char *)"branch",
 		.update_index = 1,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
@@ -582,11 +583,11 @@ static void test_reftable_stack_log_normalize(void)
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 
-	input.value.update.message = "one\ntwo";
+	input.value.update.message = (char *)"one\ntwo";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT(err == REFTABLE_API_ERROR);
 
-	input.value.update.message = "one";
+	input.value.update.message = (char *)"one";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
 
@@ -594,7 +595,7 @@ static void test_reftable_stack_log_normalize(void)
 	EXPECT_ERR(err);
 	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
 
-	input.value.update.message = "two\n";
+	input.value.update.message = (char *)"two\n";
 	arg.update_index = 2;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
@@ -697,9 +698,9 @@ static void test_reftable_stack_hash_id(void)
 	int err;
 
 	struct reftable_ref_record ref = {
-		.refname = "master",
+		.refname = (char *)"master",
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "target",
+		.value.symref = (char *)"target",
 		.update_index = 1,
 	};
 	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
@@ -879,7 +880,7 @@ static void test_reftable_stack_auto_compaction(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *)"master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -913,7 +914,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *)"master",
 		};
 
 		/*
@@ -964,7 +965,7 @@ static void test_reftable_stack_compaction_concurrent(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *)"master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -1014,7 +1015,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *)"master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 06/19] refspec: remove global tag refspec structure
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (4 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 05/19] reftable: improve const correctness when assigning " Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 17:47   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 07/19] http: do not assign string constant to non-const field Patrick Steinhardt
                   ` (18 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 3608 bytes --]

We have a global tag refspec structure that is used by both git-clone(1)
and git-fetch(1). Initialization fo the structure will break once we
enable `-Wwrite-strings`, even though the breakage is harmless. While we
could just add casts, the structure isn't really required in the first
place as we can simply initialize the structures at the respective
callsites.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c |  8 ++++++--
 builtin/fetch.c | 11 ++++++++---
 refspec.c       | 13 -------------
 refspec.h       |  1 -
 4 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 92ab7d7165..bde1d284a2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
 	struct ref *local_refs = head;
 	struct ref **tail = head ? &head->next : &local_refs;
+	struct refspec_item tag_refspec;
+
+	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
 
 	if (option_single_branch) {
 		struct ref *remote_head = NULL;
@@ -545,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 					      &tail, 0);
 
 			/* if --branch=tag, pull the requested tag explicitly */
-			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+			get_fetch_map(remote_head, &tag_refspec, &tail, 0);
 		}
 		free_refs(remote_head);
 	} else {
@@ -555,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	}
 
 	if (!option_mirror && !option_single_branch && !option_no_tags)
-		get_fetch_map(refs, tag_refspec, &tail, 0);
+		get_fetch_map(refs, &tag_refspec, &tail, 0);
 
+	refspec_item_clear(&tag_refspec);
 	return local_refs;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 75255dc600..06b60867f5 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
 		}
 	}
 
-	if (tags == TAGS_SET)
+	if (tags == TAGS_SET) {
+		struct refspec_item tag_refspec;
+
 		/* also fetch all tags */
-		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-	else if (tags == TAGS_DEFAULT && *autotags)
+		refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+		get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+		refspec_item_clear(&tag_refspec);
+	} else if (tags == TAGS_DEFAULT && *autotags) {
 		find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+	}
 
 	/* Now append any refs to be updated opportunistically: */
 	*tail = orefs;
diff --git a/refspec.c b/refspec.c
index d60932f4de..1df5de6c2f 100644
--- a/refspec.c
+++ b/refspec.c
@@ -7,19 +7,6 @@
 #include "refspec.h"
 #include "strbuf.h"
 
-static struct refspec_item s_tag_refspec = {
-	.force = 0,
-	.pattern = 1,
-	.matching = 0,
-	.exact_sha1 = 0,
-	.negative = 0,
-	.src = "refs/tags/*",
-	.dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
 /*
  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
  * Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446993..754be45cee 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
 #define REFSPEC_H
 
 #define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
 
 /**
  * A struct refspec_item holds the parsed interpretation of a refspec.  If it
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 07/19] http: do not assign string constant to non-const field
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (5 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 06/19] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 19:39   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 08/19] line-log: always allocate the output prefix Patrick Steinhardt
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 1260 bytes --]

In `write_accept_language()`, we put all acceptable languages into an
array. While all entries in that array are allocated strings, the final
entry in that array is a string constant. This is fine because we
explicitly skip over the last entry when freeing the array, but will
cause warnings once we enable `-Wwrite-strings`.

Adapt the code to also allocate the final entry.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 http.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/http.c b/http.c
index 67cc47d28f..2dea2d03da 100644
--- a/http.c
+++ b/http.c
@@ -1974,7 +1974,7 @@ static void write_accept_language(struct strbuf *buf)
 
 		/* add '*' */
 		REALLOC_ARRAY(language_tags, num_langs + 1);
-		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+		language_tags[num_langs++] = xstrdup("*");
 
 		/* compute decimal_places */
 		for (max_q = 1, decimal_places = 0;
@@ -2004,8 +2004,7 @@ static void write_accept_language(struct strbuf *buf)
 		}
 	}
 
-	/* free language tags -- last one is a static '*' */
-	for (i = 0; i < num_langs - 1; i++)
+	for (i = 0; i < num_langs; i++)
 		free(language_tags[i]);
 	free(language_tags);
 }
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 08/19] line-log: always allocate the output prefix
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (6 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 07/19] http: do not assign string constant to non-const field Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 19:51   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 09/19] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
                   ` (16 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 2111 bytes --]

The returned string by `output_prefix()` is sometimes a string constant
and sometimes an allocated string. This has been fine until now because
we always leak the allocated strings, and thus we never tried to free
the string constant.

Fix the code to always return an allocated string and free the returned
value at all callsites.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/line-log.c b/line-log.c
index d9bf2c8120..9a298209d0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
 
 static char *output_prefix(struct diff_options *opt)
 {
-	char *prefix = "";
-
 	if (opt->output_prefix) {
 		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
-		prefix = sb->buf;
+		return sb->buf;
+	} else {
+		return xstrdup("");
 	}
-
-	return prefix;
 }
 
 static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
 
 	if (!pair || !diff)
-		return;
+		goto out;
 
 	if (pair->one->oid_valid)
 		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 				   c_context, c_reset, opt->file);
 	}
 
+out:
 	free(p_ends);
 	free(t_ends);
+	free(prefix);
 }
 
 /*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
  */
 static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
 {
-	fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+	char *prefix = output_prefix(&rev->diffopt);
+
+	fprintf(rev->diffopt.file, "%s\n", prefix);
+	free(prefix);
+
 	while (range) {
 		dump_diff_hacky_one(rev, range);
 		range = range->next;
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 09/19] object-file: make `buf` parameter of `index_mem()` a constant
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (7 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 08/19] line-log: always allocate the output prefix Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 20:01   ` Junio C Hamano
  2024-05-29 12:44 ` [PATCH 10/19] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 1863 bytes --]

The `buf` parameter of `index_mem()` is a non-constant string. This will
break once we enable `-Wwrite-strings` because we also pass constants
from at least one callsite.

Adapt the parameter to be a constant. As we cannot free the buffer
without casting now, this also requires us to move the lifetime of the
nested buffer around.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/object-file.c b/object-file.c
index c9e374e57e..46ea00ac46 100644
--- a/object-file.c
+++ b/object-file.c
@@ -2483,12 +2483,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
 }
 
 static int index_mem(struct index_state *istate,
-		     struct object_id *oid, void *buf, size_t size,
+		     struct object_id *oid,
+		     const void *buf, size_t size,
 		     enum object_type type,
 		     const char *path, unsigned flags)
 {
+	struct strbuf nbuf = STRBUF_INIT;
 	int ret = 0;
-	int re_allocated = 0;
 	int write_object = flags & HASH_WRITE_OBJECT;
 
 	if (!type)
@@ -2498,11 +2499,10 @@ static int index_mem(struct index_state *istate,
 	 * Convert blobs to git internal format
 	 */
 	if ((type == OBJ_BLOB) && path) {
-		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(istate, path, buf, size, &nbuf,
 				   get_conv_flags(flags))) {
-			buf = strbuf_detach(&nbuf, &size);
-			re_allocated = 1;
+			buf = nbuf.buf;
+			size = nbuf.len;
 		}
 	}
 	if (flags & HASH_FORMAT_CHECK) {
@@ -2519,8 +2519,8 @@ static int index_mem(struct index_state *istate,
 		ret = write_object_file(buf, size, type, oid);
 	else
 		hash_object_file(the_hash_algo, buf, size, type, oid);
-	if (re_allocated)
-		free(buf);
+
+	strbuf_release(&nbuf);
 	return ret;
 }
 
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 10/19] parse-options: cast long name for OPTION_ALIAS
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (8 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 09/19] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 12:44 ` [PATCH 11/19] send-pack: always allocate receive status Patrick Steinhardt
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 792 bytes --]

We assign the long name for OPTION_ALIAS options to a non-constant value
field. We know that the variable will never be written to, but this will
cause warnings once we enable `-Wwrite-strings`.

Cast away the constness to be prepared for this change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 parse-options.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/parse-options.h b/parse-options.h
index bd62e20268..ae15342390 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
 	.type = OPTION_ALIAS, \
 	.short_name = (s), \
 	.long_name = (l), \
-	.value = (source_long_name), \
+	.value = (char *)(source_long_name), \
 }
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 11/19] send-pack: always allocate receive status
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (9 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 10/19] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 12:44 ` [PATCH 12/19] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 1586 bytes --]

In `receive_status()`, we record the reason why ref updates have been
rejected by the remote via the `remote_status`. But while we allocate
the assigned string when a reason was given, we assign a string constant
when no reason was given.

This has been working fine so far due to two reasons:

  - We don't ever free the refs in git-send-pack(1)'

  - Remotes always give a reason, at least as implemented by Git proper.

Adapt the code to always allocate the receive status string and free the
refs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/send-pack.c | 2 ++
 send-pack.c         | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaad09..17cae6bbbd 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
+	free_refs(remote_refs);
+	free_refs(local_refs);
 	return ret;
 }
diff --git a/send-pack.c b/send-pack.c
index 37f59d4f66..88e96d000b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -259,7 +259,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
 			if (p)
 				hint->remote_status = xstrdup(p);
 			else
-				hint->remote_status = "failed";
+				hint->remote_status = xstrdup("failed");
 		} else {
 			hint->status = REF_STATUS_OK;
 			hint->remote_status = xstrdup_or_null(p);
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 12/19] remote-curl: avoid assigning string constant to non-const variable
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (10 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 11/19] send-pack: always allocate receive status Patrick Steinhardt
@ 2024-05-29 12:44 ` Patrick Steinhardt
  2024-05-29 20:21   ` Junio C Hamano
  2024-05-29 12:45 ` [PATCH 13/19] revision: always store allocated strings in output encoding Patrick Steinhardt
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:44 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 7794 bytes --]

When processing remote options, we split the option line into two by
searching for a space. If there is one, we replace the space with '\0',
otherwise we implicitly assume that the value is "true" and thus assign
a string constant.

As the return value of strchr(3P) weirdly enough is a `char *` even
though it gets a `const char *` as input, the assigned-to variable also
is a non-constant. This is fine though because the argument is in fact
an allocated string, and thus we are allowed to modify it. But this will
break once we enable `-Wwrite-strings`.

Refactor the code stop splitting the fields with '\0' altogether.
Instead, we can pass the length of the option name to `set_option()` and
then use strncmp(3P) instead of strcmp(3P).

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c | 58 +++++++++++++++++++++++++++------------------------
 1 file changed, 31 insertions(+), 27 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index cae98384da..ac185d4f88 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -58,9 +58,9 @@ struct options {
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
 
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
 {
-	if (!strcmp(name, "verbosity")) {
+	if (!strncmp(name, "verbosity", namelen)) {
 		char *end;
 		int v = strtol(value, &end, 10);
 		if (value == end || *end)
@@ -68,7 +68,7 @@ static int set_option(const char *name, const char *value)
 		options.verbosity = v;
 		return 0;
 	}
-	else if (!strcmp(name, "progress")) {
+	else if (!strncmp(name, "progress", namelen)) {
 		if (!strcmp(value, "true"))
 			options.progress = 1;
 		else if (!strcmp(value, "false"))
@@ -77,7 +77,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "depth")) {
+	else if (!strncmp(name, "depth", namelen)) {
 		char *end;
 		unsigned long v = strtoul(value, &end, 10);
 		if (value == end || *end)
@@ -85,15 +85,15 @@ static int set_option(const char *name, const char *value)
 		options.depth = v;
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-since")) {
+	else if (!strncmp(name, "deepen-since", namelen)) {
 		options.deepen_since = xstrdup(value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-not")) {
+	else if (!strncmp(name, "deepen-not", namelen)) {
 		string_list_append(&options.deepen_not, value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-relative")) {
+	else if (!strncmp(name, "deepen-relative", namelen)) {
 		if (!strcmp(value, "true"))
 			options.deepen_relative = 1;
 		else if (!strcmp(value, "false"))
@@ -102,7 +102,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "followtags")) {
+	else if (!strncmp(name, "followtags", namelen)) {
 		if (!strcmp(value, "true"))
 			options.followtags = 1;
 		else if (!strcmp(value, "false"))
@@ -111,7 +111,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "dry-run")) {
+	else if (!strncmp(name, "dry-run", namelen)) {
 		if (!strcmp(value, "true"))
 			options.dry_run = 1;
 		else if (!strcmp(value, "false"))
@@ -120,7 +120,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "check-connectivity")) {
+	else if (!strncmp(name, "check-connectivity", namelen)) {
 		if (!strcmp(value, "true"))
 			options.check_self_contained_and_connected = 1;
 		else if (!strcmp(value, "false"))
@@ -129,7 +129,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "cas")) {
+	else if (!strncmp(name, "cas", namelen)) {
 		struct strbuf val = STRBUF_INIT;
 		strbuf_addstr(&val, "--force-with-lease=");
 		if (*value != '"')
@@ -139,7 +139,7 @@ static int set_option(const char *name, const char *value)
 		string_list_append(&cas_options, val.buf);
 		strbuf_release(&val);
 		return 0;
-	} else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+	} else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
 		if (!strcmp(value, "true"))
 			options.force_if_includes = 1;
 		else if (!strcmp(value, "false"))
@@ -147,7 +147,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "cloning")) {
+	} else if (!strncmp(name, "cloning", namelen)) {
 		if (!strcmp(value, "true"))
 			options.cloning = 1;
 		else if (!strcmp(value, "false"))
@@ -155,7 +155,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "update-shallow")) {
+	} else if (!strncmp(name, "update-shallow", namelen)) {
 		if (!strcmp(value, "true"))
 			options.update_shallow = 1;
 		else if (!strcmp(value, "false"))
@@ -163,7 +163,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "pushcert")) {
+	} else if (!strncmp(name, "pushcert", namelen)) {
 		if (!strcmp(value, "true"))
 			options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
 		else if (!strcmp(value, "false"))
@@ -173,7 +173,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "atomic")) {
+	} else if (!strncmp(name, "atomic", namelen)) {
 		if (!strcmp(value, "true"))
 			options.atomic = 1;
 		else if (!strcmp(value, "false"))
@@ -181,7 +181,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "push-option")) {
+	} else if (!strncmp(name, "push-option", namelen)) {
 		if (*value != '"')
 			string_list_append(&options.push_options, value);
 		else {
@@ -192,7 +192,7 @@ static int set_option(const char *name, const char *value)
 						 strbuf_detach(&unquoted, NULL));
 		}
 		return 0;
-	} else if (!strcmp(name, "family")) {
+	} else if (!strncmp(name, "family", namelen)) {
 		if (!strcmp(value, "ipv4"))
 			git_curl_ipresolve = CURL_IPRESOLVE_V4;
 		else if (!strcmp(value, "ipv6"))
@@ -202,16 +202,16 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "from-promisor")) {
+	} else if (!strncmp(name, "from-promisor", namelen)) {
 		options.from_promisor = 1;
 		return 0;
-	} else if (!strcmp(name, "refetch")) {
+	} else if (!strncmp(name, "refetch", namelen)) {
 		options.refetch = 1;
 		return 0;
-	} else if (!strcmp(name, "filter")) {
+	} else if (!strncmp(name, "filter", namelen)) {
 		options.filter = xstrdup(value);
 		return 0;
-	} else if (!strcmp(name, "object-format")) {
+	} else if (!strncmp(name, "object-format", namelen)) {
 		options.object_format = 1;
 		if (strcmp(value, "true"))
 			die(_("unknown value for object-format: %s"), value);
@@ -1588,15 +1588,19 @@ int cmd_main(int argc, const char **argv)
 			parse_push(&buf);
 
 		} else if (skip_prefix(buf.buf, "option ", &arg)) {
-			char *value = strchr(arg, ' ');
+			const char *value = strchr(arg, ' ');
+			size_t arglen;
 			int result;
 
-			if (value)
-				*value++ = '\0';
-			else
+			if (value) {
+				arglen = value - arg;
+				value++;
+			} else {
+				arglen = strlen(arg);
 				value = "true";
+			}
 
-			result = set_option(arg, value);
+			result = set_option(arg, arglen, value);
 			if (!result)
 				printf("ok\n");
 			else if (result < 0)
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 13/19] revision: always store allocated strings in output encoding
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (11 preceding siblings ...)
  2024-05-29 12:44 ` [PATCH 12/19] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
@ 2024-05-29 12:45 ` Patrick Steinhardt
  2024-05-29 20:23   ` Junio C Hamano
  2024-05-29 12:45 ` [PATCH 14/19] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 2531 bytes --]

The `git_log_output_encoding` variable can be set via the `--encoding=`
option. When doing so, we conditionally either assign it to the passed
value, or if the value is "none" we assign it the empty string.
Depending on which of the both code paths we pick though, the variable
may end up being assigned either an allocated string or a string
constant.

This is somewhat risky and may easily lead to bugs when a different code
path may want to reassign a new value to it, freeing the previous value.
We already to this when parsing the "i18n.logoutputencoding" config in
`git_default_i18n_config()`. But because the config is typically parsed
before we parse command line options this has been fine so far.

Regardless of that, safeguard the code such that the variable always
contains an allocated string. While at it, also free the old value in
case there was any to plug a potential memory leak.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 revision.c             | 3 ++-
 t/t3900-i18n-commit.sh | 1 +
 t/t3901-i18n-patch.sh  | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 7ddf0f151a..2ee6886078 100644
--- a/revision.c
+++ b/revision.c
@@ -2650,10 +2650,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		free(git_log_output_encoding);
 		if (strcmp(optarg, "none"))
 			git_log_output_encoding = xstrdup(optarg);
 		else
-			git_log_output_encoding = "";
+			git_log_output_encoding = xstrdup("");
 		return argcount;
 	} else if (!strcmp(arg, "--reverse")) {
 		revs->reverse ^= 1;
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09cfd9..db7b403bc1 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
 
 test_description='commit and log output encodings'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78829..5f0b9afc3f 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@ test_description='i18n settings and format-patch | am pipe'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_encoding () {
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 14/19] mailmap: always store allocated strings in mailmap blob
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (12 preceding siblings ...)
  2024-05-29 12:45 ` [PATCH 13/19] revision: always store allocated strings in output encoding Patrick Steinhardt
@ 2024-05-29 12:45 ` Patrick Steinhardt
  2024-05-29 12:45 ` [PATCH 15/19] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
                   ` (10 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Same as with the preceding commit, the `git_mailmap_blob` may sometimes
contain an allocated string and sometimes it may contain a string
constant. This is risky and can easily lead to bugs in case the variable
is getting re-assigned, where the code may then try to free the previous
value to avoid memory leaks.

Safeguard the code by always storing allocated strings in the variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 mailmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mailmap.c b/mailmap.c
index b2efe29b3d..3d1e092fef 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -216,7 +216,7 @@ int read_mailmap(struct string_list *map)
 	map->cmp = namemap_cmp;
 
 	if (!git_mailmap_blob && is_bare_repository())
-		git_mailmap_blob = "HEAD:.mailmap";
+		git_mailmap_blob = xstrdup("HEAD:.mailmap");
 
 	if (!startup_info->have_repository || !is_bare_repository())
 		err |= read_mailmap_file(map, ".mailmap",
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 15/19] imap-send: drop global `imap_server_conf` variable
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (13 preceding siblings ...)
  2024-05-29 12:45 ` [PATCH 14/19] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
@ 2024-05-29 12:45 ` Patrick Steinhardt
  2024-05-29 12:45 ` [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 7200 bytes --]

In "imap-send.c", we have a global `sturct imap_server_conf` variable
that keeps track of the configuration of the IMAP server. This variable
is being populated mostly via the Git configuration.

Refactor the code to allocate the structure on the stack instead of
having it globally. This change allows us to track its lifetime more
closely.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 57 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 8b723b34a5..67a7a6c456 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -82,10 +82,6 @@ struct imap_server_conf {
 	char *auth_method;
 };
 
-static struct imap_server_conf server = {
-	.ssl_verify = 1,
-};
-
 struct imap_socket {
 	int fd[2];
 	SSL *ssl;
@@ -110,6 +106,7 @@ struct imap {
 };
 
 struct imap_store {
+	const struct imap_server_conf *cfg;
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	int uidvalidity;
@@ -194,8 +191,8 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
 
 #ifdef NO_OPENSSL
 static int ssl_socket_connect(struct imap_socket *sock UNUSED,
-			      int use_tls_only UNUSED,
-			      int verify UNUSED)
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only UNUSED)
 {
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
@@ -250,7 +247,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
 		     cname, hostname);
 }
 
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock,
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 	const SSL_METHOD *meth;
@@ -279,7 +278,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	if (use_tls_only)
 		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
-	if (verify)
+	if (cfg->ssl_verify)
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
 
 	if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +305,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	 * OpenSSL does not document this function, but the implementation
 	 * returns 1 on success, 0 on failure after calling SSLerr().
 	 */
-	ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+	ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
 	if (ret != 1)
-		warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+		warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
 #endif
 
 	ret = SSL_connect(sock->ssl);
@@ -317,12 +316,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 		return -1;
 	}
 
-	if (verify) {
+	if (cfg->ssl_verify) {
 		/* make sure the hostname matches that of the certificate */
 		cert = SSL_get_peer_certificate(sock->ssl);
 		if (!cert)
 			return error("unable to get peer certificate.");
-		if (verify_hostname(cert, server.host) < 0)
+		if (verify_hostname(cert, cfg->host) < 0)
 			return -1;
 	}
 
@@ -895,7 +894,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 	int ret;
 	char *response;
 
-	response = cram(prompt, server.user, server.pass);
+	response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
 
 	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
 	if (ret != strlen(response))
@@ -935,6 +934,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 
 	CALLOC_ARRAY(ctx, 1);
 
+	ctx->cfg = srvc;
 	ctx->imap = CALLOC_ARRAY(imap, 1);
 	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
 	imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1035,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		imap->buf.sock.fd[1] = dup(s);
 
 		if (srvc->use_ssl &&
-		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+		    ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
 			close(s);
 			goto bail;
 		}
@@ -1068,8 +1068,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		if (!srvc->use_ssl && CAP(STARTTLS)) {
 			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
 				goto bail;
-			if (ssl_socket_connect(&imap->buf.sock, 1,
-					       srvc->ssl_verify))
+			if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
 				goto bail;
 			/* capabilities may have changed, so get the new capabilities */
 			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1299,23 +1298,24 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 static int git_imap_config(const char *var, const char *val,
 			   const struct config_context *ctx, void *cb)
 {
+	struct imap_server_conf *cfg = cb;
 
 	if (!strcmp("imap.sslverify", var))
-		server.ssl_verify = git_config_bool(var, val);
+		cfg->ssl_verify = git_config_bool(var, val);
 	else if (!strcmp("imap.preformattedhtml", var))
-		server.use_html = git_config_bool(var, val);
+		cfg->use_html = git_config_bool(var, val);
 	else if (!strcmp("imap.folder", var))
-		return git_config_string(&server.folder, var, val);
+		return git_config_string(&cfg->folder, var, val);
 	else if (!strcmp("imap.user", var))
-		return git_config_string(&server.user, var, val);
+		return git_config_string(&cfg->user, var, val);
 	else if (!strcmp("imap.pass", var))
-		return git_config_string(&server.pass, var, val);
+		return git_config_string(&cfg->pass, var, val);
 	else if (!strcmp("imap.tunnel", var))
-		return git_config_string(&server.tunnel, var, val);
+		return git_config_string(&cfg->tunnel, var, val);
 	else if (!strcmp("imap.authmethod", var))
-		return git_config_string(&server.auth_method, var, val);
+		return git_config_string(&cfg->auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val, ctx->kvi);
+		cfg->port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
@@ -1324,11 +1324,11 @@ static int git_imap_config(const char *var, const char *val,
 				val += 5;
 			else if (starts_with(val, "imaps:")) {
 				val += 6;
-				server.use_ssl = 1;
+				cfg->use_ssl = 1;
 			}
 			if (starts_with(val, "//"))
 				val += 2;
-			server.host = xstrdup(val);
+			cfg->host = xstrdup(val);
 		}
 	} else
 		return git_default_config(var, val, ctx, cb);
@@ -1497,12 +1497,15 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
 
 int cmd_main(int argc, const char **argv)
 {
+	struct imap_server_conf server = {
+		.ssl_verify = 1,
+	};
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
 
 	setup_git_directory_gently(&nongit_ok);
-	git_config(git_imap_config, NULL);
+	git_config(git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
 
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (14 preceding siblings ...)
  2024-05-29 12:45 ` [PATCH 15/19] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
@ 2024-05-29 12:45 ` Patrick Steinhardt
  2024-05-29 20:55   ` Junio C Hamano
  2024-05-29 12:45 ` [PATCH 17/19] builtin/rebase: adapt code to not assign string constants to non-const Patrick Steinhardt
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 4485 bytes --]

We never free any of the config strings that we populate into the
`struct imap_server_conf`. Fix this by creating a common exit path where
we can free resources.

While at it, drop the unused variables `imap_server_conf::name` and
`nongit_ok`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 68 +++++++++++++++++++++++++++++++++--------------------
 1 file changed, 43 insertions(+), 25 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 67a7a6c456..a6fb33806c 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,7 +69,6 @@ static void imap_warn(const char *, ...);
 static char *next_arg(char **);
 
 struct imap_server_conf {
-	const char *name;
 	char *tunnel;
 	char *host;
 	int port;
@@ -1300,23 +1299,28 @@ static int git_imap_config(const char *var, const char *val,
 {
 	struct imap_server_conf *cfg = cb;
 
-	if (!strcmp("imap.sslverify", var))
+	if (!strcmp("imap.sslverify", var)) {
 		cfg->ssl_verify = git_config_bool(var, val);
-	else if (!strcmp("imap.preformattedhtml", var))
+	} else if (!strcmp("imap.preformattedhtml", var)) {
 		cfg->use_html = git_config_bool(var, val);
-	else if (!strcmp("imap.folder", var))
+	} else if (!strcmp("imap.folder", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->folder, var, val);
-	else if (!strcmp("imap.user", var))
+	} else if (!strcmp("imap.user", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->user, var, val);
-	else if (!strcmp("imap.pass", var))
+	} else if (!strcmp("imap.pass", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->pass, var, val);
-	else if (!strcmp("imap.tunnel", var))
+	} else if (!strcmp("imap.tunnel", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->tunnel, var, val);
-	else if (!strcmp("imap.authmethod", var))
+	} else if (!strcmp("imap.authmethod", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->auth_method, var, val);
-	else if (!strcmp("imap.port", var))
+	} else if (!strcmp("imap.port", var)) {
 		cfg->port = git_config_int(var, val, ctx->kvi);
-	else if (!strcmp("imap.host", var)) {
+	} else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
 		} else {
@@ -1330,8 +1334,9 @@ static int git_imap_config(const char *var, const char *val,
 				val += 2;
 			cfg->host = xstrdup(val);
 		}
-	} else
+	} else {
 		return git_default_config(var, val, ctx, cb);
+	}
 
 	return 0;
 }
@@ -1502,9 +1507,9 @@ int cmd_main(int argc, const char **argv)
 	};
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
-	int nongit_ok;
+	int ret;
 
-	setup_git_directory_gently(&nongit_ok);
+	setup_git_directory_gently(NULL);
 	git_config(git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
@@ -1529,42 +1534,55 @@ int cmd_main(int argc, const char **argv)
 
 	if (!server.folder) {
 		fprintf(stderr, "no imap store specified\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
 			fprintf(stderr, "no imap host specified\n");
-			return 1;
+			ret = 1;
+			goto out;
 		}
-		server.host = "tunnel";
+		server.host = xstrdup("tunnel");
 	}
 
 	/* read the messages */
 	if (strbuf_read(&all_msgs, 0, 0) < 0) {
 		error_errno(_("could not read from stdin"));
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	if (all_msgs.len == 0) {
 		fprintf(stderr, "nothing to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	total = count_messages(&all_msgs);
 	if (!total) {
 		fprintf(stderr, "no messages to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	/* write it to the imap server */
 
 	if (server.tunnel)
-		return append_msgs_to_imap(&server, &all_msgs, total);
-
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
 #ifdef USE_CURL_FOR_IMAP_SEND
-	if (use_curl)
-		return curl_append_msgs_to_imap(&server, &all_msgs, total);
+	else if (use_curl)
+		ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
 #endif
-
-	return append_msgs_to_imap(&server, &all_msgs, total);
+	else
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
+
+out:
+	free(server.tunnel);
+	free(server.host);
+	free(server.folder);
+	free(server.user);
+	free(server.pass);
+	free(server.auth_method);
+	return ret;
 }
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 17/19] builtin/rebase: adapt code to not assign string constants to non-const
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (15 preceding siblings ...)
  2024-05-29 12:45 ` [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
@ 2024-05-29 12:45 ` Patrick Steinhardt
  2024-05-29 21:01   ` Junio C Hamano
  2024-05-29 12:45 ` [PATCH 18/19] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 2497 bytes --]

When computing the rebase strategy we temporarily assign a string
constant to `options.strategy` before we call `xstrdup()` on it.
Furthermore, the default backend is being assigned a string constant via
`REBASE_OPTIONS_INIT`. Both of these will cause warnings once we enable
`-Wwrite-strings`.

Adapt the code such that we only store allocated strings in those
variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 14d4f0a5e6..b05c3b6be3 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -135,7 +135,6 @@ struct rebase_options {
 		.type = REBASE_UNSPECIFIED,	  	\
 		.empty = EMPTY_UNSPECIFIED,	  	\
 		.keep_empty = 1,			\
-		.default_backend = "merge",	  	\
 		.flags = REBASE_NO_QUIET, 		\
 		.git_am_opts = STRVEC_INIT,		\
 		.exec = STRING_LIST_INIT_NODUP,		\
@@ -796,6 +795,7 @@ static int rebase_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "rebase.backend")) {
+		FREE_AND_NULL(opts->default_backend);
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
@@ -1471,12 +1471,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	}
 
 	if (options.strategy_opts.nr && !options.strategy)
-		options.strategy = "ort";
-
-	if (options.strategy) {
-		options.strategy = xstrdup(options.strategy);
+		options.strategy = xstrdup("ort");
+	else
+		options.strategy = xstrdup_or_null(options.strategy);
+	if (options.strategy)
 		imply_merge(&options, "--strategy");
-	}
 
 	if (options.root && !options.onto_name)
 		imply_merge(&options, "--root without --onto");
@@ -1522,7 +1521,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	}
 
 	if (options.type == REBASE_UNSPECIFIED) {
-		if (!strcmp(options.default_backend, "merge"))
+		if (!options.default_backend)
+			options.type = REBASE_MERGE;
+		else if (!strcmp(options.default_backend, "merge"))
 			options.type = REBASE_MERGE;
 		else if (!strcmp(options.default_backend, "apply"))
 			options.type = REBASE_APPLY;
@@ -1833,6 +1834,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 cleanup:
 	strbuf_release(&buf);
 	strbuf_release(&revisions);
+	free(options.default_backend);
 	free(options.reflog_action);
 	free(options.head_name);
 	strvec_clear(&options.git_am_opts);
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 18/19] builtin/merge: always store allocated strings in `pull_twohead`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (16 preceding siblings ...)
  2024-05-29 12:45 ` [PATCH 17/19] builtin/rebase: adapt code to not assign string constants to non-const Patrick Steinhardt
@ 2024-05-29 12:45 ` Patrick Steinhardt
  2024-05-29 12:45 ` [PATCH 19/19] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 2451 bytes --]

The `pull_twohead` configuration may sometimes contain an allocated
string, and sometimes it may contain a string constant. Refactor this to
instead always store an allocated string such that we can release its
resources without risk.

While at it, manage the lifetime of other config strings, as well. Note
that we explicitly don't free `cleanup_arg` here. This is because the
variable may be assigned a string constant via command line options.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/merge.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/builtin/merge.c b/builtin/merge.c
index daed2d4e1e..fb3eb15b89 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -611,17 +611,19 @@ static int git_merge_config(const char *k, const char *v,
 		return 0;
 	}
 
-	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
 		show_diffstat = git_config_bool(k, v);
-	else if (!strcmp(k, "merge.verifysignatures"))
+	} else if (!strcmp(k, "merge.verifysignatures")) {
 		verify_signatures = git_config_bool(k, v);
-	else if (!strcmp(k, "pull.twohead"))
+	} else if (!strcmp(k, "pull.twohead")) {
+		FREE_AND_NULL(pull_twohead);
 		return git_config_string(&pull_twohead, k, v);
-	else if (!strcmp(k, "pull.octopus"))
+	} else if (!strcmp(k, "pull.octopus")) {
+		FREE_AND_NULL(pull_octopus);
 		return git_config_string(&pull_octopus, k, v);
-	else if (!strcmp(k, "commit.cleanup"))
+	} else if (!strcmp(k, "commit.cleanup")) {
 		return git_config_string(&cleanup_arg, k, v);
-	else if (!strcmp(k, "merge.ff")) {
+	} else if (!strcmp(k, "merge.ff")) {
 		int boolval = git_parse_maybe_bool(v);
 		if (0 <= boolval) {
 			fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -1294,7 +1296,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (!pull_twohead) {
 		char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
 		if (default_strategy && !strcmp(default_strategy, "ort"))
-			pull_twohead = "ort";
+			pull_twohead = xstrdup("ort");
 	}
 
 	init_diff_ui_defaults();
@@ -1793,6 +1795,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
+	free(pull_twohead);
+	free(pull_octopus);
 	discard_index(the_repository->index);
 	return ret;
 }
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH 19/19] config.mak.dev: enable `-Wwrite-strings` warning
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (17 preceding siblings ...)
  2024-05-29 12:45 ` [PATCH 18/19] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
@ 2024-05-29 12:45 ` Patrick Steinhardt
  2024-05-29 12:52 ` [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:45 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

Writing to string constants is undefined behaviour and must be avoided
in C. Even so, the compiler does not help us with this by default
because those constants are not in fact marked as `const`. This makes it
rather easy to accidentally assign a constant to a non-const variable or
field and then later on try to either free it or write to it.

Enable `-Wwrite-strings` to catch such mistakes. With this warning
enabled, the type of string constants is changed to `const char[]` and
will thus cause compiler warnings when being assigned to non-const
fields and variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.mak.dev | 1 +
 1 file changed, 1 insertion(+)

diff --git a/config.mak.dev b/config.mak.dev
index 981304727c..1ce4c70613 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -37,6 +37,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
 DEVELOPER_CFLAGS += -Wstrict-prototypes
 DEVELOPER_CFLAGS += -Wunused
 DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
 DEVELOPER_CFLAGS += -fno-common
 
 ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH 00/19] Compile with `-Wwrite-strings`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (18 preceding siblings ...)
  2024-05-29 12:45 ` [PATCH 19/19] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
@ 2024-05-29 12:52 ` Patrick Steinhardt
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-29 12:52 UTC (permalink / raw)
  To: git; +Cc: Jeff King

[-- Attachment #1: Type: text/plain, Size: 2711 bytes --]

On Wed, May 29, 2024 at 02:44:01PM +0200, Patrick Steinhardt wrote:
> Hi,
> 
> there were some recent discussions about compiler warnings and how to
> stay on top of breaking changes in compilers in general [1] and about
> string constants in particular [2]. This made me look into what kind of
> warnings we should reasonably enable, which led me to the following
> list of warnings that may be sensible:
> 
>   - `-Wformat-nonliteral` to warn about non-constant strings being
>     passed as format string.
> 
>   - `-Wwrite-strings` to warn about string constants being assigned to a
>     non-constant variable.
> 
>   - `-Wredundant-decls` to warn about redundant declarations.
> 
>   - `-Wconversion` to warn about implicit integer casts when they may
>     alter the value.
> 
> This patch series adapts our code to compile with `-Wwrite-strings`.
> This option will change the type of string constants from `char []` to
> `const char []` such that it is now invalid to assign it to non-const
> variables without a cast. The intent is to avoid undefined behaviour
> when accedintally writing to such strings and to avoid free'ing such a
> variable.
> 
> There are quite some cases where we mishandle this. Oftentimes we just
> didn't bother to free any memory at all, which made it a non-issue in
> the first place. Other times we had some special logic that prevents
> writing or freeing such strings. But in most cases it was an accident
> waiting to happen.
> 
> Even though the changes are quite invasive, I think that this is a step
> into the right direction. Many of the constructs feel quite fragile, and
> most of those get fixed in this series. Some others I just paper over,
> for example when assigning to structures with global lifetime where we
> know that they are never released at all.
> 
> I also have a patch series cooking for `-Wredundant-decls`. But while
> that warning detects some redundant declarations indeed, it creates a
> problem with `extern char **environ`. There is no header for it and
> programs are asked to declare it by themselves. But of course, some libc
> implementations disagree and declare it. I haven't found a nice way to
> work around this issue, but may send the patches that drop the redundant
> declarations nonetheless.
> 
> The other two warnings I haven't yet looked into.
> 
> I ran some test jobs on both GitHub [3] and GitLab [4] to verify that
> the result is sane.
> 
> Thanks!

I forgot to say that this is based on top of 3a57aa566a (The eighth
batch, 2024-05-28) with ps/leakfixes merged into it at ebdbefa4fe
(builtin/mv: fix leaks for submodule gitfile paths, 2024-05-27).

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 01/19] global: improve const correctness when assigning string constants
  2024-05-29 12:44 ` [PATCH 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
@ 2024-05-29 16:58   ` Junio C Hamano
  2024-05-30 11:29     ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 16:58 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> We're about to enable `-Wwrite-strings`, which changes the type of
> string constants to `const char[]`. Fix various sites where we assign
> such constants to non-const variables.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---

This is a noisy change, and it is kind of surprising that ...

> diff --git a/builtin/bisect.c b/builtin/bisect.c
> index a58432b9d9..dabce9b542 100644
> --- a/builtin/bisect.c
> +++ b/builtin/bisect.c
> @@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
>  	return bisect_clean_state();
>  }
>  
> -static void log_commit(FILE *fp, char *fmt, const char *state,
> +static void log_commit(FILE *fp,
> +		       const char *fmt, const char *state,
>  		       struct commit *commit)
>  {
>  	struct pretty_print_context pp = {0};

... a change like this does not require any other change to the
code inside the function (e.g., by making further cascading changes
to what the function calls).

But applying this step alone and building would not give us any
constness warning/error from the compiler, so the result looks good
to the compiler, I guess?

Thanks.


^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-29 12:44 ` [PATCH 02/19] global: assign non-const strings as required Patrick Steinhardt
@ 2024-05-29 17:25   ` Junio C Hamano
  2024-05-30 11:29     ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 17:25 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> +	char refspec_str[] = "refs/heads/*";
>  
>  	memset(&refspec, 0, sizeof(refspec));
>  	refspec.force = 0;
>  	refspec.pattern = 1;
> -	refspec.src = refspec.dst = "refs/heads/*";
> +	refspec.src = refspec.dst = refspec_str;
>  	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
>  	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
>  				    fetch_map, 1);

I have no objections to changes along this line, but this makes me
wonder if we somehow have ways to ensure that after everything is
done, refspec_str[], empty_str[], and the like in other hunks remain
to have their initial contents.  Stepping back a bit, without
applying this series, if we told the compiler to store these
constant strings in read-only segment, any attempt to write into
refspec.src or refspec.dst string would have been caught at runtime
as an error.  With the patch, that would no longer work.  The piece
of memory in refspec_str[] is a fair game to be overwritten by
anybody.

Which makes me wonder why these refspec.src and refspec.dst members
are not "const char *" pointers in the first place?  Obviously we do
not expect "refs/heads/*" to be overwritten after storing the pointer
to it in these members and making the get_fetch_map() call.


^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 03/19] global: convert intentionally-leaking config strings to consts
  2024-05-29 12:44 ` [PATCH 03/19] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
@ 2024-05-29 17:28   ` Junio C Hamano
  2024-05-30 11:30     ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 17:28 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

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.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 05/19] reftable: improve const correctness when assigning string constants
  2024-05-29 12:44 ` [PATCH 05/19] reftable: improve const correctness when assigning " Patrick Steinhardt
@ 2024-05-29 17:43   ` Junio C Hamano
  2024-05-30 11:30     ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 17:43 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> diff --git a/reftable/basics_test.c b/reftable/basics_test.c
> index 997c4d9e01..af9209d535 100644
> --- a/reftable/basics_test.c
> +++ b/reftable/basics_test.c
> @@ -58,8 +58,8 @@ static void test_binsearch(void)
>  
>  static void test_names_length(void)
>  {
> -	char *a[] = { "a", "b", NULL };
> -	EXPECT(names_length(a) == 2);
> +	char *names[] = { (char *)"a", (char *)"b", NULL };
> +	EXPECT(names_length(names) == 2);
>  }

I would have preferred to see this kind of rewrite more than
separate and clearly writable variables that are initialied with the
constant contents e.g. branches[] = "refs/heads/*", we saw in
earlier steps.  Wouldn't that approach, combined with making the
literal constants stored in read-only segment to trigger runtime
failure when a bug causes the "unfortunately non-const" variables
to be written, give us a better result?

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 06/19] refspec: remove global tag refspec structure
  2024-05-29 12:44 ` [PATCH 06/19] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-05-29 17:47   ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 17:47 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> +	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
> ...
> -static struct refspec_item s_tag_refspec = {
> -	.force = 0,
> -	.pattern = 1,
> -	.matching = 0,
> -	.exact_sha1 = 0,
> -	.negative = 0,
> -	.src = "refs/tags/*",
> -	.dst = "refs/tags/*",
> -};

Regardless of the constness issue, replacing these hardcoded
initializer values that is an accident waiting to happen with a call
to refspec_item_init() is very much welcomed.

Unless parse_refspec() is an immensely high cost operation, which it
isn't.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 07/19] http: do not assign string constant to non-const field
  2024-05-29 12:44 ` [PATCH 07/19] http: do not assign string constant to non-const field Patrick Steinhardt
@ 2024-05-29 19:39   ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 19:39 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> In `write_accept_language()`, we put all acceptable languages into an
> array. While all entries in that array are allocated strings, the final
> entry in that array is a string constant. This is fine because we
> explicitly skip over the last entry when freeing the array, but will
> cause warnings once we enable `-Wwrite-strings`.
>
> Adapt the code to also allocate the final entry.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  http.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
>
> diff --git a/http.c b/http.c
> index 67cc47d28f..2dea2d03da 100644
> --- a/http.c
> +++ b/http.c
> @@ -1974,7 +1974,7 @@ static void write_accept_language(struct strbuf *buf)
>  
>  		/* add '*' */
>  		REALLOC_ARRAY(language_tags, num_langs + 1);
> -		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
> +		language_tags[num_langs++] = xstrdup("*");
>  
>  		/* compute decimal_places */
>  		for (max_q = 1, decimal_places = 0;
> @@ -2004,8 +2004,7 @@ static void write_accept_language(struct strbuf *buf)
>  		}
>  	}
>  
> -	/* free language tags -- last one is a static '*' */
> -	for (i = 0; i < num_langs - 1; i++)
> +	for (i = 0; i < num_langs; i++)
>  		free(language_tags[i]);
>  	free(language_tags);
>  }

Makes sense, especially that this is done only once per process.


^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 08/19] line-log: always allocate the output prefix
  2024-05-29 12:44 ` [PATCH 08/19] line-log: always allocate the output prefix Patrick Steinhardt
@ 2024-05-29 19:51   ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 19:51 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> The returned string by `output_prefix()` is sometimes a string constant
> and sometimes an allocated string. This has been fine until now because
> we always leak the allocated strings, and thus we never tried to free
> the string constant.
>
> Fix the code to always return an allocated string and free the returned
> value at all callsites.

Yay!

This leak always has bothered me, as it is not just once per process
invocation, but once per each commit that is shown.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 09/19] object-file: make `buf` parameter of `index_mem()` a constant
  2024-05-29 12:44 ` [PATCH 09/19] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
@ 2024-05-29 20:01   ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 20:01 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> The `buf` parameter of `index_mem()` is a non-constant string. This will
> break once we enable `-Wwrite-strings` because we also pass constants
> from at least one callsite.
>
> Adapt the parameter to be a constant. As we cannot free the buffer
> without casting now, this also requires us to move the lifetime of the
> nested buffer around.

Makes sense.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 12/19] remote-curl: avoid assigning string constant to non-const variable
  2024-05-29 12:44 ` [PATCH 12/19] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
@ 2024-05-29 20:21   ` Junio C Hamano
  2024-05-30 11:30     ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 20:21 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

>  		} else if (skip_prefix(buf.buf, "option ", &arg)) {
> +			const char *value = strchr(arg, ' ');
> +			size_t arglen;
>  			int result;
>  
> +			if (value) {
> +				arglen = value - arg;
> +				value++;
> +			} else {
> +				arglen = strlen(arg);
>  				value = "true";
> +			}

There is a micro optimization opportunity here.

	const char *value = strchrnul(arg, ' ');
	size_t arglen = value - arg;

	if (*value)
		value++; /* skip over SP */
	else
		value = "true";

But other than that, very cleanly done.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 13/19] revision: always store allocated strings in output encoding
  2024-05-29 12:45 ` [PATCH 13/19] revision: always store allocated strings in output encoding Patrick Steinhardt
@ 2024-05-29 20:23   ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 20:23 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> The `git_log_output_encoding` variable can be set via the `--encoding=`
> option. When doing so, we conditionally either assign it to the passed
> value, or if the value is "none" we assign it the empty string.
> Depending on which of the both code paths we pick though, the variable
> may end up being assigned either an allocated string or a string
> constant.
>
> This is somewhat risky and may easily lead to bugs when a different code
> path may want to reassign a new value to it, freeing the previous value.
> We already to this when parsing the "i18n.logoutputencoding" config in
> `git_default_i18n_config()`. But because the config is typically parsed
> before we parse command line options this has been fine so far.
>
> Regardless of that, safeguard the code such that the variable always
> contains an allocated string. While at it, also free the old value in
> case there was any to plug a potential memory leak.

Nice.  Now the thing always has to be freed once we are done.
Consistency is good.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf`
  2024-05-29 12:45 ` [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
@ 2024-05-29 20:55   ` Junio C Hamano
  2024-05-30 11:31     ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 20:55 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> We never free any of the config strings that we populate into the
> `struct imap_server_conf`. Fix this by creating a common exit path where
> we can free resources.

This is more like the previous step got rid of the anchor that made
these strings reachable, so we need to turn around to free them,
which is sort-of Meh, especially given that the leaked pieces of
memory are small and very much bounded.

The main benefit of this change is to allow us prepare on the
constness change in the other (read: API this thing uses from
elsewhere) parts of the system, which is a very worthy goal.

> While at it, drop the unused variables `imap_server_conf::name` and
> `nongit_ok`.

The removal of the .name member may be correct, but I suspect the
change to nongit_ok is a change in behaviour, and it could even be a
regression.

> -	setup_git_directory_gently(&nongit_ok);
> +	setup_git_directory_gently(NULL);

The general idea behind &nongit_ok is that

 - Usually setup_git_directory_gently() dies if NULL is passed
   instead of a pointer to &nongit_ok.  Most of the Git command
   wants to make sure they have a repository to operate on, so this
   is a reasonable default behaviour.

 - Some commands would want to work also without having any
   repository, possibly with limited capability (e.g., "git apply"
   may want to work as a better "GNU patch", but obviously it cannot
   do "git apply --binary --3way" without having the object
   database).  They tell setup_git_directory_gently() not to die
   when outside a repository by passing a pointer to &nongit_ok, and
   instead tell if we are in a repository by storing 0/1 in it.

The idea is that a command that is willing to work outside a
repository can disable selected features based on what it sees in
nongit_ok.  In the case of "imap-send", there is no such features
that it needs to special case, perhaps because everything it does is
supposed to work outside a repository?

So the short version of what worries me in this change is that we
used to be able to operate without having a repository at all, but
now we would barf if run outside a repository, no?

Thanks.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 17/19] builtin/rebase: adapt code to not assign string constants to non-const
  2024-05-29 12:45 ` [PATCH 17/19] builtin/rebase: adapt code to not assign string constants to non-const Patrick Steinhardt
@ 2024-05-29 21:01   ` Junio C Hamano
  2024-05-30 11:31     ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-29 21:01 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> When computing the rebase strategy we temporarily assign a string
> constant to `options.strategy` before we call `xstrdup()` on it.
> Furthermore, the default backend is being assigned a string constant via
> `REBASE_OPTIONS_INIT`. Both of these will cause warnings once we enable
> `-Wwrite-strings`.
>
> Adapt the code such that we only store allocated strings in those
> variables.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  builtin/rebase.c | 16 +++++++++-------
>  1 file changed, 9 insertions(+), 7 deletions(-)

One gripe I have in this change is that it used to be crystal clear
what the hardcoded default was (i.e. written in the initialization
data), but now you have to follow the program flow to see what the
hardcoded default is.

>  	if (options.type == REBASE_UNSPECIFIED) {
> -		if (!strcmp(options.default_backend, "merge"))
> +		if (!options.default_backend)
> +			options.type = REBASE_MERGE;
> +		else if (!strcmp(options.default_backend, "merge"))
>  			options.type = REBASE_MERGE;
>  		else if (!strcmp(options.default_backend, "apply"))
>  			options.type = REBASE_APPLY;

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 01/19] global: improve const correctness when assigning string constants
  2024-05-29 16:58   ` Junio C Hamano
@ 2024-05-30 11:29     ` Patrick Steinhardt
  0 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 11:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 1374 bytes --]

On Wed, May 29, 2024 at 09:58:56AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > We're about to enable `-Wwrite-strings`, which changes the type of
> > string constants to `const char[]`. Fix various sites where we assign
> > such constants to non-const variables.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> 
> This is a noisy change, and it is kind of surprising that ...
> 
> > diff --git a/builtin/bisect.c b/builtin/bisect.c
> > index a58432b9d9..dabce9b542 100644
> > --- a/builtin/bisect.c
> > +++ b/builtin/bisect.c
> > @@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
> >  	return bisect_clean_state();
> >  }
> >  
> > -static void log_commit(FILE *fp, char *fmt, const char *state,
> > +static void log_commit(FILE *fp,
> > +		       const char *fmt, const char *state,
> >  		       struct commit *commit)
> >  {
> >  	struct pretty_print_context pp = {0};
> 
> ... a change like this does not require any other change to the
> code inside the function (e.g., by making further cascading changes
> to what the function calls).
> 
> But applying this step alone and building would not give us any
> constness warning/error from the compiler, so the result looks good
> to the compiler, I guess?

Yup, any intermediate commit builds with DEVELOPER=1.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-29 17:25   ` Junio C Hamano
@ 2024-05-30 11:29     ` Patrick Steinhardt
  2024-05-30 19:38       ` Junio C Hamano
  0 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 11:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 2212 bytes --]

On Wed, May 29, 2024 at 10:25:47AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > +	char refspec_str[] = "refs/heads/*";
> >  
> >  	memset(&refspec, 0, sizeof(refspec));
> >  	refspec.force = 0;
> >  	refspec.pattern = 1;
> > -	refspec.src = refspec.dst = "refs/heads/*";
> > +	refspec.src = refspec.dst = refspec_str;
> >  	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
> >  	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
> >  				    fetch_map, 1);
> 
> I have no objections to changes along this line, but this makes me
> wonder if we somehow have ways to ensure that after everything is
> done, refspec_str[], empty_str[], and the like in other hunks remain
> to have their initial contents.  Stepping back a bit, without
> applying this series, if we told the compiler to store these
> constant strings in read-only segment, any attempt to write into
> refspec.src or refspec.dst string would have been caught at runtime
> as an error.  With the patch, that would no longer work.  The piece
> of memory in refspec_str[] is a fair game to be overwritten by
> anybody.

True. Ideally, we wouldn't have to do these acrobatics here in the first
place. That would likely require quite a lot more work and go beyond the
scope of this patch series.

> Which makes me wonder why these refspec.src and refspec.dst members
> are not "const char *" pointers in the first place?  Obviously we do
> not expect "refs/heads/*" to be overwritten after storing the pointer
> to it in these members and making the get_fetch_map() call.

Well, we do. Not in `get_fetch_map()`, but in `query_refspecs()`. It
does weird stuff where it writes the result into either `src` or `dst`
depending on which of these fields is provided by the caller. Which
means that one of the fields would typically be a constant, whereas the
other one will be allocated.

Really, once you start looking you find all kinds of weird interfaces
where we cannot quite make up our mind whether fields are controlled by
the caller or by the callee. This patch series certainly surfaces quite
some places that left me scratching my head.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 03/19] global: convert intentionally-leaking config strings to consts
  2024-05-29 17:28   ` Junio C Hamano
@ 2024-05-30 11:30     ` Patrick Steinhardt
  2024-05-30 16:00       ` Junio C Hamano
  0 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 11:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 1525 bytes --]

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

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 05/19] reftable: improve const correctness when assigning string constants
  2024-05-29 17:43   ` Junio C Hamano
@ 2024-05-30 11:30     ` Patrick Steinhardt
  2024-05-30 16:07       ` Junio C Hamano
  0 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 11:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 1459 bytes --]

On Wed, May 29, 2024 at 10:43:47AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > diff --git a/reftable/basics_test.c b/reftable/basics_test.c
> > index 997c4d9e01..af9209d535 100644
> > --- a/reftable/basics_test.c
> > +++ b/reftable/basics_test.c
> > @@ -58,8 +58,8 @@ static void test_binsearch(void)
> >  
> >  static void test_names_length(void)
> >  {
> > -	char *a[] = { "a", "b", NULL };
> > -	EXPECT(names_length(a) == 2);
> > +	char *names[] = { (char *)"a", (char *)"b", NULL };
> > +	EXPECT(names_length(names) == 2);
> >  }
> 
> I would have preferred to see this kind of rewrite more than
> separate and clearly writable variables that are initialied with the
> constant contents e.g. branches[] = "refs/heads/*", we saw in
> earlier steps.  Wouldn't that approach, combined with making the
> literal constants stored in read-only segment to trigger runtime
> failure when a bug causes the "unfortunately non-const" variables
> to be written, give us a better result?

Depends on what we mean by "better", I guess. But yeah, I was torn
myself when writing this commit because there are so many string
constants in the reftable tests that we assign to non-constant fields. I
didn't find the result particularly easy to read when putting each of
the constants into a separate variable.

Revisiting this again though I don't think it's all that bad. I'll adapt
accordingly.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 12/19] remote-curl: avoid assigning string constant to non-const variable
  2024-05-29 20:21   ` Junio C Hamano
@ 2024-05-30 11:30     ` Patrick Steinhardt
  0 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 11:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 753 bytes --]

On Wed, May 29, 2024 at 01:21:47PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> >  		} else if (skip_prefix(buf.buf, "option ", &arg)) {
> > +			const char *value = strchr(arg, ' ');
> > +			size_t arglen;
> >  			int result;
> >  
> > +			if (value) {
> > +				arglen = value - arg;
> > +				value++;
> > +			} else {
> > +				arglen = strlen(arg);
> >  				value = "true";
> > +			}
> 
> There is a micro optimization opportunity here.
> 
> 	const char *value = strchrnul(arg, ' ');
> 	size_t arglen = value - arg;
> 
> 	if (*value)
> 		value++; /* skip over SP */
> 	else
> 		value = "true";
> 
> But other than that, very cleanly done.

Indeed, that version looks nicer. Thanks!

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf`
  2024-05-29 20:55   ` Junio C Hamano
@ 2024-05-30 11:31     ` Patrick Steinhardt
  2024-05-30 16:30       ` Junio C Hamano
  0 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 11:31 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 3116 bytes --]

On Wed, May 29, 2024 at 01:55:13PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > We never free any of the config strings that we populate into the
> > `struct imap_server_conf`. Fix this by creating a common exit path where
> > we can free resources.
> 
> This is more like the previous step got rid of the anchor that made
> these strings reachable, so we need to turn around to free them,
> which is sort-of Meh, especially given that the leaked pieces of
> memory are small and very much bounded.
> 
> The main benefit of this change is to allow us prepare on the
> constness change in the other (read: API this thing uses from
> elsewhere) parts of the system, which is a very worthy goal.

That's the motivation in this series at least. But I also see it as a
good goal by itself to get rid of the global state that we had before
the preceding patch. It may not be necessary, but it certainly helps me
personally to reason about code better.

> > While at it, drop the unused variables `imap_server_conf::name` and
> > `nongit_ok`.
> 
> The removal of the .name member may be correct, but I suspect the
> change to nongit_ok is a change in behaviour, and it could even be a
> regression.
> 
> > -	setup_git_directory_gently(&nongit_ok);
> > +	setup_git_directory_gently(NULL);
> 
> The general idea behind &nongit_ok is that
> 
>  - Usually setup_git_directory_gently() dies if NULL is passed
>    instead of a pointer to &nongit_ok.  Most of the Git command
>    wants to make sure they have a repository to operate on, so this
>    is a reasonable default behaviour.
> 
>  - Some commands would want to work also without having any
>    repository, possibly with limited capability (e.g., "git apply"
>    may want to work as a better "GNU patch", but obviously it cannot
>    do "git apply --binary --3way" without having the object
>    database).  They tell setup_git_directory_gently() not to die
>    when outside a repository by passing a pointer to &nongit_ok, and
>    instead tell if we are in a repository by storing 0/1 in it.
> 
> The idea is that a command that is willing to work outside a
> repository can disable selected features based on what it sees in
> nongit_ok.  In the case of "imap-send", there is no such features
> that it needs to special case, perhaps because everything it does is
> supposed to work outside a repository?
> 
> So the short version of what worries me in this change is that we
> used to be able to operate without having a repository at all, but
> now we would barf if run outside a repository, no?

Oh, I wasn't aware that the parameter being `NULL` actually causes a
change in behaviour. Which nicely demonstrates that we have some missing
test coverage for git-imap-send(1).

In fact, it's not only "some". We don't have any test coverage at all
for git-imap-send(1) as far as I can see. Which does make me rest a bit
uneasy. And I suspect that it wouldn't be trivial to add given that it
kind of requires something that talks IMAP on the receiving end.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 17/19] builtin/rebase: adapt code to not assign string constants to non-const
  2024-05-29 21:01   ` Junio C Hamano
@ 2024-05-30 11:31     ` Patrick Steinhardt
  0 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 11:31 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 1402 bytes --]

On Wed, May 29, 2024 at 02:01:08PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > When computing the rebase strategy we temporarily assign a string
> > constant to `options.strategy` before we call `xstrdup()` on it.
> > Furthermore, the default backend is being assigned a string constant via
> > `REBASE_OPTIONS_INIT`. Both of these will cause warnings once we enable
> > `-Wwrite-strings`.
> >
> > Adapt the code such that we only store allocated strings in those
> > variables.
> >
> > Signed-off-by: Patrick Steinhardt <ps@pks.im>
> > ---
> >  builtin/rebase.c | 16 +++++++++-------
> >  1 file changed, 9 insertions(+), 7 deletions(-)
> 
> One gripe I have in this change is that it used to be crystal clear
> what the hardcoded default was (i.e. written in the initialization
> data), but now you have to follow the program flow to see what the
> hardcoded default is.

True. We could adapt the macro to instead `xstrdup()` the default
backend. But that feels a bit hidden, so I don't think this is a
partiuclarly great option.

An alternative would be to wrap initialization into a function
`rebase_options_init()` that instead allocates the default backend
string. We can then also create a `rebase_options_release()` counter
part that releases its resources such that this whole thing becomes a
bit more self contained.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v2 00/19] Compile with `-Wwrite-strings`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (19 preceding siblings ...)
  2024-05-29 12:52 ` [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
@ 2024-05-30 12:50 ` Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
                     ` (19 more replies)
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                   ` (3 subsequent siblings)
  24 siblings, 20 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:50 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 53934 bytes --]

Hi,

this is the second version of my patch series that prepares our code
base to compile with `-Wwrite-strings`. This warning will convert the
type of string constants from `char []` to `const char[]` so that it
becomes harder to for example write to or free such constants by
accident.

Changes compared to v2:

  - Merged the reftable-specific test into the second patch. Instead of
    adding casts, we now allocate the required strings on the stack.

  - Apply a micro-optimization to "remote-curl.c" that was suggested by
    Junio.

  - Restore the `nongit_ok` variable in "imap-send.c". It's somewhat
    concerning that we do not have test coverage for git-imap-send(1) at
    all. But it might be a bit more involved as we do not have any IMAP
    test infra, to the best of my knowledge.

  - Rework the patch to "builtin/rebase.c". It is now split into two
    patches. The first patch reworks initialization of the rebase
    options so that the defaults are still self-contained in a single
    place. And the second patch refactors how we set up the merge
    strategy.

Thanks!

Patrick

Patrick Steinhardt (19):
  global: improve const correctness when assigning string constants
  global: assign non-const strings as required
  global: convert intentionally-leaking config strings to consts
  compat/win32: fix const-correctness with string constants
  refspec: remove global tag refspec structure
  http: do not assign string constant to non-const field
  line-log: always allocate the output prefix
  object-file: make `buf` parameter of `index_mem()` a constant
  parse-options: cast long name for OPTION_ALIAS
  send-pack: always allocate receive status
  remote-curl: avoid assigning string constant to non-const variable
  revision: always store allocated strings in output encoding
  mailmap: always store allocated strings in mailmap blob
  imap-send: drop global `imap_server_conf` variable
  imap-send: fix leaking memory in `imap_server_conf`
  builtin/rebase: do not assign default backend to non-constant field
  builtin/rebase: always store allocated string in `options.strategy`
  builtin/merge: always store allocated strings in `pull_twohead`
  config.mak.dev: enable `-Wwrite-strings` warning

 builtin/bisect.c             |   3 +-
 builtin/blame.c              |   2 +-
 builtin/bugreport.c          |   2 +-
 builtin/check-ignore.c       |   4 +-
 builtin/clone.c              |  14 ++--
 builtin/commit.c             |   6 +-
 builtin/diagnose.c           |   2 +-
 builtin/fetch.c              |  11 ++-
 builtin/log.c                |   2 +-
 builtin/mailsplit.c          |   4 +-
 builtin/merge.c              |  18 +++--
 builtin/pull.c               |  52 +++++++-------
 builtin/rebase.c             |  81 ++++++++++++----------
 builtin/receive-pack.c       |   4 +-
 builtin/remote.c             |   3 +-
 builtin/revert.c             |   2 +-
 builtin/send-pack.c          |   2 +
 compat/basename.c            |  15 +++-
 compat/mingw.c               |  25 +++----
 compat/regex/regcomp.c       |   2 +-
 compat/winansi.c             |   2 +-
 config.mak.dev               |   1 +
 diff.c                       |   7 +-
 diffcore-rename.c            |   6 +-
 entry.c                      |   7 +-
 fmt-merge-msg.c              |   2 +-
 fsck.c                       |   2 +-
 fsck.h                       |   2 +-
 gpg-interface.c              |   6 +-
 http-backend.c               |   2 +-
 http.c                       |   5 +-
 ident.c                      |   9 ++-
 imap-send.c                  | 130 ++++++++++++++++++++---------------
 line-log.c                   |  21 +++---
 mailmap.c                    |   2 +-
 merge-ll.c                   |  11 ++-
 object-file.c                |  17 ++---
 parse-options.h              |   2 +-
 pretty.c                     |   7 +-
 refs.c                       |   2 +-
 refs.h                       |   2 +-
 refs/reftable-backend.c      |   5 +-
 refspec.c                    |  13 ----
 refspec.h                    |   1 -
 reftable/basics_test.c       |   5 +-
 reftable/block_test.c        |   4 +-
 reftable/merged_test.c       |  52 +++++++-------
 reftable/readwrite_test.c    |  60 +++++++++-------
 reftable/record.c            |   6 +-
 reftable/stack_test.c        |  87 ++++++++++++-----------
 remote-curl.c                |  53 +++++++-------
 revision.c                   |   3 +-
 run-command.c                |   2 +-
 send-pack.c                  |   2 +-
 t/helper/test-hashmap.c      |   3 +-
 t/helper/test-json-writer.c  |  10 +--
 t/helper/test-regex.c        |   4 +-
 t/helper/test-rot13-filter.c |   5 +-
 t/t3900-i18n-commit.sh       |   1 +
 t/t3901-i18n-patch.sh        |   1 +
 t/unit-tests/t-strbuf.c      |  10 +--
 trailer.c                    |   2 +-
 userdiff.c                   |  10 +--
 userdiff.h                   |  12 ++--
 wt-status.c                  |   2 +-
 65 files changed, 479 insertions(+), 373 deletions(-)

Range-diff against v1:
 1:  25c31e550f =  1:  25c31e550f global: improve const correctness when assigning string constants
 5:  dc5d85257e !  2:  3430bcc09b reftable: improve const correctness when assigning string constants
    @@ Metadata
     Author: Patrick Steinhardt <ps@pks.im>
     
      ## Commit message ##
    -    reftable: improve const correctness when assigning string constants
    +    global: assign non-const strings as required
     
    -    There are many cases in the reftable tests where we assign string
    -    constants to non-const fields. This is harmless because we know that
    -    those fields are only used for reading access, but will break once we
    -    enable `-Wwrite-strings`. Add explicit casts to prepare for this.
    +    There are several cases where we initialize non-const fields with string
    +    constants. This is invalid and will cause warnings once we enable the
    +    `-Wwrite-strings` warning. Adapt those cases to instead use string
    +    arrays.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
    + ## builtin/remote.c ##
    +@@ builtin/remote.c: static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
    + 	struct ref *ref, *matches;
    + 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
    + 	struct refspec_item refspec;
    ++	char refspec_str[] = "refs/heads/*";
    + 
    + 	memset(&refspec, 0, sizeof(refspec));
    + 	refspec.force = 0;
    + 	refspec.pattern = 1;
    +-	refspec.src = refspec.dst = "refs/heads/*";
    ++	refspec.src = refspec.dst = refspec_str;
    + 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
    + 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
    + 				    fetch_map, 1);
    +
    + ## diff.c ##
    +@@ diff.c: size_t fill_textconv(struct repository *r,
    + 		     struct diff_filespec *df,
    + 		     char **outbuf)
    + {
    ++	static char empty_str[] = "";
    + 	size_t size;
    + 
    + 	if (!driver) {
    + 		if (!DIFF_FILE_VALID(df)) {
    +-			*outbuf = "";
    ++			*outbuf = empty_str;
    + 			return 0;
    + 		}
    + 		if (diff_populate_filespec(r, df, NULL))
    +
    + ## entry.c ##
    +@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    + 	struct string_list_item *filter, *path;
    + 	struct progress *progress = NULL;
    + 	struct delayed_checkout *dco = state->delayed_checkout;
    ++	char empty_str[] = "";
    + 
    + 	if (!state->delayed_checkout)
    + 		return errs;
    +@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    + 			if (!async_query_available_blobs(filter->string, &available_paths)) {
    + 				/* Filter reported an error */
    + 				errs = 1;
    +-				filter->string = "";
    ++				filter->string = empty_str;
    + 				continue;
    + 			}
    + 			if (available_paths.nr <= 0) {
    +@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    + 				 * filter from the list (see
    + 				 * "string_list_remove_empty_items" call below).
    + 				 */
    +-				filter->string = "";
    ++				filter->string = empty_str;
    + 				continue;
    + 			}
    + 
    +@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    + 					 * Do not ask the filter for available blobs,
    + 					 * again, as the filter is likely buggy.
    + 					 */
    +-					filter->string = "";
    ++					filter->string = empty_str;
    + 					continue;
    + 				}
    + 				ce = index_file_exists(state->istate, path->string,
    +
    + ## ident.c ##
    +@@ ident.c: static struct passwd *xgetpwuid_self(int *is_bogus)
    + 	pw = getpwuid(getuid());
    + 	if (!pw) {
    + 		static struct passwd fallback;
    +-		fallback.pw_name = "unknown";
    ++		static char fallback_name[] = "unknown";
    + #ifndef NO_GECOS_IN_PWENT
    +-		fallback.pw_gecos = "Unknown";
    ++		static char fallback_gcos[] = "Unknown";
    ++#endif
    ++
    ++		fallback.pw_name = fallback_name;
    ++#ifndef NO_GECOS_IN_PWENT
    ++		fallback.pw_gecos = fallback_gcos;
    + #endif
    + 		pw = &fallback;
    + 		if (is_bogus)
    +
    + ## line-log.c ##
    +@@ line-log.c: static int process_diff_filepair(struct rev_info *rev,
    + 	struct range_set tmp;
    + 	struct diff_ranges diff;
    + 	mmfile_t file_parent, file_target;
    ++	char empty_str[] = "";
    + 
    + 	assert(pair->two->path);
    + 	while (rg) {
    +@@ line-log.c: static int process_diff_filepair(struct rev_info *rev,
    + 		file_parent.ptr = pair->one->data;
    + 		file_parent.size = pair->one->size;
    + 	} else {
    +-		file_parent.ptr = "";
    ++		file_parent.ptr = empty_str;
    + 		file_parent.size = 0;
    + 	}
    + 
    +
    + ## object-file.c ##
    +@@ object-file.c: static struct cached_object {
    + } *cached_objects;
    + static int cached_object_nr, cached_object_alloc;
    + 
    ++static char empty_tree_buf[] = "";
    + static struct cached_object empty_tree = {
    + 	.oid = {
    + 		.hash = EMPTY_TREE_SHA1_BIN_LITERAL,
    + 	},
    + 	.type = OBJ_TREE,
    +-	.buf = "",
    ++	.buf = empty_tree_buf,
    + };
    + 
    + static struct cached_object *find_cached_object(const struct object_id *oid)
    +
    + ## pretty.c ##
    +@@ pretty.c: static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
    + 		return 1;
    + 	case 'D':
    + 		{
    ++			char empty_str[] = "";
    + 			const struct decoration_options opts = {
    +-				.prefix = "",
    +-				.suffix = ""
    ++				.prefix = empty_str,
    ++				.suffix = empty_str,
    + 			};
    + 
    + 			format_decorations(sb, commit, c->auto_color, &opts);
    +
    + ## refs/reftable-backend.c ##
    +@@ refs/reftable-backend.c: static int write_copy_table(struct reftable_writer *writer, void *cb_data)
    + 	struct strbuf errbuf = STRBUF_INIT;
    + 	size_t logs_nr = 0, logs_alloc = 0, i;
    + 	const char *committer_info;
    ++	char head[] = "HEAD";
    + 	int ret;
    + 
    + 	committer_info = git_committer_info(0);
    +@@ refs/reftable-backend.c: static int write_copy_table(struct reftable_writer *writer, void *cb_data)
    + 		if (append_head_reflog) {
    + 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
    + 			logs[logs_nr] = logs[logs_nr - 1];
    +-			logs[logs_nr].refname = "HEAD";
    ++			logs[logs_nr].refname = head;
    + 			logs_nr++;
    + 		}
    + 	}
    +@@ refs/reftable-backend.c: static int write_copy_table(struct reftable_writer *writer, void *cb_data)
    + 	string_list_clear(&skip, 0);
    + 	strbuf_release(&errbuf);
    + 	for (i = 0; i < logs_nr; i++) {
    +-		if (!strcmp(logs[i].refname, "HEAD"))
    ++		if (logs[i].refname == head)
    + 			continue;
    + 		logs[i].refname = NULL;
    + 		reftable_log_record_release(&logs[i]);
    +
      ## reftable/basics_test.c ##
     @@ reftable/basics_test.c: static void test_binsearch(void)
      
    @@ reftable/basics_test.c: static void test_binsearch(void)
      {
     -	char *a[] = { "a", "b", NULL };
     -	EXPECT(names_length(a) == 2);
    -+	char *names[] = { (char *)"a", (char *)"b", NULL };
    ++	char a[] = "a", b[] = "b";
    ++	char *names[] = { a, b, NULL };
     +	EXPECT(names_length(names) == 2);
      }
      
      static void test_parse_names_normal(void)
     
      ## reftable/block_test.c ##
    +@@ reftable/block_test.c: license that can be found in the LICENSE file or at
    + static void test_block_read_write(void)
    + {
    + 	const int header_off = 21; /* random */
    +-	char *names[30];
    ++	char *names[30], empty_str[] = "";
    + 	const int N = ARRAY_SIZE(names);
    + 	const int block_size = 1024;
    + 	struct reftable_block block = { NULL };
     @@ reftable/block_test.c: static void test_block_read_write(void)
      	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
      			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
      
     -	rec.u.ref.refname = "";
    -+	rec.u.ref.refname = (char *)"";
    ++	rec.u.ref.refname = empty_str;
      	rec.u.ref.value_type = REFTABLE_REF_DELETION;
      	n = block_writer_add(&bw, &rec);
      	EXPECT(n == REFTABLE_API_ERROR);
     
      ## reftable/merged_test.c ##
     @@ reftable/merged_test.c: static void readers_destroy(struct reftable_reader **readers, size_t n)
    + 
      static void test_merged_between(void)
      {
    ++	char a[] = "a", b[] = "b";
      	struct reftable_ref_record r1[] = { {
     -		.refname = "b",
    -+		.refname = (char *)"b",
    ++		.refname = b,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_VAL1,
      		.value.val1 = { 1, 2, 3, 0 },
      	} };
      	struct reftable_ref_record r2[] = { {
     -		.refname = "a",
    -+		.refname = (char *)"a",
    ++		.refname = a,
      		.update_index = 2,
      		.value_type = REFTABLE_REF_DELETION,
      	} };
    -@@ reftable/merged_test.c: static void test_merged(void)
    +@@ reftable/merged_test.c: static void test_merged_between(void)
    + 
    + static void test_merged(void)
      {
    ++	char a[] = "a", b[] = "b", c[] = "c", d[] = "d";
      	struct reftable_ref_record r1[] = {
      		{
     -			.refname = "a",
    -+			.refname = (char *)"a",
    ++			.refname = a,
      			.update_index = 1,
      			.value_type = REFTABLE_REF_VAL1,
      			.value.val1 = { 1 },
      		},
      		{
     -			.refname = "b",
    -+			.refname = (char *)"b",
    ++			.refname = b,
      			.update_index = 1,
      			.value_type = REFTABLE_REF_VAL1,
      			.value.val1 = { 1 },
      		},
      		{
     -			.refname = "c",
    -+			.refname = (char *)"c",
    ++			.refname = c,
      			.update_index = 1,
      			.value_type = REFTABLE_REF_VAL1,
      			.value.val1 = { 1 },
    @@ reftable/merged_test.c: static void test_merged(void)
      	};
      	struct reftable_ref_record r2[] = { {
     -		.refname = "a",
    -+		.refname = (char *)"a",
    ++		.refname = a,
      		.update_index = 2,
      		.value_type = REFTABLE_REF_DELETION,
      	} };
      	struct reftable_ref_record r3[] = {
      		{
     -			.refname = "c",
    -+			.refname = (char *)"c",
    ++			.refname = c,
      			.update_index = 3,
      			.value_type = REFTABLE_REF_VAL1,
      			.value.val1 = { 2 },
      		},
      		{
     -			.refname = "d",
    -+			.refname = (char *)"d",
    ++			.refname = d,
      			.update_index = 3,
      			.value_type = REFTABLE_REF_VAL1,
      			.value.val1 = { 1 },
    -@@ reftable/merged_test.c: static void test_merged_logs(void)
    +@@ reftable/merged_test.c: merged_table_from_log_records(struct reftable_log_record **logs,
    + 
    + static void test_merged_logs(void)
      {
    ++	char a[] = "a";
    ++	char name[] = "jane doe", email[] = "jane@invalid";
    ++	char message1[] = "message1", message2[] = "message2";
    ++	char message3[] = "message3";
      	struct reftable_log_record r1[] = {
      		{
     -			.refname = "a",
    -+			.refname = (char *)"a",
    ++			.refname = a,
      			.update_index = 2,
      			.value_type = REFTABLE_LOG_UPDATE,
      			.value.update = {
    @@ reftable/merged_test.c: static void test_merged_logs(void)
     -				.name = "jane doe",
     -				.email = "jane@invalid",
     -				.message = "message2",
    -+				.name = (char *)"jane doe",
    -+				.email = (char *)"jane@invalid",
    -+				.message = (char *)"message2",
    ++				.name = name,
    ++				.email = email,
    ++				.message = message2,
      			}
      		},
      		{
     -			.refname = "a",
    -+			.refname = (char *)"a",
    ++			.refname = a,
      			.update_index = 1,
      			.value_type = REFTABLE_LOG_UPDATE,
      			.value.update = {
    @@ reftable/merged_test.c: static void test_merged_logs(void)
     -				.name = "jane doe",
     -				.email = "jane@invalid",
     -				.message = "message1",
    -+				.name = (char *)"jane doe",
    -+				.email = (char *)"jane@invalid",
    -+				.message = (char *)"message1",
    ++				.name = name,
    ++				.email = email,
    ++				.message = message1,
      			}
      		},
      	};
      	struct reftable_log_record r2[] = {
      		{
     -			.refname = "a",
    -+			.refname = (char *)"a",
    ++			.refname = a,
      			.update_index = 3,
      			.value_type = REFTABLE_LOG_UPDATE,
      			.value.update = {
    @@ reftable/merged_test.c: static void test_merged_logs(void)
     -				.name = "jane doe",
     -				.email = "jane@invalid",
     -				.message = "message3",
    -+				.name = (char *)"jane doe",
    -+				.email = (char *)"jane@invalid",
    -+				.message = (char *)"message3",
    ++				.name = name,
    ++				.email = email,
    ++				.message = message3,
      			}
      		},
      	};
      	struct reftable_log_record r3[] = {
      		{
     -			.refname = "a",
    -+			.refname = (char *)"a",
    ++			.refname = a,
      			.update_index = 2,
      			.value_type = REFTABLE_LOG_DELETION,
      		},
    -@@ reftable/merged_test.c: static void test_default_write_opts(void)
    +@@ reftable/merged_test.c: static void test_merged_logs(void)
    + 
    + static void test_default_write_opts(void)
    + {
    ++	char master[] = "master";
    + 	struct reftable_write_options opts = { 0 };
      	struct strbuf buf = STRBUF_INIT;
      	struct reftable_writer *w =
      		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
     -
      	struct reftable_ref_record rec = {
     -		.refname = "master",
    -+		.refname = (char *)"master",
    ++		.refname = master,
      		.update_index = 1,
      	};
      	int err;
     
      ## reftable/readwrite_test.c ##
    +@@ reftable/readwrite_test.c: static void write_table(char ***names, struct strbuf *buf, int N,
    + 	int i = 0, n;
    + 	struct reftable_log_record log = { NULL };
    + 	const struct reftable_stats *stats = NULL;
    ++	char message[] = "message";
    + 
    + 	REFTABLE_CALLOC_ARRAY(*names, N + 1);
    + 
     @@ reftable/readwrite_test.c: static void write_table(char ***names, struct strbuf *buf, int N,
      		log.update_index = update_index;
      		log.value_type = REFTABLE_LOG_UPDATE;
      		set_test_hash(log.value.update.new_hash, i);
     -		log.value.update.message = "message";
    -+		log.value.update.message = (char *)"message";
    ++		log.value.update.message = message;
      
      		n = reftable_writer_add_log(w, &log);
      		EXPECT(n == 0);
    -@@ reftable/readwrite_test.c: static void test_log_buffer_size(void)
    +@@ reftable/readwrite_test.c: static void write_table(char ***names, struct strbuf *buf, int N,
    + 
    + static void test_log_buffer_size(void)
    + {
    ++	char refname[] = "refs/heads/master";
    ++	char name[] = "Han-Wen Hienhuys";
    ++	char email[] = "hanwen@google.com";
    ++	char message[] = "commit: 9\n";
    + 	struct strbuf buf = STRBUF_INIT;
    + 	struct reftable_write_options opts = {
    + 		.block_size = 4096,
      	};
      	int err;
      	int i;
    @@ reftable/readwrite_test.c: static void test_log_buffer_size(void)
     -					   .message = "commit: 9\n",
     -				   } } };
     +	struct reftable_log_record log = {
    -+		.refname = (char *)"refs/heads/master",
    ++		.refname = refname,
     +		.update_index = 0xa,
     +		.value_type = REFTABLE_LOG_UPDATE,
     +		.value.update = {
    -+			.name = (char *)"Han-Wen Nienhuys",
    -+			.email = (char *)"hanwen@google.com",
    ++			.name = name,
    ++			.email = email,
     +			.tz_offset = 100,
     +			.time = 0x5e430672,
    -+			.message = (char *)"commit: 9\n",
    ++			.message = message,
     +		},
     +	};
      	struct reftable_writer *w =
      		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
      
    +@@ reftable/readwrite_test.c: static void test_log_buffer_size(void)
    + 
    + static void test_log_overflow(void)
    + {
    ++	char refname[] = "refs/heads/master";
    ++	char name[] = "Han-Wen Hienhuys";
    ++	char email[] = "hanwen@google.com";
    + 	struct strbuf buf = STRBUF_INIT;
    + 	char msg[256] = { 0 };
    + 	struct reftable_write_options opts = {
     @@ reftable/readwrite_test.c: static void test_log_overflow(void)
      	};
      	int err;
      	struct reftable_log_record log = {
     -		.refname = "refs/heads/master",
    -+		.refname = (char *)"refs/heads/master",
    ++		.refname = refname,
      		.update_index = 0xa,
      		.value_type = REFTABLE_LOG_UPDATE,
      		.value = {
    @@ reftable/readwrite_test.c: static void test_log_overflow(void)
      				.new_hash = { 2 },
     -				.name = "Han-Wen Nienhuys",
     -				.email = "hanwen@google.com",
    -+				.name = (char *)"Han-Wen Nienhuys",
    -+				.email = (char *)"hanwen@google.com",
    ++				.name = name,
    ++				.email = email,
      				.tz_offset = 100,
      				.time = 0x5e430672,
      				.message = msg,
     @@ reftable/readwrite_test.c: static void test_log_zlib_corruption(void)
    + 	struct reftable_writer *w =
    + 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
    + 	const struct reftable_stats *stats = NULL;
    ++	char refname[] = "refname";
    ++	char name[] = "My Name";
    ++	char email[] = "myname@invalid";
      	char message[100] = { 0 };
      	int err, i, n;
      	struct reftable_log_record log = {
     -		.refname = "refname",
    -+		.refname = (char *)"refname",
    ++		.refname = refname,
      		.value_type = REFTABLE_LOG_UPDATE,
      		.value = {
      			.update = {
    @@ reftable/readwrite_test.c: static void test_log_zlib_corruption(void)
      				.old_hash = { 2 },
     -				.name = "My Name",
     -				.email = "myname@invalid",
    -+				.name = (char *)"My Name",
    -+				.email = (char *)"myname@invalid",
    ++				.name = name,
    ++				.email = email,
      				.message = message,
      			},
      		},
     @@ reftable/readwrite_test.c: static void test_write_empty_key(void)
    + 	struct strbuf buf = STRBUF_INIT;
      	struct reftable_writer *w =
      		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
    ++	char refname[] = "";
      	struct reftable_ref_record ref = {
     -		.refname = "",
    -+		.refname = (char *)"",
    ++		.refname = refname,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_DELETION,
      	};
     @@ reftable/readwrite_test.c: static void test_write_key_order(void)
    + 	struct strbuf buf = STRBUF_INIT;
    + 	struct reftable_writer *w =
      		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
    ++	char a[] = "a", b[] = "b", target[] = "target";
      	struct reftable_ref_record refs[2] = {
      		{
     -			.refname = "b",
    -+			.refname = (char *)"b",
    ++			.refname = b,
      			.update_index = 1,
      			.value_type = REFTABLE_REF_SYMREF,
      			.value = {
     -				.symref = "target",
    -+				.symref = (char *)"target",
    ++				.symref = target,
      			},
      		}, {
     -			.refname = "a",
    -+			.refname = (char *)"a",
    ++			.refname = a,
      			.update_index = 1,
      			.value_type = REFTABLE_REF_SYMREF,
      			.value = {
     -				.symref = "target",
    -+				.symref = (char *)"target",
    ++				.symref = target,
      			},
      		}
      	};
    @@ reftable/stack_test.c: static void test_parse_names(void)
      
      static int write_test_ref(struct reftable_writer *wr, void *arg)
     @@ reftable/stack_test.c: static void test_reftable_stack_add_one(void)
    + 	};
      	struct reftable_stack *st = NULL;
      	int err;
    ++	char head[] = "HEAD", master[] = "master";
      	struct reftable_ref_record ref = {
     -		.refname = "HEAD",
    -+		.refname = (char *)"HEAD",
    ++		.refname = head,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_SYMREF,
     -		.value.symref = "master",
    -+		.value.symref = (char *)"master",
    ++		.value.symref = master,
      	};
      	struct reftable_ref_record dest = { NULL };
      	struct stat stat_result = { 0 };
     @@ reftable/stack_test.c: static void test_reftable_stack_uptodate(void)
    + 	char *dir = get_tmp_dir(__LINE__);
      
      	int err;
    ++	char head[] = "HEAD", branch2[] = "branch2", master[] = "master";
      	struct reftable_ref_record ref1 = {
     -		.refname = "HEAD",
    -+		.refname = (char *)"HEAD",
    ++		.refname = head,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_SYMREF,
     -		.value.symref = "master",
    -+		.value.symref = (char *)"master",
    ++		.value.symref = master,
      	};
      	struct reftable_ref_record ref2 = {
     -		.refname = "branch2",
    -+		.refname = (char *)"branch2",
    ++		.refname = branch2,
      		.update_index = 2,
      		.value_type = REFTABLE_REF_SYMREF,
     -		.value.symref = "master",
    -+		.value.symref = (char *)"master",
    ++		.value.symref = master,
      	};
      
      
     @@ reftable/stack_test.c: static void test_reftable_stack_transaction_api(void)
    + 	struct reftable_stack *st = NULL;
    + 	int err;
      	struct reftable_addition *add = NULL;
    - 
    +-
    ++	char head[] = "HEAD", master[] = "master";
      	struct reftable_ref_record ref = {
     -		.refname = "HEAD",
    -+		.refname = (char *)"HEAD",
    ++		.refname = head,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_SYMREF,
     -		.value.symref = "master",
    -+		.value.symref = (char *)"master",
    ++		.value.symref = master,
      	};
      	struct reftable_ref_record dest = { NULL };
      
     @@ reftable/stack_test.c: static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
    + 	EXPECT_ERR(err);
    + 
    + 	for (i = 0; i <= n; i++) {
    ++		char master[] = "master";
      		struct reftable_ref_record ref = {
      			.update_index = reftable_stack_next_update_index(st),
      			.value_type = REFTABLE_REF_SYMREF,
     -			.value.symref = "master",
    -+			.value.symref = (char *)"master",
    ++			.value.symref = master,
      		};
      		char name[100];
      
     @@ reftable/stack_test.c: static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
    + 
      static void test_reftable_stack_auto_compaction_fails_gracefully(void)
      {
    ++	char master[] = "refs/meads/master";
      	struct reftable_ref_record ref = {
     -		.refname = "refs/heads/master",
    -+		.refname = (char *)"refs/heads/master",
    ++		.refname = master,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_VAL1,
      		.value.val1 = {0x01},
    -@@ reftable/stack_test.c: static void test_reftable_stack_update_index_check(void)
    +@@ reftable/stack_test.c: static int write_error(struct reftable_writer *wr, void *arg)
    + static void test_reftable_stack_update_index_check(void)
    + {
    + 	char *dir = get_tmp_dir(__LINE__);
    +-
    + 	struct reftable_write_options cfg = { 0 };
      	struct reftable_stack *st = NULL;
      	int err;
    ++	char name1[] = "name1", name2[] = "name2", master[] = "master";
      	struct reftable_ref_record ref1 = {
     -		.refname = "name1",
    -+		.refname = (char *)"name1",
    ++		.refname = name1,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_SYMREF,
     -		.value.symref = "master",
    -+		.value.symref = (char *)"master",
    ++		.value.symref = master,
      	};
      	struct reftable_ref_record ref2 = {
     -		.refname = "name2",
    -+		.refname = (char *)"name2",
    ++		.refname = name2,
      		.update_index = 1,
      		.value_type = REFTABLE_REF_SYMREF,
     -		.value.symref = "master",
    -+		.value.symref = (char *)"master",
    ++		.value.symref = master,
      	};
      
      	err = reftable_new_stack(&st, dir, cfg);
     @@ reftable/stack_test.c: static void test_reftable_stack_log_normalize(void)
    + 	};
      	struct reftable_stack *st = NULL;
      	char *dir = get_tmp_dir(__LINE__);
    ++	char branch[] = "branch";
    ++	char onetwomessage[] = "one\ntwo";
    ++	char onemessage[] = "one";
    ++	char twomessage[] = "two\n";
      	struct reftable_log_record input = {
     -		.refname = "branch",
    -+		.refname = (char *)"branch",
    ++		.refname = branch,
      		.update_index = 1,
      		.value_type = REFTABLE_LOG_UPDATE,
      		.value = {
    @@ reftable/stack_test.c: static void test_reftable_stack_log_normalize(void)
      	EXPECT_ERR(err);
      
     -	input.value.update.message = "one\ntwo";
    -+	input.value.update.message = (char *)"one\ntwo";
    ++	input.value.update.message = onetwomessage;
      	err = reftable_stack_add(st, &write_test_log, &arg);
      	EXPECT(err == REFTABLE_API_ERROR);
      
     -	input.value.update.message = "one";
    -+	input.value.update.message = (char *)"one";
    ++	input.value.update.message = onemessage;
      	err = reftable_stack_add(st, &write_test_log, &arg);
      	EXPECT_ERR(err);
      
    @@ reftable/stack_test.c: static void test_reftable_stack_log_normalize(void)
      	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
      
     -	input.value.update.message = "two\n";
    -+	input.value.update.message = (char *)"two\n";
    ++	input.value.update.message = twomessage;
      	arg.update_index = 2;
      	err = reftable_stack_add(st, &write_test_log, &arg);
      	EXPECT_ERR(err);
    -@@ reftable/stack_test.c: static void test_reftable_stack_hash_id(void)
    +@@ reftable/stack_test.c: static void test_reftable_stack_tombstone(void)
    + static void test_reftable_stack_hash_id(void)
    + {
    + 	char *dir = get_tmp_dir(__LINE__);
    +-
    + 	struct reftable_write_options cfg = { 0 };
    + 	struct reftable_stack *st = NULL;
      	int err;
    - 
    +-
    ++	char master[] = "master", target[] = "target";
      	struct reftable_ref_record ref = {
     -		.refname = "master",
    -+		.refname = (char *)"master",
    ++		.refname = master,
      		.value_type = REFTABLE_REF_SYMREF,
     -		.value.symref = "target",
    -+		.value.symref = (char *)"target",
    ++		.value.symref = target,
      		.update_index = 1,
      	};
      	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
     @@ reftable/stack_test.c: static void test_reftable_stack_auto_compaction(void)
    + 	EXPECT_ERR(err);
    + 
    + 	for (i = 0; i < N; i++) {
    +-		char name[100];
    ++		char name[100], master[] = "master";
    + 		struct reftable_ref_record ref = {
      			.refname = name,
      			.update_index = reftable_stack_next_update_index(st),
      			.value_type = REFTABLE_REF_SYMREF,
     -			.value.symref = "master",
    -+			.value.symref = (char *)"master",
    ++			.value.symref = master,
      		};
      		snprintf(name, sizeof(name), "branch%04d", i);
      
     @@ reftable/stack_test.c: static void test_reftable_stack_add_performs_auto_compaction(void)
    + 	EXPECT_ERR(err);
    + 
    + 	for (i = 0; i <= n; i++) {
    ++		char master[] = "master";
      		struct reftable_ref_record ref = {
      			.update_index = reftable_stack_next_update_index(st),
      			.value_type = REFTABLE_REF_SYMREF,
     -			.value.symref = "master",
    -+			.value.symref = (char *)"master",
    ++			.value.symref = master,
      		};
      
      		/*
     @@ reftable/stack_test.c: static void test_reftable_stack_compaction_concurrent(void)
    + 	EXPECT_ERR(err);
    + 
    + 	for (i = 0; i < N; i++) {
    +-		char name[100];
    ++		char name[100], master[] = "master";
    + 		struct reftable_ref_record ref = {
      			.refname = name,
      			.update_index = reftable_stack_next_update_index(st1),
      			.value_type = REFTABLE_REF_SYMREF,
     -			.value.symref = "master",
    -+			.value.symref = (char *)"master",
    ++			.value.symref = master,
      		};
      		snprintf(name, sizeof(name), "branch%04d", i);
      
     @@ reftable/stack_test.c: static void test_reftable_stack_compaction_concurrent_clean(void)
    + 	EXPECT_ERR(err);
    + 
    + 	for (i = 0; i < N; i++) {
    +-		char name[100];
    ++		char name[100], master[] = "master";
    + 		struct reftable_ref_record ref = {
      			.refname = name,
      			.update_index = reftable_stack_next_update_index(st1),
      			.value_type = REFTABLE_REF_SYMREF,
     -			.value.symref = "master",
    -+			.value.symref = (char *)"master",
    ++			.value.symref = master,
      		};
      		snprintf(name, sizeof(name), "branch%04d", i);
      
 3:  8f3decbb76 !  3:  8b71dfa208 global: convert intentionally-leaking config strings to consts
    @@ Commit message
             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.
    +    All of these are intentionally kept alive and never free'd. Furthermore,
    +    all of these are being assigned both string constants in some places,
    +    and allocated strings in other places. This will cause warnings once we
    +    enable `-Wwrite-strings`, so 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>
     
 4:  ba50e49f86 =  4:  961b3357d5 compat/win32: fix const-correctness with string constants
 6:  0eaa73c109 =  5:  b73a45133b refspec: remove global tag refspec structure
 7:  03b13c449b =  6:  6da87a0905 http: do not assign string constant to non-const field
 8:  699eeae92c =  7:  3da7df97a5 line-log: always allocate the output prefix
 9:  6cbb8444a6 =  8:  e5d14a5173 object-file: make `buf` parameter of `index_mem()` a constant
10:  c07b27bbb4 =  9:  dd40c7464d parse-options: cast long name for OPTION_ALIAS
11:  3cd28ae38c = 10:  462502127d send-pack: always allocate receive status
12:  00b4a7dbbc ! 11:  884fbe1da5 remote-curl: avoid assigning string constant to non-const variable
    @@ remote-curl.c: int cmd_main(int argc, const char **argv)
      
      		} else if (skip_prefix(buf.buf, "option ", &arg)) {
     -			char *value = strchr(arg, ' ');
    -+			const char *value = strchr(arg, ' ');
    -+			size_t arglen;
    ++			const char *value = strchrnul(arg, ' ');
    ++			size_t arglen = value - arg;
      			int result;
      
     -			if (value)
     -				*value++ = '\0';
    --			else
    -+			if (value) {
    -+				arglen = value - arg;
    -+				value++;
    -+			} else {
    -+				arglen = strlen(arg);
    ++			if (*value)
    ++				value++; /* skip over SP */
    + 			else
      				value = "true";
    -+			}
      
     -			result = set_option(arg, value);
     +			result = set_option(arg, arglen, value);
13:  68a7d24e4a = 12:  502380c2ca revision: always store allocated strings in output encoding
14:  0e393fa6a7 = 13:  ffacdc3779 mailmap: always store allocated strings in mailmap blob
15:  18ba9f7b3b = 14:  c0fce9b87e imap-send: drop global `imap_server_conf` variable
16:  357d69fa8b ! 15:  e0a5b83f0e imap-send: fix leaking memory in `imap_server_conf`
    @@ Commit message
         `struct imap_server_conf`. Fix this by creating a common exit path where
         we can free resources.
     
    -    While at it, drop the unused variables `imap_server_conf::name` and
    -    `nongit_ok`.
    +    While at it, drop the unused member `imap_server_conf::name`.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
    @@ imap-send.c: static int git_imap_config(const char *var, const char *val,
      	return 0;
      }
     @@ imap-send.c: int cmd_main(int argc, const char **argv)
    - 	};
      	struct strbuf all_msgs = STRBUF_INIT;
      	int total;
    --	int nongit_ok;
    + 	int nongit_ok;
     +	int ret;
      
    --	setup_git_directory_gently(&nongit_ok);
    -+	setup_git_directory_gently(NULL);
    + 	setup_git_directory_gently(&nongit_ok);
      	git_config(git_imap_config, &server);
    - 
    - 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
     @@ imap-send.c: int cmd_main(int argc, const char **argv)
      
      	if (!server.folder) {
 2:  51ee5660a1 ! 16:  36a7b0a4b0 global: assign non-const strings as required
    @@ Metadata
     Author: Patrick Steinhardt <ps@pks.im>
     
      ## Commit message ##
    -    global: assign non-const strings as required
    +    builtin/rebase: do not assign default backend to non-constant field
     
    -    There are several cases where we initialize non-const fields with string
    -    constants. This is invalid and will cause warnings once we enable the
    -    `-Wwrite-strings` warning. Adapt those cases to instead use string
    -    arrays.
    +    The `struct rebase_options::default_backend` field is a non-constant
    +    string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
    +    Refactor the code to initialize and release options via two functions
    +    `rebase_options_init()` and `rebase_options_release()`. Like this, we
    +    can easily adapt the former funnction to use `xstrdup()` on the default
    +    value without hiding it away in a macro.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
    - ## builtin/remote.c ##
    -@@ builtin/remote.c: static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
    - 	struct ref *ref, *matches;
    - 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
    - 	struct refspec_item refspec;
    -+	char refspec_str[] = "refs/heads/*";
    - 
    - 	memset(&refspec, 0, sizeof(refspec));
    - 	refspec.force = 0;
    - 	refspec.pattern = 1;
    --	refspec.src = refspec.dst = "refs/heads/*";
    -+	refspec.src = refspec.dst = refspec_str;
    - 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
    - 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
    - 				    fetch_map, 1);
    -
    - ## diff.c ##
    -@@ diff.c: size_t fill_textconv(struct repository *r,
    - 		     struct diff_filespec *df,
    - 		     char **outbuf)
    - {
    -+	static char empty_str[] = "";
    - 	size_t size;
    - 
    - 	if (!driver) {
    - 		if (!DIFF_FILE_VALID(df)) {
    --			*outbuf = "";
    -+			*outbuf = empty_str;
    - 			return 0;
    - 		}
    - 		if (diff_populate_filespec(r, df, NULL))
    -
    - ## entry.c ##
    -@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    - 	struct string_list_item *filter, *path;
    - 	struct progress *progress = NULL;
    - 	struct delayed_checkout *dco = state->delayed_checkout;
    -+	char empty_str[] = "";
    - 
    - 	if (!state->delayed_checkout)
    - 		return errs;
    -@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    - 			if (!async_query_available_blobs(filter->string, &available_paths)) {
    - 				/* Filter reported an error */
    - 				errs = 1;
    --				filter->string = "";
    -+				filter->string = empty_str;
    - 				continue;
    - 			}
    - 			if (available_paths.nr <= 0) {
    -@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    - 				 * filter from the list (see
    - 				 * "string_list_remove_empty_items" call below).
    - 				 */
    --				filter->string = "";
    -+				filter->string = empty_str;
    - 				continue;
    - 			}
    + ## builtin/rebase.c ##
    +@@ builtin/rebase.c: struct rebase_options {
    + 	int config_update_refs;
    + };
      
    -@@ entry.c: int finish_delayed_checkout(struct checkout *state, int show_progress)
    - 					 * Do not ask the filter for available blobs,
    - 					 * again, as the filter is likely buggy.
    - 					 */
    --					filter->string = "";
    -+					filter->string = empty_str;
    - 					continue;
    - 				}
    - 				ce = index_file_exists(state->istate, path->string,
    -
    - ## ident.c ##
    -@@ ident.c: static struct passwd *xgetpwuid_self(int *is_bogus)
    - 	pw = getpwuid(getuid());
    - 	if (!pw) {
    - 		static struct passwd fallback;
    --		fallback.pw_name = "unknown";
    -+		static char fallback_name[] = "unknown";
    - #ifndef NO_GECOS_IN_PWENT
    --		fallback.pw_gecos = "Unknown";
    -+		static char fallback_gcos[] = "Unknown";
    -+#endif
    +-#define REBASE_OPTIONS_INIT {			  	\
    +-		.type = REBASE_UNSPECIFIED,	  	\
    +-		.empty = EMPTY_UNSPECIFIED,	  	\
    +-		.keep_empty = 1,			\
    +-		.default_backend = "merge",	  	\
    +-		.flags = REBASE_NO_QUIET, 		\
    +-		.git_am_opts = STRVEC_INIT,		\
    +-		.exec = STRING_LIST_INIT_NODUP,		\
    +-		.git_format_patch_opt = STRBUF_INIT,	\
    +-		.fork_point = -1,			\
    +-		.reapply_cherry_picks = -1,             \
    +-		.allow_empty_message = 1,               \
    +-		.autosquash = -1,                       \
    +-		.rebase_merges = -1,                    \
    +-		.config_rebase_merges = -1,             \
    +-		.update_refs = -1,                      \
    +-		.config_update_refs = -1,               \
    +-		.strategy_opts = STRING_LIST_INIT_NODUP,\
    +-	}
    ++static void rebase_options_init(struct rebase_options *opts)
    ++{
    ++	memset(opts, 0, sizeof(*opts));
    ++	opts->type = REBASE_UNSPECIFIED;
    ++	opts->empty = EMPTY_UNSPECIFIED;
    ++	opts->default_backend = xstrdup("merge");
    ++	opts->keep_empty = 1;
    ++	opts->flags = REBASE_NO_QUIET;
    ++	strvec_init(&opts->git_am_opts);
    ++	string_list_init_nodup(&opts->exec);
    ++	strbuf_init(&opts->git_format_patch_opt, 0);
    ++	opts->fork_point = -1;
    ++	opts->reapply_cherry_picks = -1;
    ++	opts->allow_empty_message = 1;
    ++	opts->autosquash = -1;
    ++	opts->rebase_merges = -1;
    ++	opts->config_rebase_merges = -1;
    ++	opts->update_refs = -1;
    ++	opts->config_update_refs = -1;
    ++	string_list_init_nodup(&opts->strategy_opts);
    ++}
     +
    -+		fallback.pw_name = fallback_name;
    -+#ifndef NO_GECOS_IN_PWENT
    -+		fallback.pw_gecos = fallback_gcos;
    - #endif
    - 		pw = &fallback;
    - 		if (is_bogus)
    -
    - ## line-log.c ##
    -@@ line-log.c: static int process_diff_filepair(struct rev_info *rev,
    - 	struct range_set tmp;
    - 	struct diff_ranges diff;
    - 	mmfile_t file_parent, file_target;
    -+	char empty_str[] = "";
    ++static void rebase_options_release(struct rebase_options *opts)
    ++{
    ++	free(opts->default_backend);
    ++	free(opts->reflog_action);
    ++	free(opts->head_name);
    ++	strvec_clear(&opts->git_am_opts);
    ++	free(opts->gpg_sign_opt);
    ++	string_list_clear(&opts->exec, 0);
    ++	free(opts->strategy);
    ++	string_list_clear(&opts->strategy_opts, 0);
    ++	strbuf_release(&opts->git_format_patch_opt);
    ++}
      
    - 	assert(pair->two->path);
    - 	while (rg) {
    -@@ line-log.c: static int process_diff_filepair(struct rev_info *rev,
    - 		file_parent.ptr = pair->one->data;
    - 		file_parent.size = pair->one->size;
    - 	} else {
    --		file_parent.ptr = "";
    -+		file_parent.ptr = empty_str;
    - 		file_parent.size = 0;
    + static struct replay_opts get_replay_opts(const struct rebase_options *opts)
    + {
    +@@ builtin/rebase.c: static int rebase_config(const char *var, const char *value,
      	}
      
    -
    - ## object-file.c ##
    -@@ object-file.c: static struct cached_object {
    - } *cached_objects;
    - static int cached_object_nr, cached_object_alloc;
    - 
    -+static char empty_tree_buf[] = "";
    - static struct cached_object empty_tree = {
    - 	.oid = {
    - 		.hash = EMPTY_TREE_SHA1_BIN_LITERAL,
    - 	},
    - 	.type = OBJ_TREE,
    --	.buf = "",
    -+	.buf = empty_tree_buf,
    - };
    + 	if (!strcmp(var, "rebase.backend")) {
    ++		FREE_AND_NULL(opts->default_backend);
    + 		return git_config_string(&opts->default_backend, var, value);
    + 	}
      
    - static struct cached_object *find_cached_object(const struct object_id *oid)
    -
    - ## pretty.c ##
    -@@ pretty.c: static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
    - 		return 1;
    - 	case 'D':
    - 		{
    -+			char empty_str[] = "";
    - 			const struct decoration_options opts = {
    --				.prefix = "",
    --				.suffix = ""
    -+				.prefix = empty_str,
    -+				.suffix = empty_str,
    - 			};
    +@@ builtin/rebase.c: static int check_exec_cmd(const char *cmd)
      
    - 			format_decorations(sb, commit, c->auto_color, &opts);
    -
    - ## refs/reftable-backend.c ##
    -@@ refs/reftable-backend.c: static int write_copy_table(struct reftable_writer *writer, void *cb_data)
    - 	struct strbuf errbuf = STRBUF_INIT;
    - 	size_t logs_nr = 0, logs_alloc = 0, i;
    - 	const char *committer_info;
    -+	char head[] = "HEAD";
    - 	int ret;
    + int cmd_rebase(int argc, const char **argv, const char *prefix)
    + {
    +-	struct rebase_options options = REBASE_OPTIONS_INIT;
    ++	struct rebase_options options;
    + 	const char *branch_name;
    + 	int ret, flags, total_argc, in_progress = 0;
    + 	int keep_base = 0;
    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + 	};
    + 	int i;
      
    - 	committer_info = git_committer_info(0);
    -@@ refs/reftable-backend.c: static int write_copy_table(struct reftable_writer *writer, void *cb_data)
    - 		if (append_head_reflog) {
    - 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
    - 			logs[logs_nr] = logs[logs_nr - 1];
    --			logs[logs_nr].refname = "HEAD";
    -+			logs[logs_nr].refname = head;
    - 			logs_nr++;
    - 		}
    - 	}
    -@@ refs/reftable-backend.c: static int write_copy_table(struct reftable_writer *writer, void *cb_data)
    - 	string_list_clear(&skip, 0);
    - 	strbuf_release(&errbuf);
    - 	for (i = 0; i < logs_nr; i++) {
    --		if (!strcmp(logs[i].refname, "HEAD"))
    -+		if (logs[i].refname == head)
    - 			continue;
    - 		logs[i].refname = NULL;
    - 		reftable_log_record_release(&logs[i]);
    ++	rebase_options_init(&options);
    ++
    + 	if (argc == 2 && !strcmp(argv[1], "-h"))
    + 		usage_with_options(builtin_rebase_usage,
    + 				   builtin_rebase_options);
    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + cleanup:
    + 	strbuf_release(&buf);
    + 	strbuf_release(&revisions);
    +-	free(options.reflog_action);
    +-	free(options.head_name);
    +-	strvec_clear(&options.git_am_opts);
    +-	free(options.gpg_sign_opt);
    +-	string_list_clear(&options.exec, 0);
    +-	free(options.strategy);
    +-	string_list_clear(&options.strategy_opts, 0);
    +-	strbuf_release(&options.git_format_patch_opt);
    ++	rebase_options_release(&options);
    + 	free(squash_onto_name);
    + 	free(keep_base_onto_name);
    + 	return !!ret;
17:  16d3d28243 ! 17:  3552ab9748 builtin/rebase: adapt code to not assign string constants to non-const
    @@ Metadata
     Author: Patrick Steinhardt <ps@pks.im>
     
      ## Commit message ##
    -    builtin/rebase: adapt code to not assign string constants to non-const
    +    builtin/rebase: always store allocated string in `options.strategy`
     
    -    When computing the rebase strategy we temporarily assign a string
    -    constant to `options.strategy` before we call `xstrdup()` on it.
    -    Furthermore, the default backend is being assigned a string constant via
    -    `REBASE_OPTIONS_INIT`. Both of these will cause warnings once we enable
    -    `-Wwrite-strings`.
    +    The `struct rebase_options::strategy` field is a `char *`, but we do end
    +    up assigning string constants to it in two cases:
     
    -    Adapt the code such that we only store allocated strings in those
    -    variables.
    +      - When being passed a `--strategy=` option via the command line.
    +
    +      - When being passed a strategy option via `--strategy-option=`, but
    +        not a strategy.
    +
    +    This will cause warnings once we enable `-Wwrite-strings`.
    +
    +    Ideally, we'd just convert the field to be a `const char *`. But we also
    +    assign to this field via the GIT_TEST_MERGE_ALGORITHM envvar, which we
    +    have to strdup(3P) into it.
    +
    +    Instead, refactor the code to make sure that we only ever assign
    +    allocated strings to this field.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## builtin/rebase.c ##
    -@@ builtin/rebase.c: struct rebase_options {
    - 		.type = REBASE_UNSPECIFIED,	  	\
    - 		.empty = EMPTY_UNSPECIFIED,	  	\
    - 		.keep_empty = 1,			\
    --		.default_backend = "merge",	  	\
    - 		.flags = REBASE_NO_QUIET, 		\
    - 		.git_am_opts = STRVEC_INIT,		\
    - 		.exec = STRING_LIST_INIT_NODUP,		\
    -@@ builtin/rebase.c: static int rebase_config(const char *var, const char *value,
    - 	}
    - 
    - 	if (!strcmp(var, "rebase.backend")) {
    -+		FREE_AND_NULL(opts->default_backend);
    - 		return git_config_string(&opts->default_backend, var, value);
    - 	}
    - 
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + {
    + 	struct rebase_options options;
    + 	const char *branch_name;
    ++	const char *strategy_opt = NULL;
    + 	int ret, flags, total_argc, in_progress = 0;
    + 	int keep_base = 0;
    + 	int ok_to_skip_pre_rebase = 0;
    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + 			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
    + 		OPT_BOOL(0, "fork-point", &options.fork_point,
    + 			 N_("use 'merge-base --fork-point' to refine upstream")),
    +-		OPT_STRING('s', "strategy", &options.strategy,
    ++		OPT_STRING('s', "strategy", &strategy_opt,
    + 			   N_("strategy"), N_("use the given merge strategy")),
    + 		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
    + 				N_("option"),
    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + 		}
      	}
      
    - 	if (options.strategy_opts.nr && !options.strategy)
    +-	if (options.strategy_opts.nr && !options.strategy)
     -		options.strategy = "ort";
     -
     -	if (options.strategy) {
     -		options.strategy = xstrdup(options.strategy);
    ++	if (strategy_opt)
    ++		options.strategy = xstrdup(strategy_opt);
    ++	else if (options.strategy_opts.nr && !options.strategy)
     +		options.strategy = xstrdup("ort");
    -+	else
    -+		options.strategy = xstrdup_or_null(options.strategy);
     +	if (options.strategy)
      		imply_merge(&options, "--strategy");
     -	}
      
      	if (options.root && !options.onto_name)
      		imply_merge(&options, "--root without --onto");
    -@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - 	}
    - 
    - 	if (options.type == REBASE_UNSPECIFIED) {
    --		if (!strcmp(options.default_backend, "merge"))
    -+		if (!options.default_backend)
    -+			options.type = REBASE_MERGE;
    -+		else if (!strcmp(options.default_backend, "merge"))
    - 			options.type = REBASE_MERGE;
    - 		else if (!strcmp(options.default_backend, "apply"))
    - 			options.type = REBASE_APPLY;
    -@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - cleanup:
    - 	strbuf_release(&buf);
    - 	strbuf_release(&revisions);
    -+	free(options.default_backend);
    - 	free(options.reflog_action);
    - 	free(options.head_name);
    - 	strvec_clear(&options.git_am_opts);
18:  129482dbaa = 18:  bf854b3979 builtin/merge: always store allocated strings in `pull_twohead`
19:  37e7aaed97 = 19:  9b9d57ae84 config.mak.dev: enable `-Wwrite-strings` warning
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v2 01/19] global: improve const correctness when assigning string constants
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
@ 2024-05-30 12:50   ` Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 02/19] global: assign non-const strings as required Patrick Steinhardt
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:50 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 23139 bytes --]

We're about to enable `-Wwrite-strings`, which changes the type of
string constants to `const char[]`. Fix various sites where we assign
such constants to non-const variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/bisect.c             |  3 ++-
 builtin/blame.c              |  2 +-
 builtin/bugreport.c          |  2 +-
 builtin/check-ignore.c       |  4 +--
 builtin/clone.c              |  6 ++---
 builtin/commit.c             |  6 ++---
 builtin/diagnose.c           |  2 +-
 builtin/log.c                |  2 +-
 builtin/mailsplit.c          |  4 +--
 builtin/pull.c               | 52 ++++++++++++++++++------------------
 builtin/receive-pack.c       |  4 +--
 builtin/revert.c             |  2 +-
 compat/regex/regcomp.c       |  2 +-
 diff.c                       |  4 +--
 diffcore-rename.c            |  6 ++---
 fmt-merge-msg.c              |  2 +-
 fsck.c                       |  2 +-
 fsck.h                       |  2 +-
 gpg-interface.c              |  2 +-
 http-backend.c               |  2 +-
 imap-send.c                  |  6 ++---
 pretty.c                     |  2 +-
 refs.c                       |  2 +-
 refs.h                       |  2 +-
 reftable/record.c            |  6 ++---
 run-command.c                |  2 +-
 t/helper/test-hashmap.c      |  3 ++-
 t/helper/test-json-writer.c  | 10 +++----
 t/helper/test-regex.c        |  4 +--
 t/helper/test-rot13-filter.c |  5 ++--
 t/unit-tests/t-strbuf.c      | 10 ++++---
 trailer.c                    |  2 +-
 wt-status.c                  |  2 +-
 33 files changed, 86 insertions(+), 81 deletions(-)

diff --git a/builtin/bisect.c b/builtin/bisect.c
index a58432b9d9..dabce9b542 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
 	return bisect_clean_state();
 }
 
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+		       const char *fmt, const char *state,
 		       struct commit *commit)
 {
 	struct pretty_print_context pp = {0};
diff --git a/builtin/blame.c b/builtin/blame.c
index 838cd476be..98c7629b6a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
-	char *tmp, *endp;
+	const char *tmp, *endp;
 	const char *namebuf, *mailbuf;
 
 	tmp = strstr(inbuf, what);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 25f860a0d9..b3cc77af53 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode diagnose = DIAGNOSE_NONE;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	const char *user_relative_path = NULL;
 	char *prefixed_filename;
 	size_t output_path_len;
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 6c43430ec4..2bda6a1d46 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -35,8 +35,8 @@ static const struct option check_ignore_options[] = {
 
 static void output_pattern(const char *path, struct path_pattern *pattern)
 {
-	char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
-	char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+	const char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
+	const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
 	if (!nul_term_line) {
 		if (!verbose) {
 			write_name_quoted(path, stdout, '\n');
diff --git a/builtin/clone.c b/builtin/clone.c
index 23993b905b..92ab7d7165 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -71,7 +71,7 @@ static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
 static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
 static int option_sparse_checkout;
@@ -177,8 +177,8 @@ static struct option builtin_clone_options[] = {
 
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
-	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
-	static char *bundle_suffix[] = { ".bundle", "" };
+	static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static const char *bundle_suffix[] = { ".bundle", "" };
 	size_t baselen = path->len;
 	struct stat st;
 	int i;
diff --git a/builtin/commit.c b/builtin/commit.c
index f53e7e86ff..75c741173e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,7 +113,7 @@ static char *template_file;
  * the commit message and/or authorship.
  */
 static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
 static char *fixup_message, *fixup_commit, *squash_message;
 static const char *fixup_prefix;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +121,8 @@ static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
 static struct strvec trailer_args = STRVEC_INIT;
 
 /*
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2b55..4857a4395b 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode mode = DIAGNOSE_STATS;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	char *prefixed_filename;
 
 	const struct option diagnose_options[] = {
diff --git a/builtin/log.c b/builtin/log.c
index 78a247d8a9..b8846a9458 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1283,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 	o2->flags = flags2;
 }
 
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb8ae..fe6dbc5d05 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
 	DIR *dir;
 	struct dirent *dent;
 	char *name = NULL;
-	char *subs[] = { "cur", "new", NULL };
-	char **sub;
+	const char *subs[] = { "cur", "new", NULL };
+	const char **sub;
 	int ret = -1;
 
 	for (sub = subs; *sub; ++sub) {
diff --git a/builtin/pull.c b/builtin/pull.c
index d622202bce..2d0429f14f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
 
 /* Shared options */
 static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
 
 /* Options passed to git-merge or git-rebase */
 static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
 static int opt_autostash = -1;
 static int config_autostash;
 static int check_trust_level = 1;
 static struct strvec opt_strategies = STRVEC_INIT;
 static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
 static int opt_allow_unrelated_histories;
 
 /* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
 static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
 static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
 static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
 static struct strvec opt_fetch = STRVEC_INIT;
 
 static struct option pull_options[] = {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 01c1f04ece..c8d12ee0a7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1249,7 +1249,7 @@ static int run_proc_receive_hook(struct command *commands,
 	return code;
 }
 
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
 	N_("By default, updating the current branch in a non-bare repository\n"
 	   "is denied, because it will make the index and work tree inconsistent\n"
 	   "with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
 	rp_error("%s", _(refuse_unconfigured_deny_msg));
 }
 
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
 	N_("By default, deleting the current branch is denied, because the next\n"
 	   "'git clone' won't result in any file checked out, causing confusion.\n"
 	   "\n"
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2c68..7bf2b4e11d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 
 	/* Check for incompatible command line arguments */
 	if (cmd) {
-		char *this_operation;
+		const char *this_operation;
 		if (cmd == 'q')
 			this_operation = "--quit";
 		else if (cmd == 'c')
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 2bc0f1187a..6c5d455e92 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
   unsigned int table_size;
 #ifndef _LIBC
-  char *codeset_name;
+  const char *codeset_name;
 #endif
 
   memset (dfa, '\0', sizeof (re_dfa_t));
diff --git a/diff.c b/diff.c
index e70301df76..ffd867ef6c 100644
--- a/diff.c
+++ b/diff.c
@@ -3764,7 +3764,7 @@ static void builtin_diff(const char *name_a,
 	return;
 }
 
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 {
 	if (!is_renamed) {
 		if (p->status == DIFF_STATUS_ADDED) {
@@ -4076,7 +4076,7 @@ static int reuse_worktree_file(struct index_state *istate,
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
 	struct strbuf buf = STRBUF_INIT;
-	char *dirty = "";
+	const char *dirty = "";
 
 	/* Are we looking at the work tree? */
 	if (s->dirty_submodule)
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bcac7..0e1adb87df 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -406,7 +406,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
 	return highest_destination_dir;
 }
 
-static char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
 
 static int dir_rename_already_determinable(struct strintmap *counts)
 {
@@ -429,8 +429,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
 }
 
 static void increment_count(struct dir_rename_info *info,
-			    char *old_dir,
-			    char *new_dir)
+			    const char *old_dir,
+			    const char *new_dir)
 {
 	struct strintmap *counts;
 	struct strmap_entry *e;
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 7d144b803a..5af63ab5ab 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -447,7 +447,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
 				const char *current_branch)
 {
 	int i = 0;
-	char *sep = "";
+	const char *sep = "";
 
 	strbuf_addstr(out, "Merge ");
 	for (i = 0; i < srcs.nr; i++) {
diff --git a/fsck.c b/fsck.c
index 7dff41413e..61cd48aa25 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1231,7 +1231,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 }
 
 int fsck_buffer(const struct object_id *oid, enum object_type type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options)
 {
 	if (type == OBJ_BLOB)
diff --git a/fsck.h b/fsck.h
index 17fa2dda5d..4f0c4e6479 100644
--- a/fsck.h
+++ b/fsck.h
@@ -202,7 +202,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
  * struct.
  */
 int fsck_buffer(const struct object_id *oid, enum object_type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options);
 
 /*
diff --git a/gpg-interface.c b/gpg-interface.c
index 5193223714..71a9382a61 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -727,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
 			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
-	char *fmtname = NULL;
+	const char *fmtname = NULL;
 	char *trust;
 	int ret;
 
diff --git a/http-backend.c b/http-backend.c
index 5b65287ac9..5b4dca65ed 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -753,7 +753,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
 
 int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
-	char *method = getenv("REQUEST_METHOD");
+	const char *method = getenv("REQUEST_METHOD");
 	const char *proto_header;
 	char *dir;
 	struct service_cmd *cmd = NULL;
diff --git a/imap-send.c b/imap-send.c
index a5d1510180..8b723b34a5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1215,9 +1215,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
 static void wrap_in_html(struct strbuf *msg)
 {
 	struct strbuf buf = STRBUF_INIT;
-	static char *content_type = "Content-Type: text/html;\n";
-	static char *pre_open = "<pre>\n";
-	static char *pre_close = "</pre>\n";
+	static const char *content_type = "Content-Type: text/html;\n";
+	static const char *pre_open = "<pre>\n";
+	static const char *pre_close = "</pre>\n";
 	const char *body = strstr(msg->buf, "\n\n");
 
 	if (!body)
diff --git a/pretty.c b/pretty.c
index 22a81506b7..ec05db5655 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1325,7 +1325,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
 static size_t parse_describe_args(const char *start, struct strvec *args)
 {
 	struct {
-		char *name;
+		const char *name;
 		enum {
 			DESCRIBE_ARG_BOOL,
 			DESCRIBE_ARG_INTEGER,
diff --git a/refs.c b/refs.c
index 8260c27cde..292e8d947e 100644
--- a/refs.c
+++ b/refs.c
@@ -159,7 +159,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
 {
 	struct ref_namespace_info *info = &ref_namespace[namespace];
 	if (info->ref_updated)
-		free(info->ref);
+		free((char *)info->ref);
 	info->ref = ref;
 	info->ref_updated = 1;
 }
diff --git a/refs.h b/refs.h
index 34568ee1fb..923f751d18 100644
--- a/refs.h
+++ b/refs.h
@@ -975,7 +975,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
  */
 
 struct ref_namespace_info {
-	char *ref;
+	const char *ref;
 	enum decoration_type decoration;
 
 	/*
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e913..a2cba5ef74 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
 	return start_len - in.len;
 }
 
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
 {
 	struct string_view start = s;
 	int l = strlen(str);
@@ -969,9 +969,9 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
 	return REFTABLE_FORMAT_ERROR;
 }
 
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
 {
-	char *empty = "";
+	const char *empty = "";
 	if (!a)
 		a = empty;
 
diff --git a/run-command.c b/run-command.c
index 1b821042b4..7600531fb6 100644
--- a/run-command.c
+++ b/run-command.c
@@ -663,7 +663,7 @@ int start_command(struct child_process *cmd)
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
 	int failed_errno;
-	char *str;
+	const char *str;
 
 	/*
 	 * In case of errors we must keep the promise to close FDs
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d49c..2912899558 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
 }
 
 static struct test_entry *alloc_test_entry(unsigned int hash,
-					   char *key, char *value)
+					   const char *key,
+					   const char *value)
 {
 	size_t klen = strlen(key);
 	size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f597..ed52eb76bf 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
 	jw_end(&arr4);
 }
 
-static char *expect_nest1 =
+static const char *expect_nest1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
 static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
 	jw_release(&arr1);
 }
 
-static char *expect_inline1 =
+static const char *expect_inline1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
 	("{\n"
 	 "  \"obj1\": {\n"
 	 "    \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
 	jw_end(&inline1);
 }
 
-static char *expect_inline2 =
+static const char *expect_inline2 =
 	"[[1,2],[3,4],{\"a\":\"abc\"}]";
 
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
 	("[\n"
 	 "  [\n"
 	 "    1,\n"
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042eafc2..366bd70976 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
 
 static int test_regex_bug(void)
 {
-	char *pat = "[^={} \t]+";
-	char *str = "={}\nfred";
+	const char *pat = "[^={} \t]+";
+	const char *str = "={}\nfred";
 	regex_t r;
 	regmatch_t m[1];
 
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c622..7e1d9e0ee4 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
 	strmap_clear(&delay, 0);
 }
 
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
 {
 	struct delay_entry *entry = xcalloc(1, sizeof(*entry));
 	entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
 static void command_loop(void)
 {
 	for (;;) {
-		char *buf, *output;
+		char *buf;
+		const char *output;
 		char *pathname;
 		struct delay_entry *entry;
 		struct strbuf input = STRBUF_INIT;
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4441..6027dafef7 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
 #include "strbuf.h"
 
 /* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+		  const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
 }
 
 /* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+			    const char *init_str, const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
 	strbuf_release(&buf);
 }
 
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
 {
 	const char *p_ch = data;
 	const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
 	check_char(buf->buf[buf->len], ==, '\0');
 }
 
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
 {
 	const char *text = data;
 	size_t len = strlen(text);
diff --git a/trailer.c b/trailer.c
index 2bcb9ba8f7..72e5136c73 100644
--- a/trailer.c
+++ b/trailer.c
@@ -63,7 +63,7 @@ struct arg_item {
 
 static LIST_HEAD(conf_head);
 
-static char *separators = ":";
+static const char *separators = ":";
 
 static int configured;
 
diff --git a/wt-status.c b/wt-status.c
index ff4be071ca..7912545e4e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2408,7 +2408,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
 		int mode;
 		struct object_id oid;
 	} stages[3];
-	char *key;
+	const char *key;
 	char submodule_token[5];
 	char unmerged_prefix = 'u';
 	char eol_char = s->null_termination ? '\0' : '\n';
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 02/19] global: assign non-const strings as required
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
@ 2024-05-30 12:50   ` Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 03/19] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
                     ` (17 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:50 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 24439 bytes --]

There are several cases where we initialize non-const fields with string
constants. This is invalid and will cause warnings once we enable the
`-Wwrite-strings` warning. Adapt those cases to instead use string
arrays.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/remote.c          |  3 +-
 diff.c                    |  3 +-
 entry.c                   |  7 ++--
 ident.c                   |  9 +++-
 line-log.c                |  3 +-
 object-file.c             |  3 +-
 pretty.c                  |  5 ++-
 refs/reftable-backend.c   |  5 ++-
 reftable/basics_test.c    |  5 ++-
 reftable/block_test.c     |  4 +-
 reftable/merged_test.c    | 52 ++++++++++++-----------
 reftable/readwrite_test.c | 60 ++++++++++++++++-----------
 reftable/stack_test.c     | 87 +++++++++++++++++++++------------------
 13 files changed, 144 insertions(+), 102 deletions(-)

diff --git a/builtin/remote.c b/builtin/remote.c
index d52b1c0e10..0324a5d48d 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -494,11 +494,12 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 	struct ref *ref, *matches;
 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
 	struct refspec_item refspec;
+	char refspec_str[] = "refs/heads/*";
 
 	memset(&refspec, 0, sizeof(refspec));
 	refspec.force = 0;
 	refspec.pattern = 1;
-	refspec.src = refspec.dst = "refs/heads/*";
+	refspec.src = refspec.dst = refspec_str;
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 				    fetch_map, 1);
diff --git a/diff.c b/diff.c
index ffd867ef6c..1439a5a01d 100644
--- a/diff.c
+++ b/diff.c
@@ -7231,11 +7231,12 @@ size_t fill_textconv(struct repository *r,
 		     struct diff_filespec *df,
 		     char **outbuf)
 {
+	static char empty_str[] = "";
 	size_t size;
 
 	if (!driver) {
 		if (!DIFF_FILE_VALID(df)) {
-			*outbuf = "";
+			*outbuf = empty_str;
 			return 0;
 		}
 		if (diff_populate_filespec(r, df, NULL))
diff --git a/entry.c b/entry.c
index b8c257f6f9..2fc06ac90c 100644
--- a/entry.c
+++ b/entry.c
@@ -175,6 +175,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 	struct string_list_item *filter, *path;
 	struct progress *progress = NULL;
 	struct delayed_checkout *dco = state->delayed_checkout;
+	char empty_str[] = "";
 
 	if (!state->delayed_checkout)
 		return errs;
@@ -189,7 +190,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 			if (!async_query_available_blobs(filter->string, &available_paths)) {
 				/* Filter reported an error */
 				errs = 1;
-				filter->string = "";
+				filter->string = empty_str;
 				continue;
 			}
 			if (available_paths.nr <= 0) {
@@ -199,7 +200,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 				 * filter from the list (see
 				 * "string_list_remove_empty_items" call below).
 				 */
-				filter->string = "";
+				filter->string = empty_str;
 				continue;
 			}
 
@@ -225,7 +226,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					 * Do not ask the filter for available blobs,
 					 * again, as the filter is likely buggy.
 					 */
-					filter->string = "";
+					filter->string = empty_str;
 					continue;
 				}
 				ce = index_file_exists(state->istate, path->string,
diff --git a/ident.c b/ident.c
index cc7afdbf81..df7aa42802 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,14 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
 	pw = getpwuid(getuid());
 	if (!pw) {
 		static struct passwd fallback;
-		fallback.pw_name = "unknown";
+		static char fallback_name[] = "unknown";
 #ifndef NO_GECOS_IN_PWENT
-		fallback.pw_gecos = "Unknown";
+		static char fallback_gcos[] = "Unknown";
+#endif
+
+		fallback.pw_name = fallback_name;
+#ifndef NO_GECOS_IN_PWENT
+		fallback.pw_gecos = fallback_gcos;
 #endif
 		pw = &fallback;
 		if (is_bogus)
diff --git a/line-log.c b/line-log.c
index 8ff6ccb772..d9bf2c8120 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1032,6 +1032,7 @@ static int process_diff_filepair(struct rev_info *rev,
 	struct range_set tmp;
 	struct diff_ranges diff;
 	mmfile_t file_parent, file_target;
+	char empty_str[] = "";
 
 	assert(pair->two->path);
 	while (rg) {
@@ -1056,7 +1057,7 @@ static int process_diff_filepair(struct rev_info *rev,
 		file_parent.ptr = pair->one->data;
 		file_parent.size = pair->one->size;
 	} else {
-		file_parent.ptr = "";
+		file_parent.ptr = empty_str;
 		file_parent.size = 0;
 	}
 
diff --git a/object-file.c b/object-file.c
index 610b1f465c..c9e374e57e 100644
--- a/object-file.c
+++ b/object-file.c
@@ -282,12 +282,13 @@ static struct cached_object {
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
 
+static char empty_tree_buf[] = "";
 static struct cached_object empty_tree = {
 	.oid = {
 		.hash = EMPTY_TREE_SHA1_BIN_LITERAL,
 	},
 	.type = OBJ_TREE,
-	.buf = "",
+	.buf = empty_tree_buf,
 };
 
 static struct cached_object *find_cached_object(const struct object_id *oid)
diff --git a/pretty.c b/pretty.c
index ec05db5655..1a0030b32a 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1583,9 +1583,10 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 		return 1;
 	case 'D':
 		{
+			char empty_str[] = "";
 			const struct decoration_options opts = {
-				.prefix = "",
-				.suffix = ""
+				.prefix = empty_str,
+				.suffix = empty_str,
 			};
 
 			format_decorations(sb, commit, c->auto_color, &opts);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1af86bbdec..1908e74dea 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1285,6 +1285,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	struct strbuf errbuf = STRBUF_INIT;
 	size_t logs_nr = 0, logs_alloc = 0, i;
 	const char *committer_info;
+	char head[] = "HEAD";
 	int ret;
 
 	committer_info = git_committer_info(0);
@@ -1387,7 +1388,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (append_head_reflog) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			logs[logs_nr] = logs[logs_nr - 1];
-			logs[logs_nr].refname = "HEAD";
+			logs[logs_nr].refname = head;
 			logs_nr++;
 		}
 	}
@@ -1463,7 +1464,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	string_list_clear(&skip, 0);
 	strbuf_release(&errbuf);
 	for (i = 0; i < logs_nr; i++) {
-		if (!strcmp(logs[i].refname, "HEAD"))
+		if (logs[i].refname == head)
 			continue;
 		logs[i].refname = NULL;
 		reftable_log_record_release(&logs[i]);
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 997c4d9e01..23fab22eb1 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -58,8 +58,9 @@ static void test_binsearch(void)
 
 static void test_names_length(void)
 {
-	char *a[] = { "a", "b", NULL };
-	EXPECT(names_length(a) == 2);
+	char a[] = "a", b[] = "b";
+	char *names[] = { a, b, NULL };
+	EXPECT(names_length(names) == 2);
 }
 
 static void test_parse_names_normal(void)
diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfbc83..d5967b214d 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -19,7 +19,7 @@ license that can be found in the LICENSE file or at
 static void test_block_read_write(void)
 {
 	const int header_off = 21; /* random */
-	char *names[30];
+	char *names[30], empty_str[] = "";
 	const int N = ARRAY_SIZE(names);
 	const int block_size = 1024;
 	struct reftable_block block = { NULL };
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
 			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
-	rec.u.ref.refname = "";
+	rec.u.ref.refname = empty_str;
 	rec.u.ref.value_type = REFTABLE_REF_DELETION;
 	n = block_writer_add(&bw, &rec);
 	EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82d1c..fd5a065e42 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -123,14 +123,15 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 
 static void test_merged_between(void)
 {
+	char a[] = "a", b[] = "b";
 	struct reftable_ref_record r1[] = { {
-		.refname = "b",
+		.refname = b,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = a,
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
@@ -163,40 +164,41 @@ static void test_merged_between(void)
 
 static void test_merged(void)
 {
+	char a[] = "a", b[] = "b", c[] = "c", d[] = "d";
 	struct reftable_ref_record r1[] = {
 		{
-			.refname = "a",
+			.refname = a,
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "b",
+			.refname = b,
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "c",
+			.refname = c,
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = a,
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
 	struct reftable_ref_record r3[] = {
 		{
-			.refname = "c",
+			.refname = c,
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 2 },
 		},
 		{
-			.refname = "d",
+			.refname = d,
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
@@ -289,48 +291,52 @@ merged_table_from_log_records(struct reftable_log_record **logs,
 
 static void test_merged_logs(void)
 {
+	char a[] = "a";
+	char name[] = "jane doe", email[] = "jane@invalid";
+	char message1[] = "message1", message2[] = "message2";
+	char message3[] = "message3";
 	struct reftable_log_record r1[] = {
 		{
-			.refname = "a",
+			.refname = a,
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 2 },
 				/* deletion */
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message2",
+				.name = name,
+				.email = email,
+				.message = message2,
 			}
 		},
 		{
-			.refname = "a",
+			.refname = a,
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message1",
+				.name = name,
+				.email = email,
+				.message = message1,
 			}
 		},
 	};
 	struct reftable_log_record r2[] = {
 		{
-			.refname = "a",
+			.refname = a,
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.new_hash = { 3 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message3",
+				.name = name,
+				.email = email,
+				.message = message3,
 			}
 		},
 	};
 	struct reftable_log_record r3[] = {
 		{
-			.refname = "a",
+			.refname = a,
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_DELETION,
 		},
@@ -400,13 +406,13 @@ static void test_merged_logs(void)
 
 static void test_default_write_opts(void)
 {
+	char master[] = "master";
 	struct reftable_write_options opts = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-
 	struct reftable_ref_record rec = {
-		.refname = "master",
+		.refname = master,
 		.update_index = 1,
 	};
 	int err;
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd214c5..064d693111 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -56,6 +56,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 	int i = 0, n;
 	struct reftable_log_record log = { NULL };
 	const struct reftable_stats *stats = NULL;
+	char message[] = "message";
 
 	REFTABLE_CALLOC_ARRAY(*names, N + 1);
 
@@ -86,7 +87,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
 		set_test_hash(log.value.update.new_hash, i);
-		log.value.update.message = "message";
+		log.value.update.message = message;
 
 		n = reftable_writer_add_log(w, &log);
 		EXPECT(n == 0);
@@ -111,23 +112,28 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 
 static void test_log_buffer_size(void)
 {
+	char refname[] = "refs/heads/master";
+	char name[] = "Han-Wen Hienhuys";
+	char email[] = "hanwen@google.com";
+	char message[] = "commit: 9\n";
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_write_options opts = {
 		.block_size = 4096,
 	};
 	int err;
 	int i;
-	struct reftable_log_record
-		log = { .refname = "refs/heads/master",
-			.update_index = 0xa,
-			.value_type = REFTABLE_LOG_UPDATE,
-			.value = { .update = {
-					   .name = "Han-Wen Nienhuys",
-					   .email = "hanwen@google.com",
-					   .tz_offset = 100,
-					   .time = 0x5e430672,
-					   .message = "commit: 9\n",
-				   } } };
+	struct reftable_log_record log = {
+		.refname = refname,
+		.update_index = 0xa,
+		.value_type = REFTABLE_LOG_UPDATE,
+		.value.update = {
+			.name = name,
+			.email = email,
+			.tz_offset = 100,
+			.time = 0x5e430672,
+			.message = message,
+		},
+	};
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
@@ -149,6 +155,9 @@ static void test_log_buffer_size(void)
 
 static void test_log_overflow(void)
 {
+	char refname[] = "refs/heads/master";
+	char name[] = "Han-Wen Hienhuys";
+	char email[] = "hanwen@google.com";
 	struct strbuf buf = STRBUF_INIT;
 	char msg[256] = { 0 };
 	struct reftable_write_options opts = {
@@ -156,15 +165,15 @@ static void test_log_overflow(void)
 	};
 	int err;
 	struct reftable_log_record log = {
-		.refname = "refs/heads/master",
+		.refname = refname,
 		.update_index = 0xa,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "Han-Wen Nienhuys",
-				.email = "hanwen@google.com",
+				.name = name,
+				.email = email,
 				.tz_offset = 100,
 				.time = 0x5e430672,
 				.message = msg,
@@ -290,17 +299,20 @@ static void test_log_zlib_corruption(void)
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	const struct reftable_stats *stats = NULL;
+	char refname[] = "refname";
+	char name[] = "My Name";
+	char email[] = "myname@invalid";
 	char message[100] = { 0 };
 	int err, i, n;
 	struct reftable_log_record log = {
-		.refname = "refname",
+		.refname = refname,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.new_hash = { 1 },
 				.old_hash = { 2 },
-				.name = "My Name",
-				.email = "myname@invalid",
+				.name = name,
+				.email = email,
 				.message = message,
 			},
 		},
@@ -727,8 +739,9 @@ static void test_write_empty_key(void)
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
+	char refname[] = "";
 	struct reftable_ref_record ref = {
-		.refname = "",
+		.refname = refname,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_DELETION,
 	};
@@ -750,20 +763,21 @@ static void test_write_key_order(void)
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
+	char a[] = "a", b[] = "b", target[] = "target";
 	struct reftable_ref_record refs[2] = {
 		{
-			.refname = "b",
+			.refname = b,
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = target,
 			},
 		}, {
-			.refname = "a",
+			.refname = a,
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = target,
 			},
 		}
 	};
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 7889f818d1..c6d88e6ea8 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
 	char out[1024] = "line1\n\nline2\nline3";
 	int n, err;
 	char **names = NULL;
-	char *want[] = { "line1", "line2", "line3" };
+	const char *want[] = { "line1", "line2", "line3" };
 	int i = 0;
 
 	EXPECT(fd > 0);
@@ -116,13 +116,14 @@ static void test_parse_names(void)
 
 static void test_names_equal(void)
 {
-	char *a[] = { "a", "b", "c", NULL };
-	char *b[] = { "a", "b", "d", NULL };
-	char *c[] = { "a", "b", NULL };
-
-	EXPECT(names_equal(a, a));
-	EXPECT(!names_equal(a, b));
-	EXPECT(!names_equal(a, c));
+	char a[] = "a", b[] = "b", c[] = "c", d[] = "d";
+	char *abc[] = { a, b, c, NULL };
+	char *abd[] = { a, b, d, NULL };
+	char *ab[] = { a, b, NULL };
+
+	EXPECT(names_equal(abc, abc));
+	EXPECT(!names_equal(abc, abd));
+	EXPECT(!names_equal(abc, ab));
 }
 
 static int write_test_ref(struct reftable_writer *wr, void *arg)
@@ -155,11 +156,12 @@ static void test_reftable_stack_add_one(void)
 	};
 	struct reftable_stack *st = NULL;
 	int err;
+	char head[] = "HEAD", master[] = "master";
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = head,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = master,
 	};
 	struct reftable_ref_record dest = { NULL };
 	struct stat stat_result = { 0 };
@@ -215,17 +217,18 @@ static void test_reftable_stack_uptodate(void)
 	char *dir = get_tmp_dir(__LINE__);
 
 	int err;
+	char head[] = "HEAD", branch2[] = "branch2", master[] = "master";
 	struct reftable_ref_record ref1 = {
-		.refname = "HEAD",
+		.refname = head,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = master,
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "branch2",
+		.refname = branch2,
 		.update_index = 2,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = master,
 	};
 
 
@@ -262,12 +265,12 @@ static void test_reftable_stack_transaction_api(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_addition *add = NULL;
-
+	char head[] = "HEAD", master[] = "master";
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = head,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = master,
 	};
 	struct reftable_ref_record dest = { NULL };
 
@@ -310,10 +313,11 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i <= n; i++) {
+		char master[] = "master";
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = master,
 		};
 		char name[100];
 
@@ -355,8 +359,9 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 
 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
 {
+	char master[] = "refs/meads/master";
 	struct reftable_ref_record ref = {
-		.refname = "refs/heads/master",
+		.refname = master,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = {0x01},
@@ -404,21 +409,21 @@ static int write_error(struct reftable_writer *wr, void *arg)
 static void test_reftable_stack_update_index_check(void)
 {
 	char *dir = get_tmp_dir(__LINE__);
-
 	struct reftable_write_options cfg = { 0 };
 	struct reftable_stack *st = NULL;
 	int err;
+	char name1[] = "name1", name2[] = "name2", master[] = "master";
 	struct reftable_ref_record ref1 = {
-		.refname = "name1",
+		.refname = name1,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = master,
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "name2",
+		.refname = name2,
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = master,
 	};
 
 	err = reftable_new_stack(&st, dir, cfg);
@@ -560,8 +565,12 @@ static void test_reftable_stack_log_normalize(void)
 	};
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
+	char branch[] = "branch";
+	char onetwomessage[] = "one\ntwo";
+	char onemessage[] = "one";
+	char twomessage[] = "two\n";
 	struct reftable_log_record input = {
-		.refname = "branch",
+		.refname = branch,
 		.update_index = 1,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
@@ -582,11 +591,11 @@ static void test_reftable_stack_log_normalize(void)
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 
-	input.value.update.message = "one\ntwo";
+	input.value.update.message = onetwomessage;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT(err == REFTABLE_API_ERROR);
 
-	input.value.update.message = "one";
+	input.value.update.message = onemessage;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
 
@@ -594,7 +603,7 @@ static void test_reftable_stack_log_normalize(void)
 	EXPECT_ERR(err);
 	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
 
-	input.value.update.message = "two\n";
+	input.value.update.message = twomessage;
 	arg.update_index = 2;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
@@ -691,15 +700,14 @@ static void test_reftable_stack_tombstone(void)
 static void test_reftable_stack_hash_id(void)
 {
 	char *dir = get_tmp_dir(__LINE__);
-
 	struct reftable_write_options cfg = { 0 };
 	struct reftable_stack *st = NULL;
 	int err;
-
+	char master[] = "master", target[] = "target";
 	struct reftable_ref_record ref = {
-		.refname = "master",
+		.refname = master,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "target",
+		.value.symref = target,
 		.update_index = 1,
 	};
 	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
@@ -874,12 +882,12 @@ static void test_reftable_stack_auto_compaction(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i < N; i++) {
-		char name[100];
+		char name[100], master[] = "master";
 		struct reftable_ref_record ref = {
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = master,
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -910,10 +918,11 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i <= n; i++) {
+		char master[] = "master";
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = master,
 		};
 
 		/*
@@ -959,12 +968,12 @@ static void test_reftable_stack_compaction_concurrent(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i < N; i++) {
-		char name[100];
+		char name[100], master[] = "master";
 		struct reftable_ref_record ref = {
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = master,
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -1009,12 +1018,12 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i < N; i++) {
-		char name[100];
+		char name[100], master[] = "master";
 		struct reftable_ref_record ref = {
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = master,
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 03/19] global: convert intentionally-leaking config strings to consts
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 02/19] global: assign non-const strings as required Patrick Steinhardt
@ 2024-05-30 12:50   ` Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 04/19] compat/win32: fix const-correctness with string constants Patrick Steinhardt
                     ` (16 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:50 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 5072 bytes --]

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. Furthermore,
all of these are being assigned both string constants in some places,
and allocated strings in other places. This will cause warnings once we
enable `-Wwrite-strings`, so 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(-)

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;
 };
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 04/19] compat/win32: fix const-correctness with string constants
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (2 preceding siblings ...)
  2024-05-30 12:50   ` [PATCH v2 03/19] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
@ 2024-05-30 12:50   ` Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 05/19] refspec: remove global tag refspec structure Patrick Steinhardt
                     ` (15 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:50 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 4104 bytes --]

Adjust various places in our Win32 compatibility layer where we are not
assigning string constants to `const char *` variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 compat/basename.c | 15 +++++++++++++--
 compat/mingw.c    | 25 +++++++++++++------------
 compat/winansi.c  |  2 +-
 3 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/compat/basename.c b/compat/basename.c
index 96bd9533b4..c3c9d65fac 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -1,6 +1,13 @@
 #include "../git-compat-util.h"
 #include "../strbuf.h"
 
+/*
+ * Both basename(3P) and dirname(3P) are mis-specified because they return a
+ * non-constant pointer even though it is specified that they may return a
+ * pointer to internal memory. This variable here is a result of that.
+ */
+static char current_directory[] = ".";
+
 /* Adapted from libiberty's basename.c.  */
 char *gitbasename (char *path)
 {
@@ -10,7 +17,7 @@ char *gitbasename (char *path)
 		skip_dos_drive_prefix(&path);
 
 	if (!path || !*path)
-		return ".";
+		return current_directory;
 
 	for (base = path; *path; path++) {
 		if (!is_dir_sep(*path))
@@ -33,8 +40,12 @@ char *gitdirname(char *path)
 	char *p = path, *slash = NULL, c;
 	int dos_drive_prefix;
 
+	/*
+	 * Same here, dirname(3P) is broken because it returns a non-constant
+	 * pointer that may point to internal memory.
+	 */
 	if (!p)
-		return ".";
+		return current_directory;
 
 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
 		goto dot;
diff --git a/compat/mingw.c b/compat/mingw.c
index 6b06ea540f..60f0986f76 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2257,6 +2257,7 @@ struct passwd *getpwuid(int uid)
 {
 	static unsigned initialized;
 	static char user_name[100];
+	static char unknown[] = "unknown";
 	static struct passwd *p;
 	wchar_t buf[100];
 	DWORD len;
@@ -2279,7 +2280,7 @@ struct passwd *getpwuid(int uid)
 	p->pw_name = user_name;
 	p->pw_gecos = get_extended_user_info(NameDisplay);
 	if (!p->pw_gecos)
-		p->pw_gecos = "unknown";
+		p->pw_gecos = unknown;
 	p->pw_dir = NULL;
 
 	initialized = 1;
@@ -2800,16 +2801,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 			strbuf_addf(report, "'%s' is on a file system that does "
 				    "not record ownership\n", path);
 		} else if (report) {
-			LPSTR str1, str2, str3, str4, to_free1 = NULL,
-			    to_free3 = NULL, to_local_free2 = NULL,
-			    to_local_free4 = NULL;
+			PCSTR str1, str2, str3, str4;
+			LPSTR to_free1 = NULL, to_free3 = NULL,
+			    to_local_free2 = NULL, to_local_free4 = NULL;
 
-			if (user_sid_to_user_name(sid, &str1))
-				to_free1 = str1;
+			if (user_sid_to_user_name(sid, &to_free1))
+				str1 = to_free1;
 			else
 				str1 = "(inconvertible)";
-			if (ConvertSidToStringSidA(sid, &str2))
-				to_local_free2 = str2;
+			if (ConvertSidToStringSidA(sid, &to_local_free2))
+				str2 = to_local_free2;
 			else
 				str2 = "(inconvertible)";
 
@@ -2822,13 +2823,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 				str4 = "(invalid)";
 			} else {
 				if (user_sid_to_user_name(current_user_sid,
-							  &str3))
-					to_free3 = str3;
+							  &to_free3))
+					str3 = to_free3;
 				else
 					str3 = "(inconvertible)";
 				if (ConvertSidToStringSidA(current_user_sid,
-							   &str4))
-					to_local_free4 = str4;
+							   &to_local_free4))
+					str4 = to_local_free4;
 				else
 					str4 = "(inconvertible)";
 			}
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f684..575813bde8 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
 	/* convert utf-8 to utf-16 */
 	int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
 	if (wlen < 0) {
-		wchar_t *err = L"[invalid]";
+		const wchar_t *err = L"[invalid]";
 		WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
 		return;
 	}
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 05/19] refspec: remove global tag refspec structure
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (3 preceding siblings ...)
  2024-05-30 12:50   ` [PATCH v2 04/19] compat/win32: fix const-correctness with string constants Patrick Steinhardt
@ 2024-05-30 12:50   ` Patrick Steinhardt
  2024-05-30 12:50   ` [PATCH v2 06/19] http: do not assign string constant to non-const field Patrick Steinhardt
                     ` (14 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:50 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 3608 bytes --]

We have a global tag refspec structure that is used by both git-clone(1)
and git-fetch(1). Initialization fo the structure will break once we
enable `-Wwrite-strings`, even though the breakage is harmless. While we
could just add casts, the structure isn't really required in the first
place as we can simply initialize the structures at the respective
callsites.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c |  8 ++++++--
 builtin/fetch.c | 11 ++++++++---
 refspec.c       | 13 -------------
 refspec.h       |  1 -
 4 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 92ab7d7165..bde1d284a2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
 	struct ref *local_refs = head;
 	struct ref **tail = head ? &head->next : &local_refs;
+	struct refspec_item tag_refspec;
+
+	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
 
 	if (option_single_branch) {
 		struct ref *remote_head = NULL;
@@ -545,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 					      &tail, 0);
 
 			/* if --branch=tag, pull the requested tag explicitly */
-			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+			get_fetch_map(remote_head, &tag_refspec, &tail, 0);
 		}
 		free_refs(remote_head);
 	} else {
@@ -555,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	}
 
 	if (!option_mirror && !option_single_branch && !option_no_tags)
-		get_fetch_map(refs, tag_refspec, &tail, 0);
+		get_fetch_map(refs, &tag_refspec, &tail, 0);
 
+	refspec_item_clear(&tag_refspec);
 	return local_refs;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 75255dc600..06b60867f5 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
 		}
 	}
 
-	if (tags == TAGS_SET)
+	if (tags == TAGS_SET) {
+		struct refspec_item tag_refspec;
+
 		/* also fetch all tags */
-		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-	else if (tags == TAGS_DEFAULT && *autotags)
+		refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+		get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+		refspec_item_clear(&tag_refspec);
+	} else if (tags == TAGS_DEFAULT && *autotags) {
 		find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+	}
 
 	/* Now append any refs to be updated opportunistically: */
 	*tail = orefs;
diff --git a/refspec.c b/refspec.c
index d60932f4de..1df5de6c2f 100644
--- a/refspec.c
+++ b/refspec.c
@@ -7,19 +7,6 @@
 #include "refspec.h"
 #include "strbuf.h"
 
-static struct refspec_item s_tag_refspec = {
-	.force = 0,
-	.pattern = 1,
-	.matching = 0,
-	.exact_sha1 = 0,
-	.negative = 0,
-	.src = "refs/tags/*",
-	.dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
 /*
  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
  * Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446993..754be45cee 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
 #define REFSPEC_H
 
 #define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
 
 /**
  * A struct refspec_item holds the parsed interpretation of a refspec.  If it
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 06/19] http: do not assign string constant to non-const field
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (4 preceding siblings ...)
  2024-05-30 12:50   ` [PATCH v2 05/19] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-05-30 12:50   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 07/19] line-log: always allocate the output prefix Patrick Steinhardt
                     ` (13 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:50 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1260 bytes --]

In `write_accept_language()`, we put all acceptable languages into an
array. While all entries in that array are allocated strings, the final
entry in that array is a string constant. This is fine because we
explicitly skip over the last entry when freeing the array, but will
cause warnings once we enable `-Wwrite-strings`.

Adapt the code to also allocate the final entry.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 http.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/http.c b/http.c
index 67cc47d28f..2dea2d03da 100644
--- a/http.c
+++ b/http.c
@@ -1974,7 +1974,7 @@ static void write_accept_language(struct strbuf *buf)
 
 		/* add '*' */
 		REALLOC_ARRAY(language_tags, num_langs + 1);
-		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+		language_tags[num_langs++] = xstrdup("*");
 
 		/* compute decimal_places */
 		for (max_q = 1, decimal_places = 0;
@@ -2004,8 +2004,7 @@ static void write_accept_language(struct strbuf *buf)
 		}
 	}
 
-	/* free language tags -- last one is a static '*' */
-	for (i = 0; i < num_langs - 1; i++)
+	for (i = 0; i < num_langs; i++)
 		free(language_tags[i]);
 	free(language_tags);
 }
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 07/19] line-log: always allocate the output prefix
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (5 preceding siblings ...)
  2024-05-30 12:50   ` [PATCH v2 06/19] http: do not assign string constant to non-const field Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 08/19] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
                     ` (12 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2111 bytes --]

The returned string by `output_prefix()` is sometimes a string constant
and sometimes an allocated string. This has been fine until now because
we always leak the allocated strings, and thus we never tried to free
the string constant.

Fix the code to always return an allocated string and free the returned
value at all callsites.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/line-log.c b/line-log.c
index d9bf2c8120..9a298209d0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
 
 static char *output_prefix(struct diff_options *opt)
 {
-	char *prefix = "";
-
 	if (opt->output_prefix) {
 		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
-		prefix = sb->buf;
+		return sb->buf;
+	} else {
+		return xstrdup("");
 	}
-
-	return prefix;
 }
 
 static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
 
 	if (!pair || !diff)
-		return;
+		goto out;
 
 	if (pair->one->oid_valid)
 		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 				   c_context, c_reset, opt->file);
 	}
 
+out:
 	free(p_ends);
 	free(t_ends);
+	free(prefix);
 }
 
 /*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
  */
 static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
 {
-	fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+	char *prefix = output_prefix(&rev->diffopt);
+
+	fprintf(rev->diffopt.file, "%s\n", prefix);
+	free(prefix);
+
 	while (range) {
 		dump_diff_hacky_one(rev, range);
 		range = range->next;
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 08/19] object-file: make `buf` parameter of `index_mem()` a constant
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (6 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 07/19] line-log: always allocate the output prefix Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 09/19] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
                     ` (11 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1863 bytes --]

The `buf` parameter of `index_mem()` is a non-constant string. This will
break once we enable `-Wwrite-strings` because we also pass constants
from at least one callsite.

Adapt the parameter to be a constant. As we cannot free the buffer
without casting now, this also requires us to move the lifetime of the
nested buffer around.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/object-file.c b/object-file.c
index c9e374e57e..46ea00ac46 100644
--- a/object-file.c
+++ b/object-file.c
@@ -2483,12 +2483,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
 }
 
 static int index_mem(struct index_state *istate,
-		     struct object_id *oid, void *buf, size_t size,
+		     struct object_id *oid,
+		     const void *buf, size_t size,
 		     enum object_type type,
 		     const char *path, unsigned flags)
 {
+	struct strbuf nbuf = STRBUF_INIT;
 	int ret = 0;
-	int re_allocated = 0;
 	int write_object = flags & HASH_WRITE_OBJECT;
 
 	if (!type)
@@ -2498,11 +2499,10 @@ static int index_mem(struct index_state *istate,
 	 * Convert blobs to git internal format
 	 */
 	if ((type == OBJ_BLOB) && path) {
-		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(istate, path, buf, size, &nbuf,
 				   get_conv_flags(flags))) {
-			buf = strbuf_detach(&nbuf, &size);
-			re_allocated = 1;
+			buf = nbuf.buf;
+			size = nbuf.len;
 		}
 	}
 	if (flags & HASH_FORMAT_CHECK) {
@@ -2519,8 +2519,8 @@ static int index_mem(struct index_state *istate,
 		ret = write_object_file(buf, size, type, oid);
 	else
 		hash_object_file(the_hash_algo, buf, size, type, oid);
-	if (re_allocated)
-		free(buf);
+
+	strbuf_release(&nbuf);
 	return ret;
 }
 
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 09/19] parse-options: cast long name for OPTION_ALIAS
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (7 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 08/19] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 10/19] send-pack: always allocate receive status Patrick Steinhardt
                     ` (10 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 792 bytes --]

We assign the long name for OPTION_ALIAS options to a non-constant value
field. We know that the variable will never be written to, but this will
cause warnings once we enable `-Wwrite-strings`.

Cast away the constness to be prepared for this change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 parse-options.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/parse-options.h b/parse-options.h
index bd62e20268..ae15342390 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
 	.type = OPTION_ALIAS, \
 	.short_name = (s), \
 	.long_name = (l), \
-	.value = (source_long_name), \
+	.value = (char *)(source_long_name), \
 }
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 10/19] send-pack: always allocate receive status
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (8 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 09/19] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 11/19] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
                     ` (9 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1586 bytes --]

In `receive_status()`, we record the reason why ref updates have been
rejected by the remote via the `remote_status`. But while we allocate
the assigned string when a reason was given, we assign a string constant
when no reason was given.

This has been working fine so far due to two reasons:

  - We don't ever free the refs in git-send-pack(1)'

  - Remotes always give a reason, at least as implemented by Git proper.

Adapt the code to always allocate the receive status string and free the
refs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/send-pack.c | 2 ++
 send-pack.c         | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaad09..17cae6bbbd 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
+	free_refs(remote_refs);
+	free_refs(local_refs);
 	return ret;
 }
diff --git a/send-pack.c b/send-pack.c
index 37f59d4f66..88e96d000b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -259,7 +259,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
 			if (p)
 				hint->remote_status = xstrdup(p);
 			else
-				hint->remote_status = "failed";
+				hint->remote_status = xstrdup("failed");
 		} else {
 			hint->status = REF_STATUS_OK;
 			hint->remote_status = xstrdup_or_null(p);
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 11/19] remote-curl: avoid assigning string constant to non-const variable
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (9 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 10/19] send-pack: always allocate receive status Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 12/19] revision: always store allocated strings in output encoding Patrick Steinhardt
                     ` (8 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 7752 bytes --]

When processing remote options, we split the option line into two by
searching for a space. If there is one, we replace the space with '\0',
otherwise we implicitly assume that the value is "true" and thus assign
a string constant.

As the return value of strchr(3P) weirdly enough is a `char *` even
though it gets a `const char *` as input, the assigned-to variable also
is a non-constant. This is fine though because the argument is in fact
an allocated string, and thus we are allowed to modify it. But this will
break once we enable `-Wwrite-strings`.

Refactor the code stop splitting the fields with '\0' altogether.
Instead, we can pass the length of the option name to `set_option()` and
then use strncmp(3P) instead of strcmp(3P).

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c | 53 ++++++++++++++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index cae98384da..d0f767df8e 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -58,9 +58,9 @@ struct options {
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
 
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
 {
-	if (!strcmp(name, "verbosity")) {
+	if (!strncmp(name, "verbosity", namelen)) {
 		char *end;
 		int v = strtol(value, &end, 10);
 		if (value == end || *end)
@@ -68,7 +68,7 @@ static int set_option(const char *name, const char *value)
 		options.verbosity = v;
 		return 0;
 	}
-	else if (!strcmp(name, "progress")) {
+	else if (!strncmp(name, "progress", namelen)) {
 		if (!strcmp(value, "true"))
 			options.progress = 1;
 		else if (!strcmp(value, "false"))
@@ -77,7 +77,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "depth")) {
+	else if (!strncmp(name, "depth", namelen)) {
 		char *end;
 		unsigned long v = strtoul(value, &end, 10);
 		if (value == end || *end)
@@ -85,15 +85,15 @@ static int set_option(const char *name, const char *value)
 		options.depth = v;
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-since")) {
+	else if (!strncmp(name, "deepen-since", namelen)) {
 		options.deepen_since = xstrdup(value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-not")) {
+	else if (!strncmp(name, "deepen-not", namelen)) {
 		string_list_append(&options.deepen_not, value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-relative")) {
+	else if (!strncmp(name, "deepen-relative", namelen)) {
 		if (!strcmp(value, "true"))
 			options.deepen_relative = 1;
 		else if (!strcmp(value, "false"))
@@ -102,7 +102,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "followtags")) {
+	else if (!strncmp(name, "followtags", namelen)) {
 		if (!strcmp(value, "true"))
 			options.followtags = 1;
 		else if (!strcmp(value, "false"))
@@ -111,7 +111,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "dry-run")) {
+	else if (!strncmp(name, "dry-run", namelen)) {
 		if (!strcmp(value, "true"))
 			options.dry_run = 1;
 		else if (!strcmp(value, "false"))
@@ -120,7 +120,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "check-connectivity")) {
+	else if (!strncmp(name, "check-connectivity", namelen)) {
 		if (!strcmp(value, "true"))
 			options.check_self_contained_and_connected = 1;
 		else if (!strcmp(value, "false"))
@@ -129,7 +129,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "cas")) {
+	else if (!strncmp(name, "cas", namelen)) {
 		struct strbuf val = STRBUF_INIT;
 		strbuf_addstr(&val, "--force-with-lease=");
 		if (*value != '"')
@@ -139,7 +139,7 @@ static int set_option(const char *name, const char *value)
 		string_list_append(&cas_options, val.buf);
 		strbuf_release(&val);
 		return 0;
-	} else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+	} else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
 		if (!strcmp(value, "true"))
 			options.force_if_includes = 1;
 		else if (!strcmp(value, "false"))
@@ -147,7 +147,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "cloning")) {
+	} else if (!strncmp(name, "cloning", namelen)) {
 		if (!strcmp(value, "true"))
 			options.cloning = 1;
 		else if (!strcmp(value, "false"))
@@ -155,7 +155,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "update-shallow")) {
+	} else if (!strncmp(name, "update-shallow", namelen)) {
 		if (!strcmp(value, "true"))
 			options.update_shallow = 1;
 		else if (!strcmp(value, "false"))
@@ -163,7 +163,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "pushcert")) {
+	} else if (!strncmp(name, "pushcert", namelen)) {
 		if (!strcmp(value, "true"))
 			options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
 		else if (!strcmp(value, "false"))
@@ -173,7 +173,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "atomic")) {
+	} else if (!strncmp(name, "atomic", namelen)) {
 		if (!strcmp(value, "true"))
 			options.atomic = 1;
 		else if (!strcmp(value, "false"))
@@ -181,7 +181,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "push-option")) {
+	} else if (!strncmp(name, "push-option", namelen)) {
 		if (*value != '"')
 			string_list_append(&options.push_options, value);
 		else {
@@ -192,7 +192,7 @@ static int set_option(const char *name, const char *value)
 						 strbuf_detach(&unquoted, NULL));
 		}
 		return 0;
-	} else if (!strcmp(name, "family")) {
+	} else if (!strncmp(name, "family", namelen)) {
 		if (!strcmp(value, "ipv4"))
 			git_curl_ipresolve = CURL_IPRESOLVE_V4;
 		else if (!strcmp(value, "ipv6"))
@@ -202,16 +202,16 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "from-promisor")) {
+	} else if (!strncmp(name, "from-promisor", namelen)) {
 		options.from_promisor = 1;
 		return 0;
-	} else if (!strcmp(name, "refetch")) {
+	} else if (!strncmp(name, "refetch", namelen)) {
 		options.refetch = 1;
 		return 0;
-	} else if (!strcmp(name, "filter")) {
+	} else if (!strncmp(name, "filter", namelen)) {
 		options.filter = xstrdup(value);
 		return 0;
-	} else if (!strcmp(name, "object-format")) {
+	} else if (!strncmp(name, "object-format", namelen)) {
 		options.object_format = 1;
 		if (strcmp(value, "true"))
 			die(_("unknown value for object-format: %s"), value);
@@ -1588,15 +1588,16 @@ int cmd_main(int argc, const char **argv)
 			parse_push(&buf);
 
 		} else if (skip_prefix(buf.buf, "option ", &arg)) {
-			char *value = strchr(arg, ' ');
+			const char *value = strchrnul(arg, ' ');
+			size_t arglen = value - arg;
 			int result;
 
-			if (value)
-				*value++ = '\0';
+			if (*value)
+				value++; /* skip over SP */
 			else
 				value = "true";
 
-			result = set_option(arg, value);
+			result = set_option(arg, arglen, value);
 			if (!result)
 				printf("ok\n");
 			else if (result < 0)
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 12/19] revision: always store allocated strings in output encoding
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (10 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 11/19] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 13/19] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
                     ` (7 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2531 bytes --]

The `git_log_output_encoding` variable can be set via the `--encoding=`
option. When doing so, we conditionally either assign it to the passed
value, or if the value is "none" we assign it the empty string.
Depending on which of the both code paths we pick though, the variable
may end up being assigned either an allocated string or a string
constant.

This is somewhat risky and may easily lead to bugs when a different code
path may want to reassign a new value to it, freeing the previous value.
We already to this when parsing the "i18n.logoutputencoding" config in
`git_default_i18n_config()`. But because the config is typically parsed
before we parse command line options this has been fine so far.

Regardless of that, safeguard the code such that the variable always
contains an allocated string. While at it, also free the old value in
case there was any to plug a potential memory leak.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 revision.c             | 3 ++-
 t/t3900-i18n-commit.sh | 1 +
 t/t3901-i18n-patch.sh  | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 7ddf0f151a..2ee6886078 100644
--- a/revision.c
+++ b/revision.c
@@ -2650,10 +2650,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		free(git_log_output_encoding);
 		if (strcmp(optarg, "none"))
 			git_log_output_encoding = xstrdup(optarg);
 		else
-			git_log_output_encoding = "";
+			git_log_output_encoding = xstrdup("");
 		return argcount;
 	} else if (!strcmp(arg, "--reverse")) {
 		revs->reverse ^= 1;
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09cfd9..db7b403bc1 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
 
 test_description='commit and log output encodings'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78829..5f0b9afc3f 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@ test_description='i18n settings and format-patch | am pipe'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_encoding () {
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 13/19] mailmap: always store allocated strings in mailmap blob
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (11 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 12/19] revision: always store allocated strings in output encoding Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 14/19] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
                     ` (6 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Same as with the preceding commit, the `git_mailmap_blob` may sometimes
contain an allocated string and sometimes it may contain a string
constant. This is risky and can easily lead to bugs in case the variable
is getting re-assigned, where the code may then try to free the previous
value to avoid memory leaks.

Safeguard the code by always storing allocated strings in the variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 mailmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mailmap.c b/mailmap.c
index b2efe29b3d..3d1e092fef 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -216,7 +216,7 @@ int read_mailmap(struct string_list *map)
 	map->cmp = namemap_cmp;
 
 	if (!git_mailmap_blob && is_bare_repository())
-		git_mailmap_blob = "HEAD:.mailmap";
+		git_mailmap_blob = xstrdup("HEAD:.mailmap");
 
 	if (!startup_info->have_repository || !is_bare_repository())
 		err |= read_mailmap_file(map, ".mailmap",
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 14/19] imap-send: drop global `imap_server_conf` variable
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (12 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 13/19] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 15/19] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
                     ` (5 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 7200 bytes --]

In "imap-send.c", we have a global `sturct imap_server_conf` variable
that keeps track of the configuration of the IMAP server. This variable
is being populated mostly via the Git configuration.

Refactor the code to allocate the structure on the stack instead of
having it globally. This change allows us to track its lifetime more
closely.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 57 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 8b723b34a5..67a7a6c456 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -82,10 +82,6 @@ struct imap_server_conf {
 	char *auth_method;
 };
 
-static struct imap_server_conf server = {
-	.ssl_verify = 1,
-};
-
 struct imap_socket {
 	int fd[2];
 	SSL *ssl;
@@ -110,6 +106,7 @@ struct imap {
 };
 
 struct imap_store {
+	const struct imap_server_conf *cfg;
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	int uidvalidity;
@@ -194,8 +191,8 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
 
 #ifdef NO_OPENSSL
 static int ssl_socket_connect(struct imap_socket *sock UNUSED,
-			      int use_tls_only UNUSED,
-			      int verify UNUSED)
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only UNUSED)
 {
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
@@ -250,7 +247,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
 		     cname, hostname);
 }
 
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock,
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 	const SSL_METHOD *meth;
@@ -279,7 +278,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	if (use_tls_only)
 		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
-	if (verify)
+	if (cfg->ssl_verify)
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
 
 	if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +305,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	 * OpenSSL does not document this function, but the implementation
 	 * returns 1 on success, 0 on failure after calling SSLerr().
 	 */
-	ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+	ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
 	if (ret != 1)
-		warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+		warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
 #endif
 
 	ret = SSL_connect(sock->ssl);
@@ -317,12 +316,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 		return -1;
 	}
 
-	if (verify) {
+	if (cfg->ssl_verify) {
 		/* make sure the hostname matches that of the certificate */
 		cert = SSL_get_peer_certificate(sock->ssl);
 		if (!cert)
 			return error("unable to get peer certificate.");
-		if (verify_hostname(cert, server.host) < 0)
+		if (verify_hostname(cert, cfg->host) < 0)
 			return -1;
 	}
 
@@ -895,7 +894,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 	int ret;
 	char *response;
 
-	response = cram(prompt, server.user, server.pass);
+	response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
 
 	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
 	if (ret != strlen(response))
@@ -935,6 +934,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 
 	CALLOC_ARRAY(ctx, 1);
 
+	ctx->cfg = srvc;
 	ctx->imap = CALLOC_ARRAY(imap, 1);
 	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
 	imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1035,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		imap->buf.sock.fd[1] = dup(s);
 
 		if (srvc->use_ssl &&
-		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+		    ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
 			close(s);
 			goto bail;
 		}
@@ -1068,8 +1068,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		if (!srvc->use_ssl && CAP(STARTTLS)) {
 			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
 				goto bail;
-			if (ssl_socket_connect(&imap->buf.sock, 1,
-					       srvc->ssl_verify))
+			if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
 				goto bail;
 			/* capabilities may have changed, so get the new capabilities */
 			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1299,23 +1298,24 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 static int git_imap_config(const char *var, const char *val,
 			   const struct config_context *ctx, void *cb)
 {
+	struct imap_server_conf *cfg = cb;
 
 	if (!strcmp("imap.sslverify", var))
-		server.ssl_verify = git_config_bool(var, val);
+		cfg->ssl_verify = git_config_bool(var, val);
 	else if (!strcmp("imap.preformattedhtml", var))
-		server.use_html = git_config_bool(var, val);
+		cfg->use_html = git_config_bool(var, val);
 	else if (!strcmp("imap.folder", var))
-		return git_config_string(&server.folder, var, val);
+		return git_config_string(&cfg->folder, var, val);
 	else if (!strcmp("imap.user", var))
-		return git_config_string(&server.user, var, val);
+		return git_config_string(&cfg->user, var, val);
 	else if (!strcmp("imap.pass", var))
-		return git_config_string(&server.pass, var, val);
+		return git_config_string(&cfg->pass, var, val);
 	else if (!strcmp("imap.tunnel", var))
-		return git_config_string(&server.tunnel, var, val);
+		return git_config_string(&cfg->tunnel, var, val);
 	else if (!strcmp("imap.authmethod", var))
-		return git_config_string(&server.auth_method, var, val);
+		return git_config_string(&cfg->auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val, ctx->kvi);
+		cfg->port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
@@ -1324,11 +1324,11 @@ static int git_imap_config(const char *var, const char *val,
 				val += 5;
 			else if (starts_with(val, "imaps:")) {
 				val += 6;
-				server.use_ssl = 1;
+				cfg->use_ssl = 1;
 			}
 			if (starts_with(val, "//"))
 				val += 2;
-			server.host = xstrdup(val);
+			cfg->host = xstrdup(val);
 		}
 	} else
 		return git_default_config(var, val, ctx, cb);
@@ -1497,12 +1497,15 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
 
 int cmd_main(int argc, const char **argv)
 {
+	struct imap_server_conf server = {
+		.ssl_verify = 1,
+	};
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
 
 	setup_git_directory_gently(&nongit_ok);
-	git_config(git_imap_config, NULL);
+	git_config(git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
 
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 15/19] imap-send: fix leaking memory in `imap_server_conf`
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (13 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 14/19] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 16/19] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
                     ` (4 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 4324 bytes --]

We never free any of the config strings that we populate into the
`struct imap_server_conf`. Fix this by creating a common exit path where
we can free resources.

While at it, drop the unused member `imap_server_conf::name`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 65 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 23 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 67a7a6c456..da3e7ec17e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,7 +69,6 @@ static void imap_warn(const char *, ...);
 static char *next_arg(char **);
 
 struct imap_server_conf {
-	const char *name;
 	char *tunnel;
 	char *host;
 	int port;
@@ -1300,23 +1299,28 @@ static int git_imap_config(const char *var, const char *val,
 {
 	struct imap_server_conf *cfg = cb;
 
-	if (!strcmp("imap.sslverify", var))
+	if (!strcmp("imap.sslverify", var)) {
 		cfg->ssl_verify = git_config_bool(var, val);
-	else if (!strcmp("imap.preformattedhtml", var))
+	} else if (!strcmp("imap.preformattedhtml", var)) {
 		cfg->use_html = git_config_bool(var, val);
-	else if (!strcmp("imap.folder", var))
+	} else if (!strcmp("imap.folder", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->folder, var, val);
-	else if (!strcmp("imap.user", var))
+	} else if (!strcmp("imap.user", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->user, var, val);
-	else if (!strcmp("imap.pass", var))
+	} else if (!strcmp("imap.pass", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->pass, var, val);
-	else if (!strcmp("imap.tunnel", var))
+	} else if (!strcmp("imap.tunnel", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->tunnel, var, val);
-	else if (!strcmp("imap.authmethod", var))
+	} else if (!strcmp("imap.authmethod", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->auth_method, var, val);
-	else if (!strcmp("imap.port", var))
+	} else if (!strcmp("imap.port", var)) {
 		cfg->port = git_config_int(var, val, ctx->kvi);
-	else if (!strcmp("imap.host", var)) {
+	} else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
 		} else {
@@ -1330,8 +1334,9 @@ static int git_imap_config(const char *var, const char *val,
 				val += 2;
 			cfg->host = xstrdup(val);
 		}
-	} else
+	} else {
 		return git_default_config(var, val, ctx, cb);
+	}
 
 	return 0;
 }
@@ -1503,6 +1508,7 @@ int cmd_main(int argc, const char **argv)
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
+	int ret;
 
 	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, &server);
@@ -1529,42 +1535,55 @@ int cmd_main(int argc, const char **argv)
 
 	if (!server.folder) {
 		fprintf(stderr, "no imap store specified\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
 			fprintf(stderr, "no imap host specified\n");
-			return 1;
+			ret = 1;
+			goto out;
 		}
-		server.host = "tunnel";
+		server.host = xstrdup("tunnel");
 	}
 
 	/* read the messages */
 	if (strbuf_read(&all_msgs, 0, 0) < 0) {
 		error_errno(_("could not read from stdin"));
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	if (all_msgs.len == 0) {
 		fprintf(stderr, "nothing to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	total = count_messages(&all_msgs);
 	if (!total) {
 		fprintf(stderr, "no messages to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	/* write it to the imap server */
 
 	if (server.tunnel)
-		return append_msgs_to_imap(&server, &all_msgs, total);
-
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
 #ifdef USE_CURL_FOR_IMAP_SEND
-	if (use_curl)
-		return curl_append_msgs_to_imap(&server, &all_msgs, total);
+	else if (use_curl)
+		ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
 #endif
-
-	return append_msgs_to_imap(&server, &all_msgs, total);
+	else
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
+
+out:
+	free(server.tunnel);
+	free(server.host);
+	free(server.folder);
+	free(server.user);
+	free(server.pass);
+	free(server.auth_method);
+	return ret;
 }
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 16/19] builtin/rebase: do not assign default backend to non-constant field
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (14 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 15/19] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 17/19] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
                     ` (3 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 4136 bytes --]

The `struct rebase_options::default_backend` field is a non-constant
string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
Refactor the code to initialize and release options via two functions
`rebase_options_init()` and `rebase_options_release()`. Like this, we
can easily adapt the former funnction to use `xstrdup()` on the default
value without hiding it away in a macro.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 67 ++++++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 28 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 14d4f0a5e6..11f276012c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -131,25 +131,40 @@ struct rebase_options {
 	int config_update_refs;
 };
 
-#define REBASE_OPTIONS_INIT {			  	\
-		.type = REBASE_UNSPECIFIED,	  	\
-		.empty = EMPTY_UNSPECIFIED,	  	\
-		.keep_empty = 1,			\
-		.default_backend = "merge",	  	\
-		.flags = REBASE_NO_QUIET, 		\
-		.git_am_opts = STRVEC_INIT,		\
-		.exec = STRING_LIST_INIT_NODUP,		\
-		.git_format_patch_opt = STRBUF_INIT,	\
-		.fork_point = -1,			\
-		.reapply_cherry_picks = -1,             \
-		.allow_empty_message = 1,               \
-		.autosquash = -1,                       \
-		.rebase_merges = -1,                    \
-		.config_rebase_merges = -1,             \
-		.update_refs = -1,                      \
-		.config_update_refs = -1,               \
-		.strategy_opts = STRING_LIST_INIT_NODUP,\
-	}
+static void rebase_options_init(struct rebase_options *opts)
+{
+	memset(opts, 0, sizeof(*opts));
+	opts->type = REBASE_UNSPECIFIED;
+	opts->empty = EMPTY_UNSPECIFIED;
+	opts->default_backend = xstrdup("merge");
+	opts->keep_empty = 1;
+	opts->flags = REBASE_NO_QUIET;
+	strvec_init(&opts->git_am_opts);
+	string_list_init_nodup(&opts->exec);
+	strbuf_init(&opts->git_format_patch_opt, 0);
+	opts->fork_point = -1;
+	opts->reapply_cherry_picks = -1;
+	opts->allow_empty_message = 1;
+	opts->autosquash = -1;
+	opts->rebase_merges = -1;
+	opts->config_rebase_merges = -1;
+	opts->update_refs = -1;
+	opts->config_update_refs = -1;
+	string_list_init_nodup(&opts->strategy_opts);
+}
+
+static void rebase_options_release(struct rebase_options *opts)
+{
+	free(opts->default_backend);
+	free(opts->reflog_action);
+	free(opts->head_name);
+	strvec_clear(&opts->git_am_opts);
+	free(opts->gpg_sign_opt);
+	string_list_clear(&opts->exec, 0);
+	free(opts->strategy);
+	string_list_clear(&opts->strategy_opts, 0);
+	strbuf_release(&opts->git_format_patch_opt);
+}
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 {
@@ -796,6 +811,7 @@ static int rebase_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "rebase.backend")) {
+		FREE_AND_NULL(opts->default_backend);
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
@@ -1045,7 +1061,7 @@ static int check_exec_cmd(const char *cmd)
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
-	struct rebase_options options = REBASE_OPTIONS_INIT;
+	struct rebase_options options;
 	const char *branch_name;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
@@ -1178,6 +1194,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	};
 	int i;
 
+	rebase_options_init(&options);
+
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_rebase_usage,
 				   builtin_rebase_options);
@@ -1833,14 +1851,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 cleanup:
 	strbuf_release(&buf);
 	strbuf_release(&revisions);
-	free(options.reflog_action);
-	free(options.head_name);
-	strvec_clear(&options.git_am_opts);
-	free(options.gpg_sign_opt);
-	string_list_clear(&options.exec, 0);
-	free(options.strategy);
-	string_list_clear(&options.strategy_opts, 0);
-	strbuf_release(&options.git_format_patch_opt);
+	rebase_options_release(&options);
 	free(squash_onto_name);
 	free(keep_base_onto_name);
 	return !!ret;
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 17/19] builtin/rebase: always store allocated string in `options.strategy`
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (15 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 16/19] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:51   ` [PATCH v2 18/19] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
                     ` (2 subsequent siblings)
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2338 bytes --]

The `struct rebase_options::strategy` field is a `char *`, but we do end
up assigning string constants to it in two cases:

  - When being passed a `--strategy=` option via the command line.

  - When being passed a strategy option via `--strategy-option=`, but
    not a strategy.

This will cause warnings once we enable `-Wwrite-strings`.

Ideally, we'd just convert the field to be a `const char *`. But we also
assign to this field via the GIT_TEST_MERGE_ALGORITHM envvar, which we
have to strdup(3P) into it.

Instead, refactor the code to make sure that we only ever assign
allocated strings to this field.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 11f276012c..26068cf542 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1063,6 +1063,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options;
 	const char *branch_name;
+	const char *strategy_opt = NULL;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
 	int ok_to_skip_pre_rebase = 0;
@@ -1177,7 +1178,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
-		OPT_STRING('s', "strategy", &options.strategy,
+		OPT_STRING('s', "strategy", &strategy_opt,
 			   N_("strategy"), N_("use the given merge strategy")),
 		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
 				N_("option"),
@@ -1488,13 +1489,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	if (options.strategy_opts.nr && !options.strategy)
-		options.strategy = "ort";
-
-	if (options.strategy) {
-		options.strategy = xstrdup(options.strategy);
+	if (strategy_opt)
+		options.strategy = xstrdup(strategy_opt);
+	else if (options.strategy_opts.nr && !options.strategy)
+		options.strategy = xstrdup("ort");
+	if (options.strategy)
 		imply_merge(&options, "--strategy");
-	}
 
 	if (options.root && !options.onto_name)
 		imply_merge(&options, "--root without --onto");
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 18/19] builtin/merge: always store allocated strings in `pull_twohead`
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (16 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 17/19] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
@ 2024-05-30 12:51   ` Patrick Steinhardt
  2024-05-30 12:52   ` [PATCH v2 19/19] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
  2024-05-31  9:13   ` [PATCH v2 00/19] Compile with `-Wwrite-strings` Junio C Hamano
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:51 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2451 bytes --]

The `pull_twohead` configuration may sometimes contain an allocated
string, and sometimes it may contain a string constant. Refactor this to
instead always store an allocated string such that we can release its
resources without risk.

While at it, manage the lifetime of other config strings, as well. Note
that we explicitly don't free `cleanup_arg` here. This is because the
variable may be assigned a string constant via command line options.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/merge.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/builtin/merge.c b/builtin/merge.c
index daed2d4e1e..fb3eb15b89 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -611,17 +611,19 @@ static int git_merge_config(const char *k, const char *v,
 		return 0;
 	}
 
-	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
 		show_diffstat = git_config_bool(k, v);
-	else if (!strcmp(k, "merge.verifysignatures"))
+	} else if (!strcmp(k, "merge.verifysignatures")) {
 		verify_signatures = git_config_bool(k, v);
-	else if (!strcmp(k, "pull.twohead"))
+	} else if (!strcmp(k, "pull.twohead")) {
+		FREE_AND_NULL(pull_twohead);
 		return git_config_string(&pull_twohead, k, v);
-	else if (!strcmp(k, "pull.octopus"))
+	} else if (!strcmp(k, "pull.octopus")) {
+		FREE_AND_NULL(pull_octopus);
 		return git_config_string(&pull_octopus, k, v);
-	else if (!strcmp(k, "commit.cleanup"))
+	} else if (!strcmp(k, "commit.cleanup")) {
 		return git_config_string(&cleanup_arg, k, v);
-	else if (!strcmp(k, "merge.ff")) {
+	} else if (!strcmp(k, "merge.ff")) {
 		int boolval = git_parse_maybe_bool(v);
 		if (0 <= boolval) {
 			fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -1294,7 +1296,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (!pull_twohead) {
 		char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
 		if (default_strategy && !strcmp(default_strategy, "ort"))
-			pull_twohead = "ort";
+			pull_twohead = xstrdup("ort");
 	}
 
 	init_diff_ui_defaults();
@@ -1793,6 +1795,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
+	free(pull_twohead);
+	free(pull_octopus);
 	discard_index(the_repository->index);
 	return ret;
 }
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v2 19/19] config.mak.dev: enable `-Wwrite-strings` warning
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (17 preceding siblings ...)
  2024-05-30 12:51   ` [PATCH v2 18/19] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
@ 2024-05-30 12:52   ` Patrick Steinhardt
  2024-05-31  9:13   ` [PATCH v2 00/19] Compile with `-Wwrite-strings` Junio C Hamano
  19 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-30 12:52 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

Writing to string constants is undefined behaviour and must be avoided
in C. Even so, the compiler does not help us with this by default
because those constants are not in fact marked as `const`. This makes it
rather easy to accidentally assign a constant to a non-const variable or
field and then later on try to either free it or write to it.

Enable `-Wwrite-strings` to catch such mistakes. With this warning
enabled, the type of string constants is changed to `const char[]` and
will thus cause compiler warnings when being assigned to non-const
fields and variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.mak.dev | 1 +
 1 file changed, 1 insertion(+)

diff --git a/config.mak.dev b/config.mak.dev
index 981304727c..1ce4c70613 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -37,6 +37,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
 DEVELOPER_CFLAGS += -Wstrict-prototypes
 DEVELOPER_CFLAGS += -Wunused
 DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
 DEVELOPER_CFLAGS += -fno-common
 
 ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
-- 
2.45.1.313.g3a57aa566a.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH 03/19] global: convert intentionally-leaking config strings to consts
  2024-05-30 11:30     ` Patrick Steinhardt
@ 2024-05-30 16:00       ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-30 16:00 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

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?


^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 05/19] reftable: improve const correctness when assigning string constants
  2024-05-30 11:30     ` Patrick Steinhardt
@ 2024-05-30 16:07       ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-30 16:07 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> On Wed, May 29, 2024 at 10:43:47AM -0700, Junio C Hamano wrote:
>> Patrick Steinhardt <ps@pks.im> writes:
>> 
>> > diff --git a/reftable/basics_test.c b/reftable/basics_test.c
>> > index 997c4d9e01..af9209d535 100644
>> > --- a/reftable/basics_test.c
>> > +++ b/reftable/basics_test.c
>> > @@ -58,8 +58,8 @@ static void test_binsearch(void)
>> >  
>> >  static void test_names_length(void)
>> >  {
>> > -	char *a[] = { "a", "b", NULL };
>> > -	EXPECT(names_length(a) == 2);
>> > +	char *names[] = { (char *)"a", (char *)"b", NULL };
>> > +	EXPECT(names_length(names) == 2);
>> >  }
>> 
>> I would have preferred to see this kind of rewrite more than
>> separate and clearly writable variables that are initialied with the
>> constant contents e.g. branches[] = "refs/heads/*", we saw in
>> earlier steps.  Wouldn't that approach, combined with making the
>> literal constants stored in read-only segment to trigger runtime
>> failure when a bug causes the "unfortunately non-const" variables
>> to be written, give us a better result?
>
> Depends on what we mean by "better", I guess. But yeah, I was torn
> myself when writing this commit because there are so many string
> constants in the reftable tests that we assign to non-constant fields. I
> didn't find the result particularly easy to read when putting each of
> the constants into a separate variable.

Oh, I do *not* want to see.

	char a_string[] = "a";
	char *names[] = { a_string, ... };

As a way to workaround the -Wwritable-strings warnings, what we see
in this patch is much better.

I was referring to a redoing of this series in the other direction.
The earlier one that introduced

	char refspec_string[] = "refs/heads/*";

and rewrite assignments of

	non_const_pointer_to_char = "refs/heads/*";

with

	non_const_pointer_to_char = refspec_string;

would have been better if it were done without refspec_string[]
array.  It is a runtime bug to overwrite the refspec_string[] via
the non_const_pointer_to_char, but use of refspec_string[] would
make it impossible for us to catch.



^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf`
  2024-05-30 11:31     ` Patrick Steinhardt
@ 2024-05-30 16:30       ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-30 16:30 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> Oh, I wasn't aware that the parameter being `NULL` actually causes a
> change in behaviour. Which nicely demonstrates that we have some missing
> test coverage for git-imap-send(1).
>
> In fact, it's not only "some". We don't have any test coverage at all
> for git-imap-send(1) as far as I can see. Which does make me rest a bit
> uneasy. And I suspect that it wouldn't be trivial to add given that it
> kind of requires something that talks IMAP on the receiving end.

Quite honestly, there is absolutely nothing that makes imap-send
necessary to be part of Git suite.  It does depend on Git as it uses
our configuration files to store its knobs, but if it were written
outside the context of the Git project as a handy way to move a draft
message to your imap draft folder, it would have been perfectly fine
as a standalone tool (and it would have used its own simple
configuration file format).

Other than its use of .gitconfig, it does not even know what a
commit is, unlike, say send-email [*], which is also fairly
unrelated to Git proper.

    Side note: send-email is only marginally closer to Git than
    imap-send, as it optionally can run format-patch as a part of
    its operation.  Without the format-patch integration, it is just
    a MUA whose primary purpose is not to munge its payload.

So I do not find it surprising at all that we have no tests for it.
We could do something like this at least, perhaps on top of the
recent topic that added a test to ensure commands that ought to be
operable without any repository?

 t/t1517-outside-repo.sh | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git c/t/t1517-outside-repo.sh w/t/t1517-outside-repo.sh
index 557808ffa7..6d5f07df93 100755
--- c/t/t1517-outside-repo.sh
+++ w/t/t1517-outside-repo.sh
@@ -56,4 +56,19 @@ test_expect_success 'grep outside repository' '
 	test_cmp expect actual
 '
 
+test_expect_success 'imap-send outside repository' '
+	test_config_global imap.host imaps://localhost &&
+	test_config_global imap.folder Drafts &&
+
+	echo nothing to send >expect &&
+	test_must_fail git imap-send -v </dev/null 2>actual &&
+	test_cmp expect actual &&
+
+	(
+		cd non-repo &&
+		test_must_fail git imap-send -v </dev/null 2>../actual
+	) &&
+	test_cmp expect actual
+'
+
 test_done

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-30 11:29     ` Patrick Steinhardt
@ 2024-05-30 19:38       ` Junio C Hamano
  2024-05-31 13:00         ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-30 19:38 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> Well, we do. Not in `get_fetch_map()`, but in `query_refspecs()`. It
> does weird stuff where it writes the result into either `src` or `dst`
> depending on which of these fields is provided by the caller. Which
> means that one of the fields would typically be a constant, whereas the
> other one will be allocated.

Yes, <src, dst> is used as a pair of <key, value> to query one with
the other (i.e. "where does this one go?" "where does this one come
from?").

But we are not talking about const-ness of the member (iow, once you
point a string with the member, you cannot repoint the pointer to
another string), but we are talking about const-ness of the string
that is pointed by the member (iow, not "char const *src" but "const
char *src"), no?  If I ask "I have this src, where does it go?" with
a refspec element filled with src, the dst member may need to be
updated to point at the string that is the answer of the query, but
that still can be done with "const char *src, *dst", can't it?  That
was what I was wondering.

And again you are conflating "allocated" with "read-write" here.  It
is often convenient if a variable that points at an allocated string
is of type "char *" and not "const char *", because you do not cast
it when calling free().  But if you want to make a structure member
or a variable that holds an allocated string responsible for
_owning_ the piece of memory, then you need to consistently have the
member point at an allocated piece of memory (or NULL), no?  What
this patch does, i.e. prepare an on-stack refspec_str[] array that
is initialized from a constant string, and have .src member point at
it, would not make .src freeable.  In other words, .src pointing at
an allocated piece of string "some of the time" alone is not a good
justification to make it a non-const pointer, I would think.


^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v2 00/19] Compile with `-Wwrite-strings`
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
                     ` (18 preceding siblings ...)
  2024-05-30 12:52   ` [PATCH v2 19/19] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
@ 2024-05-31  9:13   ` Junio C Hamano
  2024-05-31 12:10     ` Patrick Steinhardt
  19 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-31  9:13 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

>      @@ reftable/readwrite_test.c: static void test_write_key_order(void)
>     + 	struct strbuf buf = STRBUF_INIT;
>     + 	struct reftable_writer *w =
>       		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
>     ++	char a[] = "a", b[] = "b", target[] = "target";

So you decided to go in the complete opposite direction, hmph...

I was hoping that we do not add more "writable" pieces of memory
like target[] only to please the constness-strict compilers.



 

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v2 00/19] Compile with `-Wwrite-strings`
  2024-05-31  9:13   ` [PATCH v2 00/19] Compile with `-Wwrite-strings` Junio C Hamano
@ 2024-05-31 12:10     ` Patrick Steinhardt
  0 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-31 12:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 731 bytes --]

On Fri, May 31, 2024 at 02:13:27AM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> >      @@ reftable/readwrite_test.c: static void test_write_key_order(void)
> >     + 	struct strbuf buf = STRBUF_INIT;
> >     + 	struct reftable_writer *w =
> >       		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
> >     ++	char a[] = "a", b[] = "b", target[] = "target";
> 
> So you decided to go in the complete opposite direction, hmph...
> 
> I was hoping that we do not add more "writable" pieces of memory
> like target[] only to please the constness-strict compilers.

I guess I misunderstood what you were saying. I'll revise this to go
into the other direction.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-30 19:38       ` Junio C Hamano
@ 2024-05-31 13:00         ` Patrick Steinhardt
  2024-05-31 13:33           ` Patrick Steinhardt
  2024-05-31 15:27           ` Junio C Hamano
  0 siblings, 2 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-31 13:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 3453 bytes --]

On Thu, May 30, 2024 at 12:38:45PM -0700, Junio C Hamano wrote:
> Patrick Steinhardt <ps@pks.im> writes:
> 
> > Well, we do. Not in `get_fetch_map()`, but in `query_refspecs()`. It
> > does weird stuff where it writes the result into either `src` or `dst`
> > depending on which of these fields is provided by the caller. Which
> > means that one of the fields would typically be a constant, whereas the
> > other one will be allocated.
> 
> Yes, <src, dst> is used as a pair of <key, value> to query one with
> the other (i.e. "where does this one go?" "where does this one come
> from?").
> 
> But we are not talking about const-ness of the member (iow, once you
> point a string with the member, you cannot repoint the pointer to
> another string), but we are talking about const-ness of the string
> that is pointed by the member (iow, not "char const *src" but "const
> char *src"), no?  If I ask "I have this src, where does it go?" with
> a refspec element filled with src, the dst member may need to be
> updated to point at the string that is the answer of the query, but
> that still can be done with "const char *src, *dst", can't it?  That
> was what I was wondering.

Well, yes, the pointers can be updated. But that doesn't solve the
underlying problem that the interface is not well-prepared to track
ownership of its inputs and outputs.

The root problem is that `struct refspec_item` is being abused for
multiple purposes. One of these purposes it to store parsed refspec
items. Another purpose is to actually serve as a query. The requirements
for those two purposes regarding the lifetime of associated strings is
different:

  - When using it for storage purposes the strings should be owned by
    the structure itself. Consequently, the lifetime should be managed
    by the structure too, namely via `refspec_item_clear()`.

  - When using it for parsing purposes the input string is owned by the
    caller, whereas the output string is owned by the callee and then
    handed over to the caller. Here we cannot rely on the structure
    itself to manage the lifetimes of both strings, at least not in the
    current form.

Now of course, we could amend `struct refspec_item` to grow two
additional fields `src_to_free` and `dst_to_free`. But that only doubles
down on this misdesigned interface.

The proper solution in my opinion is to detangle those two usecases and
split them out into two independent interfaces.

> And again you are conflating "allocated" with "read-write" here.  It
> is often convenient if a variable that points at an allocated string
> is of type "char *" and not "const char *", because you do not cast
> it when calling free().  But if you want to make a structure member
> or a variable that holds an allocated string responsible for
> _owning_ the piece of memory, then you need to consistently have the
> member point at an allocated piece of memory (or NULL), no?  What
> this patch does, i.e. prepare an on-stack refspec_str[] array that
> is initialized from a constant string, and have .src member point at
> it, would not make .src freeable.  In other words, .src pointing at
> an allocated piece of string "some of the time" alone is not a good
> justification to make it a non-const pointer, I would think.

That's fair enough. I'm trying to work around a broken interface, but do
not solve the underlying issue.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-31 13:00         ` Patrick Steinhardt
@ 2024-05-31 13:33           ` Patrick Steinhardt
  2024-05-31 15:27             ` Junio C Hamano
  2024-05-31 15:27           ` Junio C Hamano
  1 sibling, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-05-31 13:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 3945 bytes --]

On Fri, May 31, 2024 at 03:00:36PM +0200, Patrick Steinhardt wrote:
> On Thu, May 30, 2024 at 12:38:45PM -0700, Junio C Hamano wrote:
> > Patrick Steinhardt <ps@pks.im> writes:
> > 
> > > Well, we do. Not in `get_fetch_map()`, but in `query_refspecs()`. It
> > > does weird stuff where it writes the result into either `src` or `dst`
> > > depending on which of these fields is provided by the caller. Which
> > > means that one of the fields would typically be a constant, whereas the
> > > other one will be allocated.
> > 
> > Yes, <src, dst> is used as a pair of <key, value> to query one with
> > the other (i.e. "where does this one go?" "where does this one come
> > from?").
> > 
> > But we are not talking about const-ness of the member (iow, once you
> > point a string with the member, you cannot repoint the pointer to
> > another string), but we are talking about const-ness of the string
> > that is pointed by the member (iow, not "char const *src" but "const
> > char *src"), no?  If I ask "I have this src, where does it go?" with
> > a refspec element filled with src, the dst member may need to be
> > updated to point at the string that is the answer of the query, but
> > that still can be done with "const char *src, *dst", can't it?  That
> > was what I was wondering.
> 
> Well, yes, the pointers can be updated. But that doesn't solve the
> underlying problem that the interface is not well-prepared to track
> ownership of its inputs and outputs.
> 
> The root problem is that `struct refspec_item` is being abused for
> multiple purposes. One of these purposes it to store parsed refspec
> items. Another purpose is to actually serve as a query. The requirements
> for those two purposes regarding the lifetime of associated strings is
> different:
> 
>   - When using it for storage purposes the strings should be owned by
>     the structure itself. Consequently, the lifetime should be managed
>     by the structure too, namely via `refspec_item_clear()`.
> 
>   - When using it for parsing purposes the input string is owned by the
>     caller, whereas the output string is owned by the callee and then
>     handed over to the caller. Here we cannot rely on the structure
>     itself to manage the lifetimes of both strings, at least not in the
>     current form.
> 
> Now of course, we could amend `struct refspec_item` to grow two
> additional fields `src_to_free` and `dst_to_free`. But that only doubles
> down on this misdesigned interface.
> 
> The proper solution in my opinion is to detangle those two usecases and
> split them out into two independent interfaces.
> 
> > And again you are conflating "allocated" with "read-write" here.  It
> > is often convenient if a variable that points at an allocated string
> > is of type "char *" and not "const char *", because you do not cast
> > it when calling free().  But if you want to make a structure member
> > or a variable that holds an allocated string responsible for
> > _owning_ the piece of memory, then you need to consistently have the
> > member point at an allocated piece of memory (or NULL), no?  What
> > this patch does, i.e. prepare an on-stack refspec_str[] array that
> > is initialized from a constant string, and have .src member point at
> > it, would not make .src freeable.  In other words, .src pointing at
> > an allocated piece of string "some of the time" alone is not a good
> > justification to make it a non-const pointer, I would think.
> 
> That's fair enough. I'm trying to work around a broken interface, but do
> not solve the underlying issue.
> 
> Patrick

In any case, I have now invested some more time into the individual
sites where I was converting to use on-stack strings. In many of the
cases we were indeed able to avoid the issue altogether without too much
of a hassle. The end result is more pleasing indeed, I'd say.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-31 13:00         ` Patrick Steinhardt
  2024-05-31 13:33           ` Patrick Steinhardt
@ 2024-05-31 15:27           ` Junio C Hamano
  2024-06-05 10:46             ` Jeff King
  1 sibling, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-05-31 15:27 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> On Thu, May 30, 2024 at 12:38:45PM -0700, Junio C Hamano wrote:
>> Patrick Steinhardt <ps@pks.im> writes:
>> 
>> > Well, we do. Not in `get_fetch_map()`, but in `query_refspecs()`. It
>> > does weird stuff where it writes the result into either `src` or `dst`
>> > depending on which of these fields is provided by the caller. Which
>> > means that one of the fields would typically be a constant, whereas the
>> > other one will be allocated.
> ...
> Well, yes, the pointers can be updated. But that doesn't solve the
> underlying problem that the interface is not well-prepared to track
> ownership of its inputs and outputs.

Oh, absolutely.  That is exactly why I wondered why .src and .dst
need to be "char *" and not "const char *".  If the piece of memory
these members point at are never modified via these pointers, marking
them as "const char *" would help the compilers help us by catching
when we try to break the promise.

Sometimes, the "constness of a pointer should help compilers help us
avoid modifying a piece of memory we promised not to modify through
the pointer" contradicts with the idea of (ab)using the constness to
signal ownership, i.e., "const pointers point at something owned by
somebody else, but via non-const pointers you always own the memory".

I wonder if we can do something to separate these two concerns
apart, using a trick similar to what we often use with an extra
variable "to_free".  Doing so would bloat the refspec_item, but
unlike the references themselves, there won't be thousands of them,
so it may not be an issue, perhaps?

>> And again you are conflating "allocated" with "read-write" here.  It
>> is often convenient if a variable that points at an allocated string
>> is of type "char *" and not "const char *", because you do not cast
>> it when calling free().  But if you want to make a structure member
>> or a variable that holds an allocated string responsible for
>> _owning_ the piece of memory, then you need to consistently have the
>> member point at an allocated piece of memory (or NULL), no?  What
>> this patch does, i.e. prepare an on-stack refspec_str[] array that
>> is initialized from a constant string, and have .src member point at
>> it, would not make .src freeable.  In other words, .src pointing at
>> an allocated piece of string "some of the time" alone is not a good
>> justification to make it a non-const pointer, I would think.
>
> That's fair enough. I'm trying to work around a broken interface, but do
> not solve the underlying issue.



^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-31 13:33           ` Patrick Steinhardt
@ 2024-05-31 15:27             ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-05-31 15:27 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> In any case, I have now invested some more time into the individual
> sites where I was converting to use on-stack strings. In many of the
> cases we were indeed able to avoid the issue altogether without too much
> of a hassle. The end result is more pleasing indeed, I'd say.

Nice to hear that.
Thanks.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v3 00/27] Compile with `-Wwrite-strings`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (20 preceding siblings ...)
  2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
@ 2024-06-03  9:38 ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
                     ` (27 more replies)
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                   ` (2 subsequent siblings)
  24 siblings, 28 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 39934 bytes --]

Hi,

this is the third version of my patch series that prepares the Git
codebase to compile with `-Wwrite-strings`. This compiler warning
changes the type of string constants from `char []` to `const char []`,
which helps to identify cases where we may accidentally write to or free
such constants.

Changes compared to v2:

  - I have split up the second patch into multiple patches. This was
    done so that we can have a deeper look at the respective sites that
    need adjustment.

  - I dropped the local string arrays that I used in v2 and instead now
    use string constants with casts in some places where we expect that
    those should never be written to. This has the benefit that we would
    segfault in case that expectation is violated, instead of silently
    allowing a write to those local arrays.

  - I adapted multiple commit messages to not only mention freeing of
    string constants, but also that writing to string constants is
    illegal.

Due to the split-up patch the range-diff got quite messy and was barely
readable anymore. I thus included an interdiff instead, which should be
way easier to read.

I realize that this patch series has grown quite large. Please let me
know in case I shall split it up into multiple patch series.

Patrick

Patrick Steinhardt (27):
  global: improve const correctness when assigning string constants
  global: convert intentionally-leaking config strings to consts
  refs/reftable: stop micro-optimizing refname allocations on copy
  reftable: cast away constness when assigning constants to records
  refspec: remove global tag refspec structure
  builtin/remote: cast away constness in `get_head_names()`
  diff: cast string constant in `fill_textconv()`
  line-log: stop assigning string constant to file parent buffer
  line-log: always allocate the output prefix
  entry: refactor how we remove items for delayed checkouts
  ident: add casts for fallback name and GECOS
  object-file: mark cached object buffers as const
  object-file: make `buf` parameter of `index_mem()` a constant
  pretty: add casts for decoration option pointers
  compat/win32: fix const-correctness with string constants
  http: do not assign string constant to non-const field
  parse-options: cast long name for OPTION_ALIAS
  send-pack: always allocate receive status
  remote-curl: avoid assigning string constant to non-const variable
  revision: always store allocated strings in output encoding
  mailmap: always store allocated strings in mailmap blob
  imap-send: drop global `imap_server_conf` variable
  imap-send: fix leaking memory in `imap_server_conf`
  builtin/rebase: do not assign default backend to non-constant field
  builtin/rebase: always store allocated string in `options.strategy`
  builtin/merge: always store allocated strings in `pull_twohead`
  config.mak.dev: enable `-Wwrite-strings` warning

 builtin/bisect.c             |   3 +-
 builtin/blame.c              |   2 +-
 builtin/bugreport.c          |   2 +-
 builtin/check-ignore.c       |   4 +-
 builtin/clone.c              |  14 ++--
 builtin/commit.c             |   6 +-
 builtin/diagnose.c           |   2 +-
 builtin/fetch.c              |  11 ++-
 builtin/log.c                |   2 +-
 builtin/mailsplit.c          |   4 +-
 builtin/merge.c              |  18 +++--
 builtin/pull.c               |  52 +++++++-------
 builtin/rebase.c             |  81 ++++++++++++----------
 builtin/receive-pack.c       |   4 +-
 builtin/remote.c             |  12 ++--
 builtin/revert.c             |   2 +-
 builtin/send-pack.c          |   2 +
 compat/basename.c            |  18 ++++-
 compat/mingw.c               |  28 ++++----
 compat/regex/regcomp.c       |   2 +-
 compat/winansi.c             |   2 +-
 config.mak.dev               |   1 +
 diff.c                       |   6 +-
 diffcore-rename.c            |   6 +-
 entry.c                      |  14 ++--
 fmt-merge-msg.c              |   2 +-
 fsck.c                       |   2 +-
 fsck.h                       |   2 +-
 gpg-interface.c              |   6 +-
 http-backend.c               |   2 +-
 http.c                       |   5 +-
 ident.c                      |   4 +-
 imap-send.c                  | 130 ++++++++++++++++++++---------------
 line-log.c                   |  22 +++---
 mailmap.c                    |   2 +-
 merge-ll.c                   |  11 ++-
 object-file.c                |  23 ++++---
 parse-options.h              |   2 +-
 pretty.c                     |   6 +-
 refs.c                       |   2 +-
 refs.h                       |   2 +-
 refs/reftable-backend.c      |  28 ++++----
 refspec.c                    |  13 ----
 refspec.h                    |   1 -
 reftable/basics.c            |  15 ++--
 reftable/basics.h            |   4 +-
 reftable/basics_test.c       |   4 +-
 reftable/block_test.c        |   2 +-
 reftable/merged_test.c       |  44 ++++++------
 reftable/readwrite_test.c    |  32 ++++-----
 reftable/record.c            |   6 +-
 reftable/stack.c             |  10 +--
 reftable/stack_test.c        |  56 +++++++--------
 remote-curl.c                |  53 +++++++-------
 revision.c                   |   3 +-
 run-command.c                |   2 +-
 send-pack.c                  |   2 +-
 t/helper/test-hashmap.c      |   3 +-
 t/helper/test-json-writer.c  |  10 +--
 t/helper/test-regex.c        |   4 +-
 t/helper/test-rot13-filter.c |   5 +-
 t/t3900-i18n-commit.sh       |   1 +
 t/t3901-i18n-patch.sh        |   1 +
 t/unit-tests/t-strbuf.c      |  10 +--
 trailer.c                    |   2 +-
 userdiff.c                   |  10 +--
 userdiff.h                   |  12 ++--
 wt-status.c                  |   2 +-
 68 files changed, 470 insertions(+), 386 deletions(-)

Interdiff against v2:
diff --git a/builtin/remote.c b/builtin/remote.c
index 0324a5d48d..b44f580b8c 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -493,13 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 {
 	struct ref *ref, *matches;
 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
-	struct refspec_item refspec;
-	char refspec_str[] = "refs/heads/*";
+	struct refspec_item refspec = {
+		.force = 0,
+		.pattern = 1,
+		.src = (char *) "refs/heads/*",
+		.dst = (char *) "refs/heads/*",
+	};
 
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.force = 0;
-	refspec.pattern = 1;
-	refspec.src = refspec.dst = refspec_str;
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 				    fetch_map, 1);
@@ -508,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 
 	free_refs(fetch_map);
 	free_refs(matches);
-
 	return 0;
 }
 
diff --git a/compat/basename.c b/compat/basename.c
index c3c9d65fac..5aa320306b 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -1,11 +1,6 @@
 #include "../git-compat-util.h"
 #include "../strbuf.h"
 
-/*
- * Both basename(3P) and dirname(3P) are mis-specified because they return a
- * non-constant pointer even though it is specified that they may return a
- * pointer to internal memory. This variable here is a result of that.
- */
 static char current_directory[] = ".";
 
 /* Adapted from libiberty's basename.c.  */
@@ -17,7 +12,13 @@ char *gitbasename (char *path)
 		skip_dos_drive_prefix(&path);
 
 	if (!path || !*path)
-		return current_directory;
+		/*
+		 * basename(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) "";
 
 	for (base = path; *path; path++) {
 		if (!is_dir_sep(*path))
@@ -40,12 +41,14 @@ char *gitdirname(char *path)
 	char *p = path, *slash = NULL, c;
 	int dos_drive_prefix;
 
-	/*
-	 * Same here, dirname(3P) is broken because it returns a non-constant
-	 * pointer that may point to internal memory.
-	 */
 	if (!p)
-		return current_directory;
+		/*
+		 * dirname(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) "";
 
 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
 		goto dot;
diff --git a/compat/mingw.c b/compat/mingw.c
index 60f0986f76..d378cd04cb 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2257,7 +2257,6 @@ struct passwd *getpwuid(int uid)
 {
 	static unsigned initialized;
 	static char user_name[100];
-	static char unknown[] = "unknown";
 	static struct passwd *p;
 	wchar_t buf[100];
 	DWORD len;
@@ -2280,7 +2279,11 @@ struct passwd *getpwuid(int uid)
 	p->pw_name = user_name;
 	p->pw_gecos = get_extended_user_info(NameDisplay);
 	if (!p->pw_gecos)
-		p->pw_gecos = unknown;
+		/*
+		 * Data returned by getpwuid(3P) is treated as internal and
+		 * must never be written to or freed.
+		 */
+		p->pw_gecos = (char *) "unknown";
 	p->pw_dir = NULL;
 
 	initialized = 1;
diff --git a/diff.c b/diff.c
index 1439a5a01d..cecda216cf 100644
--- a/diff.c
+++ b/diff.c
@@ -7231,12 +7231,11 @@ size_t fill_textconv(struct repository *r,
 		     struct diff_filespec *df,
 		     char **outbuf)
 {
-	static char empty_str[] = "";
 	size_t size;
 
 	if (!driver) {
 		if (!DIFF_FILE_VALID(df)) {
-			*outbuf = empty_str;
+			*outbuf = (char *) "";
 			return 0;
 		}
 		if (diff_populate_filespec(r, df, NULL))
diff --git a/entry.c b/entry.c
index 2fc06ac90c..f291d8eee6 100644
--- a/entry.c
+++ b/entry.c
@@ -167,6 +167,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
 	return !available;
 }
 
+static int string_is_not_null(struct string_list_item *item, void *data UNUSED)
+{
+	return !!item->string;
+}
+
 int finish_delayed_checkout(struct checkout *state, int show_progress)
 {
 	int errs = 0;
@@ -175,7 +180,6 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 	struct string_list_item *filter, *path;
 	struct progress *progress = NULL;
 	struct delayed_checkout *dco = state->delayed_checkout;
-	char empty_str[] = "";
 
 	if (!state->delayed_checkout)
 		return errs;
@@ -190,7 +194,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 			if (!async_query_available_blobs(filter->string, &available_paths)) {
 				/* Filter reported an error */
 				errs = 1;
-				filter->string = empty_str;
+				filter->string = NULL;
 				continue;
 			}
 			if (available_paths.nr <= 0) {
@@ -200,7 +204,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 				 * filter from the list (see
 				 * "string_list_remove_empty_items" call below).
 				 */
-				filter->string = empty_str;
+				filter->string = NULL;
 				continue;
 			}
 
@@ -226,7 +230,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					 * Do not ask the filter for available blobs,
 					 * again, as the filter is likely buggy.
 					 */
-					filter->string = empty_str;
+					filter->string = NULL;
 					continue;
 				}
 				ce = index_file_exists(state->istate, path->string,
@@ -240,7 +244,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					errs = 1;
 			}
 		}
-		string_list_remove_empty_items(&dco->filters, 0);
+
+		filter_string_list(&dco->filters, 0, string_is_not_null, NULL);
 	}
 	stop_progress(&progress);
 	string_list_clear(&dco->filters, 0);
diff --git a/ident.c b/ident.c
index df7aa42802..caf41fb2a9 100644
--- a/ident.c
+++ b/ident.c
@@ -46,14 +46,9 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
 	pw = getpwuid(getuid());
 	if (!pw) {
 		static struct passwd fallback;
-		static char fallback_name[] = "unknown";
+		fallback.pw_name = (char *) "unknown";
 #ifndef NO_GECOS_IN_PWENT
-		static char fallback_gcos[] = "Unknown";
-#endif
-
-		fallback.pw_name = fallback_name;
-#ifndef NO_GECOS_IN_PWENT
-		fallback.pw_gecos = fallback_gcos;
+		fallback.pw_gecos = (char *) "Unknown";
 #endif
 		pw = &fallback;
 		if (is_bogus)
diff --git a/line-log.c b/line-log.c
index 9a298209d0..67c80b39a0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1036,7 +1036,7 @@ static int process_diff_filepair(struct rev_info *rev,
 	struct range_set tmp;
 	struct diff_ranges diff;
 	mmfile_t file_parent, file_target;
-	char empty_str[] = "";
+	char *parent_data_to_free = NULL;
 
 	assert(pair->two->path);
 	while (rg) {
@@ -1061,7 +1061,7 @@ static int process_diff_filepair(struct rev_info *rev,
 		file_parent.ptr = pair->one->data;
 		file_parent.size = pair->one->size;
 	} else {
-		file_parent.ptr = empty_str;
+		file_parent.ptr = parent_data_to_free = xstrdup("");
 		file_parent.size = 0;
 	}
 
@@ -1080,6 +1080,7 @@ static int process_diff_filepair(struct rev_info *rev,
 
 	diff_ranges_release(&diff);
 
+	free(parent_data_to_free);
 	return ((*diff_out)->parent.nr > 0);
 }
 
diff --git a/object-file.c b/object-file.c
index 46ea00ac46..b5b5a59dc6 100644
--- a/object-file.c
+++ b/object-file.c
@@ -277,18 +277,17 @@ int hash_algo_by_length(int len)
 static struct cached_object {
 	struct object_id oid;
 	enum object_type type;
-	void *buf;
+	const void *buf;
 	unsigned long size;
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
 
-static char empty_tree_buf[] = "";
 static struct cached_object empty_tree = {
 	.oid = {
 		.hash = EMPTY_TREE_SHA1_BIN_LITERAL,
 	},
 	.type = OBJ_TREE,
-	.buf = empty_tree_buf,
+	.buf = "",
 };
 
 static struct cached_object *find_cached_object(const struct object_id *oid)
@@ -1779,6 +1778,10 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 			struct object_id *oid)
 {
 	struct cached_object *co;
+	char *co_buf;
+
+	co_buf = xmalloc(len);
+	memcpy(co_buf, buf, len);
 
 	hash_object_file(the_hash_algo, buf, len, type, oid);
 	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
@@ -1788,8 +1791,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 	co = &cached_objects[cached_object_nr++];
 	co->size = len;
 	co->type = type;
-	co->buf = xmalloc(len);
-	memcpy(co->buf, buf, len);
+	co->buf = co_buf;
 	oidcpy(&co->oid, oid);
 	return 0;
 }
diff --git a/pretty.c b/pretty.c
index 1a0030b32a..1df9d635fb 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1583,10 +1583,9 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 		return 1;
 	case 'D':
 		{
-			char empty_str[] = "";
 			const struct decoration_options opts = {
-				.prefix = empty_str,
-				.suffix = empty_str,
+				.prefix = (char *) "",
+				.suffix = (char *) "",
 			};
 
 			format_decorations(sb, commit, c->auto_color, &opts);
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1908e74dea..e77faa2b9d 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1285,7 +1285,6 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	struct strbuf errbuf = STRBUF_INIT;
 	size_t logs_nr = 0, logs_alloc = 0, i;
 	const char *committer_info;
-	char head[] = "HEAD";
 	int ret;
 
 	committer_info = git_committer_info(0);
@@ -1341,10 +1340,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	 * old reference.
 	 */
 	refs[0] = old_ref;
-	refs[0].refname = (char *)arg->newname;
+	refs[0].refname = xstrdup(arg->newname);
 	refs[0].update_index = creation_ts;
 	if (arg->delete_old) {
-		refs[1].refname = (char *)arg->oldname;
+		refs[1].refname = xstrdup(arg->oldname);
 		refs[1].value_type = REFTABLE_REF_DELETION;
 		refs[1].update_index = deletion_ts;
 	}
@@ -1367,7 +1366,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 		fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs[logs_nr].update_index = deletion_ts;
 		logs[logs_nr].value.update.message =
 			xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1388,7 +1387,13 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (append_head_reflog) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			logs[logs_nr] = logs[logs_nr - 1];
-			logs[logs_nr].refname = head;
+			logs[logs_nr].refname = xstrdup("HEAD");
+			logs[logs_nr].value.update.name =
+				xstrdup(logs[logs_nr].value.update.name);
+			logs[logs_nr].value.update.email =
+				xstrdup(logs[logs_nr].value.update.email);
+			logs[logs_nr].value.update.message =
+				xstrdup(logs[logs_nr].value.update.message);
 			logs_nr++;
 		}
 	}
@@ -1399,7 +1404,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 	memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 	fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-	logs[logs_nr].refname = (char *)arg->newname;
+	logs[logs_nr].refname = xstrdup(arg->newname);
 	logs[logs_nr].update_index = creation_ts;
 	logs[logs_nr].value.update.message =
 		xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1431,7 +1436,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		 */
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		logs[logs_nr] = old_log;
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs_nr++;
 
 		/*
@@ -1440,7 +1445,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (arg->delete_old) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
-			logs[logs_nr].refname = (char *)arg->oldname;
+			logs[logs_nr].refname = xstrdup(arg->oldname);
 			logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
 			logs[logs_nr].update_index = old_log.update_index;
 			logs_nr++;
@@ -1463,13 +1468,11 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	reftable_iterator_destroy(&it);
 	string_list_clear(&skip, 0);
 	strbuf_release(&errbuf);
-	for (i = 0; i < logs_nr; i++) {
-		if (logs[i].refname == head)
-			continue;
-		logs[i].refname = NULL;
+	for (i = 0; i < logs_nr; i++)
 		reftable_log_record_release(&logs[i]);
-	}
 	free(logs);
+	for (i = 0; i < ARRAY_SIZE(refs); i++)
+		reftable_ref_record_release(&refs[i]);
 	reftable_ref_record_release(&old_ref);
 	reftable_log_record_release(&old_log);
 	return ret;
diff --git a/reftable/basics.c b/reftable/basics.c
index fea711db7e..0058619ca6 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -67,9 +67,9 @@ void free_names(char **a)
 	reftable_free(a);
 }
 
-size_t names_length(char **names)
+size_t names_length(const char **names)
 {
-	char **p = names;
+	const char **p = names;
 	while (*p)
 		p++;
 	return p - names;
@@ -102,15 +102,12 @@ void parse_names(char *buf, int size, char ***namesp)
 	*namesp = names;
 }
 
-int names_equal(char **a, char **b)
+int names_equal(const char **a, const char **b)
 {
-	int i = 0;
-	for (; a[i] && b[i]; i++) {
-		if (strcmp(a[i], b[i])) {
+	size_t i = 0;
+	for (; a[i] && b[i]; i++)
+		if (strcmp(a[i], b[i]))
 			return 0;
-		}
-	}
-
 	return a[i] == b[i];
 }
 
diff --git a/reftable/basics.h b/reftable/basics.h
index 523ecd5307..c8fec68d4e 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -42,10 +42,10 @@ void free_names(char **a);
 void parse_names(char *buf, int size, char ***namesp);
 
 /* compares two NULL-terminated arrays of strings. */
-int names_equal(char **a, char **b);
+int names_equal(const char **a, const char **b);
 
 /* returns the array size of a NULL-terminated array of strings. */
-size_t names_length(char **names);
+size_t names_length(const char **names);
 
 /* Allocation routines; they invoke the functions set through
  * reftable_set_alloc() */
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 23fab22eb1..13bc761817 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -58,8 +58,7 @@ static void test_binsearch(void)
 
 static void test_names_length(void)
 {
-	char a[] = "a", b[] = "b";
-	char *names[] = { a, b, NULL };
+	const char *names[] = { "a", "b", NULL };
 	EXPECT(names_length(names) == 2);
 }
 
diff --git a/reftable/block_test.c b/reftable/block_test.c
index d5967b214d..90aecd5a7c 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -19,7 +19,7 @@ license that can be found in the LICENSE file or at
 static void test_block_read_write(void)
 {
 	const int header_off = 21; /* random */
-	char *names[30], empty_str[] = "";
+	char *names[30];
 	const int N = ARRAY_SIZE(names);
 	const int block_size = 1024;
 	struct reftable_block block = { NULL };
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
 			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
-	rec.u.ref.refname = empty_str;
+	rec.u.ref.refname = (char *) "";
 	rec.u.ref.value_type = REFTABLE_REF_DELETION;
 	n = block_writer_add(&bw, &rec);
 	EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index fd5a065e42..6d1159d12d 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -123,15 +123,14 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 
 static void test_merged_between(void)
 {
-	char a[] = "a", b[] = "b";
 	struct reftable_ref_record r1[] = { {
-		.refname = b,
+		.refname = (char *) "b",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
-		.refname = a,
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
@@ -164,41 +163,40 @@ static void test_merged_between(void)
 
 static void test_merged(void)
 {
-	char a[] = "a", b[] = "b", c[] = "c", d[] = "d";
 	struct reftable_ref_record r1[] = {
 		{
-			.refname = a,
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = b,
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = c,
+			.refname = (char *) "c",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
-		.refname = a,
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
 	struct reftable_ref_record r3[] = {
 		{
-			.refname = c,
+			.refname = (char *) "c",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 2 },
 		},
 		{
-			.refname = d,
+			.refname = (char *) "d",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
@@ -291,52 +289,48 @@ merged_table_from_log_records(struct reftable_log_record **logs,
 
 static void test_merged_logs(void)
 {
-	char a[] = "a";
-	char name[] = "jane doe", email[] = "jane@invalid";
-	char message1[] = "message1", message2[] = "message2";
-	char message3[] = "message3";
 	struct reftable_log_record r1[] = {
 		{
-			.refname = a,
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 2 },
 				/* deletion */
-				.name = name,
-				.email = email,
-				.message = message2,
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message2",
 			}
 		},
 		{
-			.refname = a,
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = name,
-				.email = email,
-				.message = message1,
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message1",
 			}
 		},
 	};
 	struct reftable_log_record r2[] = {
 		{
-			.refname = a,
+			.refname = (char *) "a",
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.new_hash = { 3 },
-				.name = name,
-				.email = email,
-				.message = message3,
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message3",
 			}
 		},
 	};
 	struct reftable_log_record r3[] = {
 		{
-			.refname = a,
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_DELETION,
 		},
@@ -406,13 +400,13 @@ static void test_merged_logs(void)
 
 static void test_default_write_opts(void)
 {
-	char master[] = "master";
 	struct reftable_write_options opts = { 0 };
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
+
 	struct reftable_ref_record rec = {
-		.refname = master,
+		.refname = (char *) "master",
 		.update_index = 1,
 	};
 	int err;
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index 064d693111..c55019232b 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -56,7 +56,6 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 	int i = 0, n;
 	struct reftable_log_record log = { NULL };
 	const struct reftable_stats *stats = NULL;
-	char message[] = "message";
 
 	REFTABLE_CALLOC_ARRAY(*names, N + 1);
 
@@ -87,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
 		set_test_hash(log.value.update.new_hash, i);
-		log.value.update.message = message;
+		log.value.update.message = (char *) "message";
 
 		n = reftable_writer_add_log(w, &log);
 		EXPECT(n == 0);
@@ -112,28 +111,23 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 
 static void test_log_buffer_size(void)
 {
-	char refname[] = "refs/heads/master";
-	char name[] = "Han-Wen Hienhuys";
-	char email[] = "hanwen@google.com";
-	char message[] = "commit: 9\n";
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_write_options opts = {
 		.block_size = 4096,
 	};
 	int err;
 	int i;
-	struct reftable_log_record log = {
-		.refname = refname,
-		.update_index = 0xa,
-		.value_type = REFTABLE_LOG_UPDATE,
-		.value.update = {
-			.name = name,
-			.email = email,
-			.tz_offset = 100,
-			.time = 0x5e430672,
-			.message = message,
-		},
-	};
+	struct reftable_log_record
+		log = { .refname = (char *) "refs/heads/master",
+			.update_index = 0xa,
+			.value_type = REFTABLE_LOG_UPDATE,
+			.value = { .update = {
+					   .name = (char *) "Han-Wen Nienhuys",
+					   .email = (char *) "hanwen@google.com",
+					   .tz_offset = 100,
+					   .time = 0x5e430672,
+					   .message = (char *) "commit: 9\n",
+				   } } };
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
@@ -155,9 +149,6 @@ static void test_log_buffer_size(void)
 
 static void test_log_overflow(void)
 {
-	char refname[] = "refs/heads/master";
-	char name[] = "Han-Wen Hienhuys";
-	char email[] = "hanwen@google.com";
 	struct strbuf buf = STRBUF_INIT;
 	char msg[256] = { 0 };
 	struct reftable_write_options opts = {
@@ -165,15 +156,15 @@ static void test_log_overflow(void)
 	};
 	int err;
 	struct reftable_log_record log = {
-		.refname = refname,
+		.refname = (char *) "refs/heads/master",
 		.update_index = 0xa,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = name,
-				.email = email,
+				.name = (char *) "Han-Wen Nienhuys",
+				.email = (char *) "hanwen@google.com",
 				.tz_offset = 100,
 				.time = 0x5e430672,
 				.message = msg,
@@ -299,20 +290,17 @@ static void test_log_zlib_corruption(void)
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	const struct reftable_stats *stats = NULL;
-	char refname[] = "refname";
-	char name[] = "My Name";
-	char email[] = "myname@invalid";
 	char message[100] = { 0 };
 	int err, i, n;
 	struct reftable_log_record log = {
-		.refname = refname,
+		.refname = (char *) "refname",
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.new_hash = { 1 },
 				.old_hash = { 2 },
-				.name = name,
-				.email = email,
+				.name = (char *) "My Name",
+				.email = (char *) "myname@invalid",
 				.message = message,
 			},
 		},
@@ -739,9 +727,8 @@ static void test_write_empty_key(void)
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-	char refname[] = "";
 	struct reftable_ref_record ref = {
-		.refname = refname,
+		.refname = (char *) "",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_DELETION,
 	};
@@ -763,21 +750,20 @@ static void test_write_key_order(void)
 	struct strbuf buf = STRBUF_INIT;
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
-	char a[] = "a", b[] = "b", target[] = "target";
 	struct reftable_ref_record refs[2] = {
 		{
-			.refname = b,
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = target,
+				.symref = (char *) "target",
 			},
 		}, {
-			.refname = a,
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = target,
+				.symref = (char *) "target",
 			},
 		}
 	};
diff --git a/reftable/stack.c b/reftable/stack.c
index a59ebe038d..09549c51c9 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -204,7 +204,8 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
 	return cur;
 }
 
-static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
+static int reftable_stack_reload_once(struct reftable_stack *st,
+				      const char **names,
 				      int reuse_open)
 {
 	size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
@@ -222,7 +223,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
 	while (*names) {
 		struct reftable_reader *rd = NULL;
-		char *name = *names++;
+		const char *name = *names++;
 
 		/* this is linear; we assume compaction keeps the number of
 		   tables under control so this is not quadratic. */
@@ -354,7 +355,7 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 				goto out;
 		}
 
-		err = reftable_stack_reload_once(st, names, reuse_open);
+		err = reftable_stack_reload_once(st, (const char **) names, reuse_open);
 		if (!err)
 			break;
 		if (err != REFTABLE_NOT_EXIST_ERROR)
@@ -368,7 +369,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 		err = read_lines(st->list_file, &names_after);
 		if (err < 0)
 			goto out;
-		if (names_equal(names_after, names)) {
+		if (names_equal((const char **) names_after,
+				(const char **) names)) {
 			err = REFTABLE_NOT_EXIST_ERROR;
 			goto out;
 		}
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index c6d88e6ea8..4abf92636d 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -116,14 +116,13 @@ static void test_parse_names(void)
 
 static void test_names_equal(void)
 {
-	char a[] = "a", b[] = "b", c[] = "c", d[] = "d";
-	char *abc[] = { a, b, c, NULL };
-	char *abd[] = { a, b, d, NULL };
-	char *ab[] = { a, b, NULL };
-
-	EXPECT(names_equal(abc, abc));
-	EXPECT(!names_equal(abc, abd));
-	EXPECT(!names_equal(abc, ab));
+	const char *a[] = { "a", "b", "c", NULL };
+	const char *b[] = { "a", "b", "d", NULL };
+	const char *c[] = { "a", "b", NULL };
+
+	EXPECT(names_equal(a, a));
+	EXPECT(!names_equal(a, b));
+	EXPECT(!names_equal(a, c));
 }
 
 static int write_test_ref(struct reftable_writer *wr, void *arg)
@@ -156,12 +155,11 @@ static void test_reftable_stack_add_one(void)
 	};
 	struct reftable_stack *st = NULL;
 	int err;
-	char head[] = "HEAD", master[] = "master";
 	struct reftable_ref_record ref = {
-		.refname = head,
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = master,
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 	struct stat stat_result = { 0 };
@@ -217,18 +215,17 @@ static void test_reftable_stack_uptodate(void)
 	char *dir = get_tmp_dir(__LINE__);
 
 	int err;
-	char head[] = "HEAD", branch2[] = "branch2", master[] = "master";
 	struct reftable_ref_record ref1 = {
-		.refname = head,
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = master,
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = branch2,
+		.refname = (char *) "branch2",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = master,
+		.value.symref = (char *) "master",
 	};
 
 
@@ -265,12 +262,12 @@ static void test_reftable_stack_transaction_api(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_addition *add = NULL;
-	char head[] = "HEAD", master[] = "master";
+
 	struct reftable_ref_record ref = {
-		.refname = head,
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = master,
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 
@@ -313,11 +310,10 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i <= n; i++) {
-		char master[] = "master";
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = master,
+			.value.symref = (char *) "master",
 		};
 		char name[100];
 
@@ -359,9 +355,8 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 
 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
 {
-	char master[] = "refs/meads/master";
 	struct reftable_ref_record ref = {
-		.refname = master,
+		.refname = (char *) "refs/heads/master",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = {0x01},
@@ -409,21 +404,21 @@ static int write_error(struct reftable_writer *wr, void *arg)
 static void test_reftable_stack_update_index_check(void)
 {
 	char *dir = get_tmp_dir(__LINE__);
+
 	struct reftable_write_options cfg = { 0 };
 	struct reftable_stack *st = NULL;
 	int err;
-	char name1[] = "name1", name2[] = "name2", master[] = "master";
 	struct reftable_ref_record ref1 = {
-		.refname = name1,
+		.refname = (char *) "name1",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = master,
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = name2,
+		.refname = (char *) "name2",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = master,
+		.value.symref = (char *) "master",
 	};
 
 	err = reftable_new_stack(&st, dir, cfg);
@@ -565,12 +560,8 @@ static void test_reftable_stack_log_normalize(void)
 	};
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
-	char branch[] = "branch";
-	char onetwomessage[] = "one\ntwo";
-	char onemessage[] = "one";
-	char twomessage[] = "two\n";
 	struct reftable_log_record input = {
-		.refname = branch,
+		.refname = (char *) "branch",
 		.update_index = 1,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
@@ -591,11 +582,11 @@ static void test_reftable_stack_log_normalize(void)
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 
-	input.value.update.message = onetwomessage;
+	input.value.update.message = (char *) "one\ntwo";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT(err == REFTABLE_API_ERROR);
 
-	input.value.update.message = onemessage;
+	input.value.update.message = (char *) "one";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
 
@@ -603,7 +594,7 @@ static void test_reftable_stack_log_normalize(void)
 	EXPECT_ERR(err);
 	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
 
-	input.value.update.message = twomessage;
+	input.value.update.message = (char *) "two\n";
 	arg.update_index = 2;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
@@ -700,14 +691,15 @@ static void test_reftable_stack_tombstone(void)
 static void test_reftable_stack_hash_id(void)
 {
 	char *dir = get_tmp_dir(__LINE__);
+
 	struct reftable_write_options cfg = { 0 };
 	struct reftable_stack *st = NULL;
 	int err;
-	char master[] = "master", target[] = "target";
+
 	struct reftable_ref_record ref = {
-		.refname = master,
+		.refname = (char *) "master",
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = target,
+		.value.symref = (char *) "target",
 		.update_index = 1,
 	};
 	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
@@ -882,12 +874,12 @@ static void test_reftable_stack_auto_compaction(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i < N; i++) {
-		char name[100], master[] = "master";
+		char name[100];
 		struct reftable_ref_record ref = {
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = master,
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -918,11 +910,10 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i <= n; i++) {
-		char master[] = "master";
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = master,
+			.value.symref = (char *) "master",
 		};
 
 		/*
@@ -968,12 +959,12 @@ static void test_reftable_stack_compaction_concurrent(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i < N; i++) {
-		char name[100], master[] = "master";
+		char name[100];
 		struct reftable_ref_record ref = {
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = master,
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -1018,12 +1009,12 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
 	EXPECT_ERR(err);
 
 	for (i = 0; i < N; i++) {
-		char name[100], master[] = "master";
+		char name[100];
 		struct reftable_ref_record ref = {
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = master,
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 01/27] global: improve const correctness when assigning string constants
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 27645 bytes --]

We're about to enable `-Wwrite-strings`, which changes the type of
string constants to `const char[]`. Fix various sites where we assign
such constants to non-const variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/bisect.c             |  3 ++-
 builtin/blame.c              |  2 +-
 builtin/bugreport.c          |  2 +-
 builtin/check-ignore.c       |  4 +--
 builtin/clone.c              |  6 ++---
 builtin/commit.c             |  6 ++---
 builtin/diagnose.c           |  2 +-
 builtin/log.c                |  2 +-
 builtin/mailsplit.c          |  4 +--
 builtin/pull.c               | 52 ++++++++++++++++++------------------
 builtin/receive-pack.c       |  4 +--
 builtin/revert.c             |  2 +-
 compat/regex/regcomp.c       |  2 +-
 diff.c                       |  4 +--
 diffcore-rename.c            |  6 ++---
 fmt-merge-msg.c              |  2 +-
 fsck.c                       |  2 +-
 fsck.h                       |  2 +-
 gpg-interface.c              |  2 +-
 http-backend.c               |  2 +-
 imap-send.c                  |  6 ++---
 pretty.c                     |  2 +-
 refs.c                       |  2 +-
 refs.h                       |  2 +-
 reftable/basics.c            | 15 +++++------
 reftable/basics.h            |  4 +--
 reftable/basics_test.c       |  4 +--
 reftable/record.c            |  6 ++---
 reftable/stack.c             | 10 ++++---
 reftable/stack_test.c        |  8 +++---
 run-command.c                |  2 +-
 t/helper/test-hashmap.c      |  3 ++-
 t/helper/test-json-writer.c  | 10 +++----
 t/helper/test-regex.c        |  4 +--
 t/helper/test-rot13-filter.c |  5 ++--
 t/unit-tests/t-strbuf.c      | 10 ++++---
 trailer.c                    |  2 +-
 wt-status.c                  |  2 +-
 38 files changed, 106 insertions(+), 102 deletions(-)

diff --git a/builtin/bisect.c b/builtin/bisect.c
index a58432b9d9..dabce9b542 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
 	return bisect_clean_state();
 }
 
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+		       const char *fmt, const char *state,
 		       struct commit *commit)
 {
 	struct pretty_print_context pp = {0};
diff --git a/builtin/blame.c b/builtin/blame.c
index 838cd476be..98c7629b6a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
-	char *tmp, *endp;
+	const char *tmp, *endp;
 	const char *namebuf, *mailbuf;
 
 	tmp = strstr(inbuf, what);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 25f860a0d9..b3cc77af53 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode diagnose = DIAGNOSE_NONE;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	const char *user_relative_path = NULL;
 	char *prefixed_filename;
 	size_t output_path_len;
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 6c43430ec4..2bda6a1d46 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -35,8 +35,8 @@ static const struct option check_ignore_options[] = {
 
 static void output_pattern(const char *path, struct path_pattern *pattern)
 {
-	char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
-	char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+	const char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
+	const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
 	if (!nul_term_line) {
 		if (!verbose) {
 			write_name_quoted(path, stdout, '\n');
diff --git a/builtin/clone.c b/builtin/clone.c
index 23993b905b..92ab7d7165 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -71,7 +71,7 @@ static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
 static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
 static int option_sparse_checkout;
@@ -177,8 +177,8 @@ static struct option builtin_clone_options[] = {
 
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
-	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
-	static char *bundle_suffix[] = { ".bundle", "" };
+	static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static const char *bundle_suffix[] = { ".bundle", "" };
 	size_t baselen = path->len;
 	struct stat st;
 	int i;
diff --git a/builtin/commit.c b/builtin/commit.c
index f53e7e86ff..75c741173e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,7 +113,7 @@ static char *template_file;
  * the commit message and/or authorship.
  */
 static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
 static char *fixup_message, *fixup_commit, *squash_message;
 static const char *fixup_prefix;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +121,8 @@ static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
 static struct strvec trailer_args = STRVEC_INIT;
 
 /*
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2b55..4857a4395b 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode mode = DIAGNOSE_STATS;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	char *prefixed_filename;
 
 	const struct option diagnose_options[] = {
diff --git a/builtin/log.c b/builtin/log.c
index 78a247d8a9..b8846a9458 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1283,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 	o2->flags = flags2;
 }
 
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb8ae..fe6dbc5d05 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
 	DIR *dir;
 	struct dirent *dent;
 	char *name = NULL;
-	char *subs[] = { "cur", "new", NULL };
-	char **sub;
+	const char *subs[] = { "cur", "new", NULL };
+	const char **sub;
 	int ret = -1;
 
 	for (sub = subs; *sub; ++sub) {
diff --git a/builtin/pull.c b/builtin/pull.c
index d622202bce..2d0429f14f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
 
 /* Shared options */
 static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
 
 /* Options passed to git-merge or git-rebase */
 static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
 static int opt_autostash = -1;
 static int config_autostash;
 static int check_trust_level = 1;
 static struct strvec opt_strategies = STRVEC_INIT;
 static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
 static int opt_allow_unrelated_histories;
 
 /* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
 static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
 static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
 static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
 static struct strvec opt_fetch = STRVEC_INIT;
 
 static struct option pull_options[] = {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 01c1f04ece..c8d12ee0a7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1249,7 +1249,7 @@ static int run_proc_receive_hook(struct command *commands,
 	return code;
 }
 
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
 	N_("By default, updating the current branch in a non-bare repository\n"
 	   "is denied, because it will make the index and work tree inconsistent\n"
 	   "with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
 	rp_error("%s", _(refuse_unconfigured_deny_msg));
 }
 
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
 	N_("By default, deleting the current branch is denied, because the next\n"
 	   "'git clone' won't result in any file checked out, causing confusion.\n"
 	   "\n"
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2c68..7bf2b4e11d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 
 	/* Check for incompatible command line arguments */
 	if (cmd) {
-		char *this_operation;
+		const char *this_operation;
 		if (cmd == 'q')
 			this_operation = "--quit";
 		else if (cmd == 'c')
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 2bc0f1187a..6c5d455e92 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
   unsigned int table_size;
 #ifndef _LIBC
-  char *codeset_name;
+  const char *codeset_name;
 #endif
 
   memset (dfa, '\0', sizeof (re_dfa_t));
diff --git a/diff.c b/diff.c
index e70301df76..ffd867ef6c 100644
--- a/diff.c
+++ b/diff.c
@@ -3764,7 +3764,7 @@ static void builtin_diff(const char *name_a,
 	return;
 }
 
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 {
 	if (!is_renamed) {
 		if (p->status == DIFF_STATUS_ADDED) {
@@ -4076,7 +4076,7 @@ static int reuse_worktree_file(struct index_state *istate,
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
 	struct strbuf buf = STRBUF_INIT;
-	char *dirty = "";
+	const char *dirty = "";
 
 	/* Are we looking at the work tree? */
 	if (s->dirty_submodule)
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bcac7..0e1adb87df 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -406,7 +406,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
 	return highest_destination_dir;
 }
 
-static char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
 
 static int dir_rename_already_determinable(struct strintmap *counts)
 {
@@ -429,8 +429,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
 }
 
 static void increment_count(struct dir_rename_info *info,
-			    char *old_dir,
-			    char *new_dir)
+			    const char *old_dir,
+			    const char *new_dir)
 {
 	struct strintmap *counts;
 	struct strmap_entry *e;
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 7d144b803a..5af63ab5ab 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -447,7 +447,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
 				const char *current_branch)
 {
 	int i = 0;
-	char *sep = "";
+	const char *sep = "";
 
 	strbuf_addstr(out, "Merge ");
 	for (i = 0; i < srcs.nr; i++) {
diff --git a/fsck.c b/fsck.c
index 7dff41413e..61cd48aa25 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1231,7 +1231,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 }
 
 int fsck_buffer(const struct object_id *oid, enum object_type type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options)
 {
 	if (type == OBJ_BLOB)
diff --git a/fsck.h b/fsck.h
index 17fa2dda5d..4f0c4e6479 100644
--- a/fsck.h
+++ b/fsck.h
@@ -202,7 +202,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
  * struct.
  */
 int fsck_buffer(const struct object_id *oid, enum object_type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options);
 
 /*
diff --git a/gpg-interface.c b/gpg-interface.c
index 5193223714..71a9382a61 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -727,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
 			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
-	char *fmtname = NULL;
+	const char *fmtname = NULL;
 	char *trust;
 	int ret;
 
diff --git a/http-backend.c b/http-backend.c
index 5b65287ac9..5b4dca65ed 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -753,7 +753,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
 
 int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
-	char *method = getenv("REQUEST_METHOD");
+	const char *method = getenv("REQUEST_METHOD");
 	const char *proto_header;
 	char *dir;
 	struct service_cmd *cmd = NULL;
diff --git a/imap-send.c b/imap-send.c
index a5d1510180..8b723b34a5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1215,9 +1215,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
 static void wrap_in_html(struct strbuf *msg)
 {
 	struct strbuf buf = STRBUF_INIT;
-	static char *content_type = "Content-Type: text/html;\n";
-	static char *pre_open = "<pre>\n";
-	static char *pre_close = "</pre>\n";
+	static const char *content_type = "Content-Type: text/html;\n";
+	static const char *pre_open = "<pre>\n";
+	static const char *pre_close = "</pre>\n";
 	const char *body = strstr(msg->buf, "\n\n");
 
 	if (!body)
diff --git a/pretty.c b/pretty.c
index 22a81506b7..ec05db5655 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1325,7 +1325,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
 static size_t parse_describe_args(const char *start, struct strvec *args)
 {
 	struct {
-		char *name;
+		const char *name;
 		enum {
 			DESCRIBE_ARG_BOOL,
 			DESCRIBE_ARG_INTEGER,
diff --git a/refs.c b/refs.c
index 8260c27cde..292e8d947e 100644
--- a/refs.c
+++ b/refs.c
@@ -159,7 +159,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
 {
 	struct ref_namespace_info *info = &ref_namespace[namespace];
 	if (info->ref_updated)
-		free(info->ref);
+		free((char *)info->ref);
 	info->ref = ref;
 	info->ref_updated = 1;
 }
diff --git a/refs.h b/refs.h
index 34568ee1fb..923f751d18 100644
--- a/refs.h
+++ b/refs.h
@@ -975,7 +975,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
  */
 
 struct ref_namespace_info {
-	char *ref;
+	const char *ref;
 	enum decoration_type decoration;
 
 	/*
diff --git a/reftable/basics.c b/reftable/basics.c
index fea711db7e..0058619ca6 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -67,9 +67,9 @@ void free_names(char **a)
 	reftable_free(a);
 }
 
-size_t names_length(char **names)
+size_t names_length(const char **names)
 {
-	char **p = names;
+	const char **p = names;
 	while (*p)
 		p++;
 	return p - names;
@@ -102,15 +102,12 @@ void parse_names(char *buf, int size, char ***namesp)
 	*namesp = names;
 }
 
-int names_equal(char **a, char **b)
+int names_equal(const char **a, const char **b)
 {
-	int i = 0;
-	for (; a[i] && b[i]; i++) {
-		if (strcmp(a[i], b[i])) {
+	size_t i = 0;
+	for (; a[i] && b[i]; i++)
+		if (strcmp(a[i], b[i]))
 			return 0;
-		}
-	}
-
 	return a[i] == b[i];
 }
 
diff --git a/reftable/basics.h b/reftable/basics.h
index 523ecd5307..c8fec68d4e 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -42,10 +42,10 @@ void free_names(char **a);
 void parse_names(char *buf, int size, char ***namesp);
 
 /* compares two NULL-terminated arrays of strings. */
-int names_equal(char **a, char **b);
+int names_equal(const char **a, const char **b);
 
 /* returns the array size of a NULL-terminated array of strings. */
-size_t names_length(char **names);
+size_t names_length(const char **names);
 
 /* Allocation routines; they invoke the functions set through
  * reftable_set_alloc() */
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 997c4d9e01..13bc761817 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -58,8 +58,8 @@ static void test_binsearch(void)
 
 static void test_names_length(void)
 {
-	char *a[] = { "a", "b", NULL };
-	EXPECT(names_length(a) == 2);
+	const char *names[] = { "a", "b", NULL };
+	EXPECT(names_length(names) == 2);
 }
 
 static void test_parse_names_normal(void)
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e913..a2cba5ef74 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
 	return start_len - in.len;
 }
 
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
 {
 	struct string_view start = s;
 	int l = strlen(str);
@@ -969,9 +969,9 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
 	return REFTABLE_FORMAT_ERROR;
 }
 
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
 {
-	char *empty = "";
+	const char *empty = "";
 	if (!a)
 		a = empty;
 
diff --git a/reftable/stack.c b/reftable/stack.c
index a59ebe038d..09549c51c9 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -204,7 +204,8 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
 	return cur;
 }
 
-static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
+static int reftable_stack_reload_once(struct reftable_stack *st,
+				      const char **names,
 				      int reuse_open)
 {
 	size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
@@ -222,7 +223,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
 	while (*names) {
 		struct reftable_reader *rd = NULL;
-		char *name = *names++;
+		const char *name = *names++;
 
 		/* this is linear; we assume compaction keeps the number of
 		   tables under control so this is not quadratic. */
@@ -354,7 +355,7 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 				goto out;
 		}
 
-		err = reftable_stack_reload_once(st, names, reuse_open);
+		err = reftable_stack_reload_once(st, (const char **) names, reuse_open);
 		if (!err)
 			break;
 		if (err != REFTABLE_NOT_EXIST_ERROR)
@@ -368,7 +369,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 		err = read_lines(st->list_file, &names_after);
 		if (err < 0)
 			goto out;
-		if (names_equal(names_after, names)) {
+		if (names_equal((const char **) names_after,
+				(const char **) names)) {
 			err = REFTABLE_NOT_EXIST_ERROR;
 			goto out;
 		}
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 7889f818d1..07d89b45da 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
 	char out[1024] = "line1\n\nline2\nline3";
 	int n, err;
 	char **names = NULL;
-	char *want[] = { "line1", "line2", "line3" };
+	const char *want[] = { "line1", "line2", "line3" };
 	int i = 0;
 
 	EXPECT(fd > 0);
@@ -116,9 +116,9 @@ static void test_parse_names(void)
 
 static void test_names_equal(void)
 {
-	char *a[] = { "a", "b", "c", NULL };
-	char *b[] = { "a", "b", "d", NULL };
-	char *c[] = { "a", "b", NULL };
+	const char *a[] = { "a", "b", "c", NULL };
+	const char *b[] = { "a", "b", "d", NULL };
+	const char *c[] = { "a", "b", NULL };
 
 	EXPECT(names_equal(a, a));
 	EXPECT(!names_equal(a, b));
diff --git a/run-command.c b/run-command.c
index 1b821042b4..7600531fb6 100644
--- a/run-command.c
+++ b/run-command.c
@@ -663,7 +663,7 @@ int start_command(struct child_process *cmd)
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
 	int failed_errno;
-	char *str;
+	const char *str;
 
 	/*
 	 * In case of errors we must keep the promise to close FDs
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d49c..2912899558 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
 }
 
 static struct test_entry *alloc_test_entry(unsigned int hash,
-					   char *key, char *value)
+					   const char *key,
+					   const char *value)
 {
 	size_t klen = strlen(key);
 	size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f597..ed52eb76bf 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
 	jw_end(&arr4);
 }
 
-static char *expect_nest1 =
+static const char *expect_nest1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
 static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
 	jw_release(&arr1);
 }
 
-static char *expect_inline1 =
+static const char *expect_inline1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
 	("{\n"
 	 "  \"obj1\": {\n"
 	 "    \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
 	jw_end(&inline1);
 }
 
-static char *expect_inline2 =
+static const char *expect_inline2 =
 	"[[1,2],[3,4],{\"a\":\"abc\"}]";
 
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
 	("[\n"
 	 "  [\n"
 	 "    1,\n"
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042eafc2..366bd70976 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
 
 static int test_regex_bug(void)
 {
-	char *pat = "[^={} \t]+";
-	char *str = "={}\nfred";
+	const char *pat = "[^={} \t]+";
+	const char *str = "={}\nfred";
 	regex_t r;
 	regmatch_t m[1];
 
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c622..7e1d9e0ee4 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
 	strmap_clear(&delay, 0);
 }
 
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
 {
 	struct delay_entry *entry = xcalloc(1, sizeof(*entry));
 	entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
 static void command_loop(void)
 {
 	for (;;) {
-		char *buf, *output;
+		char *buf;
+		const char *output;
 		char *pathname;
 		struct delay_entry *entry;
 		struct strbuf input = STRBUF_INIT;
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4441..6027dafef7 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
 #include "strbuf.h"
 
 /* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+		  const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
 }
 
 /* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+			    const char *init_str, const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
 	strbuf_release(&buf);
 }
 
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
 {
 	const char *p_ch = data;
 	const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
 	check_char(buf->buf[buf->len], ==, '\0');
 }
 
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
 {
 	const char *text = data;
 	size_t len = strlen(text);
diff --git a/trailer.c b/trailer.c
index 2bcb9ba8f7..72e5136c73 100644
--- a/trailer.c
+++ b/trailer.c
@@ -63,7 +63,7 @@ struct arg_item {
 
 static LIST_HEAD(conf_head);
 
-static char *separators = ":";
+static const char *separators = ":";
 
 static int configured;
 
diff --git a/wt-status.c b/wt-status.c
index ff4be071ca..7912545e4e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2408,7 +2408,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
 		int mode;
 		struct object_id oid;
 	} stages[3];
-	char *key;
+	const char *key;
 	char submodule_token[5];
 	char unmerged_prefix = 'u';
 	char eol_char = s->null_termination ? '\0' : '\n';
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 02/27] global: convert intentionally-leaking config strings to consts
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
                     ` (25 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 5084 bytes --]

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 the struct memers are never
    written to or free'd.

  - `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 written to or 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 written to or free'd.

All of these are intentionally kept alive and are never written to.
Furthermore, all of these are being assigned both string constants in
some places, and allocated strings in other places. This will cause
warnings once we enable `-Wwrite-strings`, so 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(-)

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;
 };
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 03/27] refs/reftable: stop micro-optimizing refname allocations on copy
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03 18:08     ` Junio C Hamano
  2024-06-03  9:39   ` [PATCH v3 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
                     ` (24 subsequent siblings)
  27 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 4884 bytes --]

When copying refs, we execute `write_copy_table()` to write the new
table. As the names arge given to use via `arg->newname` and
`arg->oldname`, respectively, we optimize away some allocations by
assigning those fields to the reftable records we are about to write.
This requires us to cast the input to `char *` pointers as they are in
fact constant strings. Later on, we then unset the refname for all of
the records before calling `reftable_log_record_release()` on them.

We also do this when assigning the "HEAD" constant, but here we do not
cast because its type is `char[]` by default. It's about to be turned
into `const char *` though once we enable `-Wwrite-strings` and will
thus cause another warning.

It's quite dubious whether this micro-optimization really helps. We're
about to write to disk anyway, which is going to be way slower than a
small handful of allocations. Let's drop the optimization altogther and
instead copy arguments to simplify the code and avoid the future warning
with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 refs/reftable-backend.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1af86bbdec..e77faa2b9d 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1340,10 +1340,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	 * old reference.
 	 */
 	refs[0] = old_ref;
-	refs[0].refname = (char *)arg->newname;
+	refs[0].refname = xstrdup(arg->newname);
 	refs[0].update_index = creation_ts;
 	if (arg->delete_old) {
-		refs[1].refname = (char *)arg->oldname;
+		refs[1].refname = xstrdup(arg->oldname);
 		refs[1].value_type = REFTABLE_REF_DELETION;
 		refs[1].update_index = deletion_ts;
 	}
@@ -1366,7 +1366,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 		fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs[logs_nr].update_index = deletion_ts;
 		logs[logs_nr].value.update.message =
 			xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1387,7 +1387,13 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (append_head_reflog) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			logs[logs_nr] = logs[logs_nr - 1];
-			logs[logs_nr].refname = "HEAD";
+			logs[logs_nr].refname = xstrdup("HEAD");
+			logs[logs_nr].value.update.name =
+				xstrdup(logs[logs_nr].value.update.name);
+			logs[logs_nr].value.update.email =
+				xstrdup(logs[logs_nr].value.update.email);
+			logs[logs_nr].value.update.message =
+				xstrdup(logs[logs_nr].value.update.message);
 			logs_nr++;
 		}
 	}
@@ -1398,7 +1404,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 	memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 	fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-	logs[logs_nr].refname = (char *)arg->newname;
+	logs[logs_nr].refname = xstrdup(arg->newname);
 	logs[logs_nr].update_index = creation_ts;
 	logs[logs_nr].value.update.message =
 		xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1430,7 +1436,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		 */
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		logs[logs_nr] = old_log;
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs_nr++;
 
 		/*
@@ -1439,7 +1445,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (arg->delete_old) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
-			logs[logs_nr].refname = (char *)arg->oldname;
+			logs[logs_nr].refname = xstrdup(arg->oldname);
 			logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
 			logs[logs_nr].update_index = old_log.update_index;
 			logs_nr++;
@@ -1462,13 +1468,11 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	reftable_iterator_destroy(&it);
 	string_list_clear(&skip, 0);
 	strbuf_release(&errbuf);
-	for (i = 0; i < logs_nr; i++) {
-		if (!strcmp(logs[i].refname, "HEAD"))
-			continue;
-		logs[i].refname = NULL;
+	for (i = 0; i < logs_nr; i++)
 		reftable_log_record_release(&logs[i]);
-	}
 	free(logs);
+	for (i = 0; i < ARRAY_SIZE(refs); i++)
+		reftable_ref_record_release(&refs[i]);
 	reftable_ref_record_release(&old_ref);
 	reftable_log_record_release(&old_log);
 	return ret;
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 04/27] reftable: cast away constness when assigning constants to records
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (2 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
                     ` (23 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 14245 bytes --]

The reftable records are used in multiple ways throughout the reftable
library. In many of those cases they merely act as input to a function
without getting modified by it at all. Most importantly, this happens
when writing records and when querying for records.

We rely on this in our tests and thus assign string constants to those
fields, which is about to generate warnings as those fields are of type
`char *`. While we could go through the process and instead allocate
those strings in all of our tests, this feels quite unnecessary.

Instead, add casts to `char *` for all of those strings. As this is part
of our tests, this also nicely serves as a demonstration that nothing
writes or frees those string constants, which would otherwise lead to
segfaults.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 reftable/block_test.c     |  2 +-
 reftable/merged_test.c    | 44 +++++++++++++++++------------------
 reftable/readwrite_test.c | 32 +++++++++++++-------------
 reftable/stack_test.c     | 48 +++++++++++++++++++--------------------
 4 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfbc83..90aecd5a7c 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
 			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
-	rec.u.ref.refname = "";
+	rec.u.ref.refname = (char *) "";
 	rec.u.ref.value_type = REFTABLE_REF_DELETION;
 	n = block_writer_add(&bw, &rec);
 	EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82d1c..6d1159d12d 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -124,13 +124,13 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 static void test_merged_between(void)
 {
 	struct reftable_ref_record r1[] = { {
-		.refname = "b",
+		.refname = (char *) "b",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
@@ -165,38 +165,38 @@ static void test_merged(void)
 {
 	struct reftable_ref_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
 	struct reftable_ref_record r3[] = {
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 2 },
 		},
 		{
-			.refname = "d",
+			.refname = (char *) "d",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
@@ -291,46 +291,46 @@ static void test_merged_logs(void)
 {
 	struct reftable_log_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 2 },
 				/* deletion */
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message2",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message2",
 			}
 		},
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message1",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message1",
 			}
 		},
 	};
 	struct reftable_log_record r2[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.new_hash = { 3 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message3",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message3",
 			}
 		},
 	};
 	struct reftable_log_record r3[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_DELETION,
 		},
@@ -406,7 +406,7 @@ static void test_default_write_opts(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	struct reftable_ref_record rec = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.update_index = 1,
 	};
 	int err;
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd214c5..c55019232b 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -86,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
 		set_test_hash(log.value.update.new_hash, i);
-		log.value.update.message = "message";
+		log.value.update.message = (char *) "message";
 
 		n = reftable_writer_add_log(w, &log);
 		EXPECT(n == 0);
@@ -118,15 +118,15 @@ static void test_log_buffer_size(void)
 	int err;
 	int i;
 	struct reftable_log_record
-		log = { .refname = "refs/heads/master",
+		log = { .refname = (char *) "refs/heads/master",
 			.update_index = 0xa,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value = { .update = {
-					   .name = "Han-Wen Nienhuys",
-					   .email = "hanwen@google.com",
+					   .name = (char *) "Han-Wen Nienhuys",
+					   .email = (char *) "hanwen@google.com",
 					   .tz_offset = 100,
 					   .time = 0x5e430672,
-					   .message = "commit: 9\n",
+					   .message = (char *) "commit: 9\n",
 				   } } };
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
@@ -156,15 +156,15 @@ static void test_log_overflow(void)
 	};
 	int err;
 	struct reftable_log_record log = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 0xa,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "Han-Wen Nienhuys",
-				.email = "hanwen@google.com",
+				.name = (char *) "Han-Wen Nienhuys",
+				.email = (char *) "hanwen@google.com",
 				.tz_offset = 100,
 				.time = 0x5e430672,
 				.message = msg,
@@ -293,14 +293,14 @@ static void test_log_zlib_corruption(void)
 	char message[100] = { 0 };
 	int err, i, n;
 	struct reftable_log_record log = {
-		.refname = "refname",
+		.refname = (char *) "refname",
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.new_hash = { 1 },
 				.old_hash = { 2 },
-				.name = "My Name",
-				.email = "myname@invalid",
+				.name = (char *) "My Name",
+				.email = (char *) "myname@invalid",
 				.message = message,
 			},
 		},
@@ -728,7 +728,7 @@ static void test_write_empty_key(void)
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
-		.refname = "",
+		.refname = (char *) "",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_DELETION,
 	};
@@ -752,18 +752,18 @@ static void test_write_key_order(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record refs[2] = {
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}, {
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}
 	};
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 07d89b45da..4abf92636d 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -156,10 +156,10 @@ static void test_reftable_stack_add_one(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 	struct stat stat_result = { 0 };
@@ -216,16 +216,16 @@ static void test_reftable_stack_uptodate(void)
 
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "branch2",
+		.refname = (char *) "branch2",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 
@@ -264,10 +264,10 @@ static void test_reftable_stack_transaction_api(void)
 	struct reftable_addition *add = NULL;
 
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 
@@ -313,7 +313,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		char name[100];
 
@@ -356,7 +356,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
 {
 	struct reftable_ref_record ref = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = {0x01},
@@ -409,16 +409,16 @@ static void test_reftable_stack_update_index_check(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "name1",
+		.refname = (char *) "name1",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "name2",
+		.refname = (char *) "name2",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 	err = reftable_new_stack(&st, dir, cfg);
@@ -561,7 +561,7 @@ static void test_reftable_stack_log_normalize(void)
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
 	struct reftable_log_record input = {
-		.refname = "branch",
+		.refname = (char *) "branch",
 		.update_index = 1,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
@@ -582,11 +582,11 @@ static void test_reftable_stack_log_normalize(void)
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 
-	input.value.update.message = "one\ntwo";
+	input.value.update.message = (char *) "one\ntwo";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT(err == REFTABLE_API_ERROR);
 
-	input.value.update.message = "one";
+	input.value.update.message = (char *) "one";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
 
@@ -594,7 +594,7 @@ static void test_reftable_stack_log_normalize(void)
 	EXPECT_ERR(err);
 	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
 
-	input.value.update.message = "two\n";
+	input.value.update.message = (char *) "two\n";
 	arg.update_index = 2;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
@@ -697,9 +697,9 @@ static void test_reftable_stack_hash_id(void)
 	int err;
 
 	struct reftable_ref_record ref = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "target",
+		.value.symref = (char *) "target",
 		.update_index = 1,
 	};
 	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
@@ -879,7 +879,7 @@ static void test_reftable_stack_auto_compaction(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -913,7 +913,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 
 		/*
@@ -964,7 +964,7 @@ static void test_reftable_stack_compaction_concurrent(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -1014,7 +1014,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 05/27] refspec: remove global tag refspec structure
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (3 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03 18:11     ` Junio C Hamano
  2024-06-03  9:39   ` [PATCH v3 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
                     ` (22 subsequent siblings)
  27 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 3608 bytes --]

We have a global tag refspec structure that is used by both git-clone(1)
and git-fetch(1). Initialization fo the structure will break once we
enable `-Wwrite-strings`, even though the breakage is harmless. While we
could just add casts, the structure isn't really required in the first
place as we can simply initialize the structures at the respective
callsites.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c |  8 ++++++--
 builtin/fetch.c | 11 ++++++++---
 refspec.c       | 13 -------------
 refspec.h       |  1 -
 4 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 92ab7d7165..bde1d284a2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
 	struct ref *local_refs = head;
 	struct ref **tail = head ? &head->next : &local_refs;
+	struct refspec_item tag_refspec;
+
+	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
 
 	if (option_single_branch) {
 		struct ref *remote_head = NULL;
@@ -545,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 					      &tail, 0);
 
 			/* if --branch=tag, pull the requested tag explicitly */
-			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+			get_fetch_map(remote_head, &tag_refspec, &tail, 0);
 		}
 		free_refs(remote_head);
 	} else {
@@ -555,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	}
 
 	if (!option_mirror && !option_single_branch && !option_no_tags)
-		get_fetch_map(refs, tag_refspec, &tail, 0);
+		get_fetch_map(refs, &tag_refspec, &tail, 0);
 
+	refspec_item_clear(&tag_refspec);
 	return local_refs;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 75255dc600..06b60867f5 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
 		}
 	}
 
-	if (tags == TAGS_SET)
+	if (tags == TAGS_SET) {
+		struct refspec_item tag_refspec;
+
 		/* also fetch all tags */
-		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-	else if (tags == TAGS_DEFAULT && *autotags)
+		refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+		get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+		refspec_item_clear(&tag_refspec);
+	} else if (tags == TAGS_DEFAULT && *autotags) {
 		find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+	}
 
 	/* Now append any refs to be updated opportunistically: */
 	*tail = orefs;
diff --git a/refspec.c b/refspec.c
index d60932f4de..1df5de6c2f 100644
--- a/refspec.c
+++ b/refspec.c
@@ -7,19 +7,6 @@
 #include "refspec.h"
 #include "strbuf.h"
 
-static struct refspec_item s_tag_refspec = {
-	.force = 0,
-	.pattern = 1,
-	.matching = 0,
-	.exact_sha1 = 0,
-	.negative = 0,
-	.src = "refs/tags/*",
-	.dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
 /*
  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
  * Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446993..754be45cee 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
 #define REFSPEC_H
 
 #define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
 
 /**
  * A struct refspec_item holds the parsed interpretation of a refspec.  If it
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 06/27] builtin/remote: cast away constness in `get_head_names()`
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (4 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
                     ` (21 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2509 bytes --]

In `get_head_names()`, we assign the "refs/heads/*" string constant to
`struct refspec_item::{src,dst}`, which are both non-constant pointers.
Ideally, we'd refactor the code such that both of these fields were
constant. But `struct refspec_item` is used for two different usecases
with conflicting requirements:

  - To query for a source or destination based on the given refspec. The
    caller either sets `src` or `dst` as the branch that we want to
    search for, and the respective other field gets populated. The
    fields should be constant when being used as a query parameter,
    which is owned by the caller, and non-constant when being used as an
    out parameter, which is owned by the refspec item. This is is
    contradictory in itself already.

  - To store refspec items with their respective source and destination
    branches, in which case both fields should be owned by the struct.

Ideally, we'd split up this interface to clearly separate between
querying and storing, which would enable us to clarify lifetimes of the
strings. This would be a much bigger undertaking though.

Instead, accept the status quo for now and cast away the constness of
the source and destination patterns. We know that those are not being
written to or freed, so while this is ugly it certainly is fine for now.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/remote.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/builtin/remote.c b/builtin/remote.c
index d52b1c0e10..b44f580b8c 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -493,12 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 {
 	struct ref *ref, *matches;
 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
-	struct refspec_item refspec;
+	struct refspec_item refspec = {
+		.force = 0,
+		.pattern = 1,
+		.src = (char *) "refs/heads/*",
+		.dst = (char *) "refs/heads/*",
+	};
 
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.force = 0;
-	refspec.pattern = 1;
-	refspec.src = refspec.dst = "refs/heads/*";
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 				    fetch_map, 1);
@@ -507,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 
 	free_refs(fetch_map);
 	free_refs(matches);
-
 	return 0;
 }
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 07/27] diff: cast string constant in `fill_textconv()`
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (5 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
                     ` (20 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1597 bytes --]

The `fill_textconv()` function is responsible for converting an input
file with a textconv driver, which is then passed to the caller. Weirdly
though, the function also handles the case where there is no textconv
driver at all. In that case, it will return either the contents of the
populated filespec, or an empty string if the filespec is invalid.

These two cases have differing memory ownership semantics. When there is
a textconv driver, then the result is an allocated string. Otherwise,
the result is either a string constant or owned by the filespec struct.
All callers are in fact aware of this weirdness and only end up freeing
the output buffer when they had a textconv driver.

Ideally, we'd split up this interface to only perform the conversion via
the textconv driver, and BUG in case the caller didn't provide one. This
would make memory ownership semantics much more straight forward. For
now though, let's simply cast the empty string constant to `char *` to
avoid a warning with `-Wwrite-strings`. This is equivalent to the same
cast that we already have in `fill_mmfile()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 diff.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/diff.c b/diff.c
index ffd867ef6c..cecda216cf 100644
--- a/diff.c
+++ b/diff.c
@@ -7235,7 +7235,7 @@ size_t fill_textconv(struct repository *r,
 
 	if (!driver) {
 		if (!DIFF_FILE_VALID(df)) {
-			*outbuf = "";
+			*outbuf = (char *) "";
 			return 0;
 		}
 		if (diff_populate_filespec(r, df, NULL))
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 08/27] line-log: stop assigning string constant to file parent buffer
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (6 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 09/27] line-log: always allocate the output prefix Patrick Steinhardt
                     ` (19 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1198 bytes --]

Stop assigning a string constant to the file parent buffer and instead
assign an allocated string. While the code is fine in practice, it will
break once we compile with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/line-log.c b/line-log.c
index 8ff6ccb772..bd3e663c24 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1032,6 +1032,7 @@ static int process_diff_filepair(struct rev_info *rev,
 	struct range_set tmp;
 	struct diff_ranges diff;
 	mmfile_t file_parent, file_target;
+	char *parent_data_to_free = NULL;
 
 	assert(pair->two->path);
 	while (rg) {
@@ -1056,7 +1057,7 @@ static int process_diff_filepair(struct rev_info *rev,
 		file_parent.ptr = pair->one->data;
 		file_parent.size = pair->one->size;
 	} else {
-		file_parent.ptr = "";
+		file_parent.ptr = parent_data_to_free = xstrdup("");
 		file_parent.size = 0;
 	}
 
@@ -1075,6 +1076,7 @@ static int process_diff_filepair(struct rev_info *rev,
 
 	diff_ranges_release(&diff);
 
+	free(parent_data_to_free);
 	return ((*diff_out)->parent.nr > 0);
 }
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 09/27] line-log: always allocate the output prefix
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (7 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
                     ` (18 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2111 bytes --]

The returned string by `output_prefix()` is sometimes a string constant
and sometimes an allocated string. This has been fine until now because
we always leak the allocated strings, and thus we never tried to free
the string constant.

Fix the code to always return an allocated string and free the returned
value at all callsites.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/line-log.c b/line-log.c
index bd3e663c24..67c80b39a0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
 
 static char *output_prefix(struct diff_options *opt)
 {
-	char *prefix = "";
-
 	if (opt->output_prefix) {
 		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
-		prefix = sb->buf;
+		return sb->buf;
+	} else {
+		return xstrdup("");
 	}
-
-	return prefix;
 }
 
 static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
 
 	if (!pair || !diff)
-		return;
+		goto out;
 
 	if (pair->one->oid_valid)
 		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 				   c_context, c_reset, opt->file);
 	}
 
+out:
 	free(p_ends);
 	free(t_ends);
+	free(prefix);
 }
 
 /*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
  */
 static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
 {
-	fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+	char *prefix = output_prefix(&rev->diffopt);
+
+	fprintf(rev->diffopt.file, "%s\n", prefix);
+	free(prefix);
+
 	while (range) {
 		dump_diff_hacky_one(rev, range);
 		range = range->next;
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 10/27] entry: refactor how we remove items for delayed checkouts
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (8 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 09/27] line-log: always allocate the output prefix Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
                     ` (17 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2328 bytes --]

When finalizing a delayed checkout, we sort out several strings from the
passed-in string list by first assigning the empty string to those
filters and then calling `string_list_remove_empty_items()`. Assigning
the empty string will cause compiler warnings though as the string is
a `char *` once we enable `-Wwrite-strings`.

Refactor the code to use a `NULL` pointer with `filter_string_list()`
instead to avoid this warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 entry.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/entry.c b/entry.c
index b8c257f6f9..f291d8eee6 100644
--- a/entry.c
+++ b/entry.c
@@ -167,6 +167,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
 	return !available;
 }
 
+static int string_is_not_null(struct string_list_item *item, void *data UNUSED)
+{
+	return !!item->string;
+}
+
 int finish_delayed_checkout(struct checkout *state, int show_progress)
 {
 	int errs = 0;
@@ -189,7 +194,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 			if (!async_query_available_blobs(filter->string, &available_paths)) {
 				/* Filter reported an error */
 				errs = 1;
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 			if (available_paths.nr <= 0) {
@@ -199,7 +204,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 				 * filter from the list (see
 				 * "string_list_remove_empty_items" call below).
 				 */
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 
@@ -225,7 +230,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					 * Do not ask the filter for available blobs,
 					 * again, as the filter is likely buggy.
 					 */
-					filter->string = "";
+					filter->string = NULL;
 					continue;
 				}
 				ce = index_file_exists(state->istate, path->string,
@@ -239,7 +244,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					errs = 1;
 			}
 		}
-		string_list_remove_empty_items(&dco->filters, 0);
+
+		filter_string_list(&dco->filters, 0, string_is_not_null, NULL);
 	}
 	stop_progress(&progress);
 	string_list_clear(&dco->filters, 0);
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 11/27] ident: add casts for fallback name and GECOS
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (9 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
                     ` (16 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1095 bytes --]

In `xgetpwuid_self()`, we return a fallback identity when it was not
possible to look up the current identity. This fallback identity needs
to be internal and must never be written to by the calles as specified
by getpwuid(3P). As both the `pw_name` and `pw_gecos` fields are marked
as non-constant though, it will cause a warning to assign constant
strings to them once compiling with `-Wwrite-strings`.

Add explicit casts to avoid the warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 ident.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ident.c b/ident.c
index cc7afdbf81..caf41fb2a9 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,9 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
 	pw = getpwuid(getuid());
 	if (!pw) {
 		static struct passwd fallback;
-		fallback.pw_name = "unknown";
+		fallback.pw_name = (char *) "unknown";
 #ifndef NO_GECOS_IN_PWENT
-		fallback.pw_gecos = "Unknown";
+		fallback.pw_gecos = (char *) "Unknown";
 #endif
 		pw = &fallback;
 		if (is_bogus)
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 12/27] object-file: mark cached object buffers as const
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (10 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:39   ` [PATCH v3 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
                     ` (15 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1817 bytes --]

The buffers of cached objects are never modified, but are still stored
as a non-constant pointer. This will cause a compiler warning once we
enable the `-Wwrite-strings` compiler warning as we assign an empty
constant string when initializing the static `empty_tree` cached object.

Convert the field to be constant. This requires us to shuffle around
the code a bit because we memcpy(3P) into the allocated buffer in
`pretend_object_file()`. This is easily fixed though by allocating the
buffer into a temporary variable first.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/object-file.c b/object-file.c
index 610b1f465c..3afe9fce06 100644
--- a/object-file.c
+++ b/object-file.c
@@ -277,7 +277,7 @@ int hash_algo_by_length(int len)
 static struct cached_object {
 	struct object_id oid;
 	enum object_type type;
-	void *buf;
+	const void *buf;
 	unsigned long size;
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
@@ -1778,6 +1778,10 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 			struct object_id *oid)
 {
 	struct cached_object *co;
+	char *co_buf;
+
+	co_buf = xmalloc(len);
+	memcpy(co_buf, buf, len);
 
 	hash_object_file(the_hash_algo, buf, len, type, oid);
 	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
@@ -1787,8 +1791,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 	co = &cached_objects[cached_object_nr++];
 	co->size = len;
 	co->type = type;
-	co->buf = xmalloc(len);
-	memcpy(co->buf, buf, len);
+	co->buf = co_buf;
 	oidcpy(&co->oid, oid);
 	return 0;
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 13/27] object-file: make `buf` parameter of `index_mem()` a constant
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (11 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
@ 2024-06-03  9:39   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
                     ` (14 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1863 bytes --]

The `buf` parameter of `index_mem()` is a non-constant string. This will
break once we enable `-Wwrite-strings` because we also pass constants
from at least one callsite.

Adapt the parameter to be a constant. As we cannot free the buffer
without casting now, this also requires us to move the lifetime of the
nested buffer around.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/object-file.c b/object-file.c
index 3afe9fce06..b5b5a59dc6 100644
--- a/object-file.c
+++ b/object-file.c
@@ -2485,12 +2485,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
 }
 
 static int index_mem(struct index_state *istate,
-		     struct object_id *oid, void *buf, size_t size,
+		     struct object_id *oid,
+		     const void *buf, size_t size,
 		     enum object_type type,
 		     const char *path, unsigned flags)
 {
+	struct strbuf nbuf = STRBUF_INIT;
 	int ret = 0;
-	int re_allocated = 0;
 	int write_object = flags & HASH_WRITE_OBJECT;
 
 	if (!type)
@@ -2500,11 +2501,10 @@ static int index_mem(struct index_state *istate,
 	 * Convert blobs to git internal format
 	 */
 	if ((type == OBJ_BLOB) && path) {
-		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(istate, path, buf, size, &nbuf,
 				   get_conv_flags(flags))) {
-			buf = strbuf_detach(&nbuf, &size);
-			re_allocated = 1;
+			buf = nbuf.buf;
+			size = nbuf.len;
 		}
 	}
 	if (flags & HASH_FORMAT_CHECK) {
@@ -2521,8 +2521,8 @@ static int index_mem(struct index_state *istate,
 		ret = write_object_file(buf, size, type, oid);
 	else
 		hash_object_file(the_hash_algo, buf, size, type, oid);
-	if (re_allocated)
-		free(buf);
+
+	strbuf_release(&nbuf);
 	return ret;
 }
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 14/27] pretty: add casts for decoration option pointers
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (12 preceding siblings ...)
  2024-06-03  9:39   ` [PATCH v3 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
                     ` (13 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 947 bytes --]

The `struct decoration_options` have a prefix and suffix field which are
both non-constant, but we assign a constant pointer to them. This is
safe to do because we pass them to `format_decorations()`, which never
modifies these pointers, and then immediately discard the structure. Add
explicit casts to avoid compilation warnings with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 pretty.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pretty.c b/pretty.c
index ec05db5655..1df9d635fb 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1584,8 +1584,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 	case 'D':
 		{
 			const struct decoration_options opts = {
-				.prefix = "",
-				.suffix = ""
+				.prefix = (char *) "",
+				.suffix = (char *) "",
 			};
 
 			format_decorations(sb, commit, c->auto_color, &opts);
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 15/27] compat/win32: fix const-correctness with string constants
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (13 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03 16:57     ` Eric Sunshine
  2024-06-03  9:40   ` [PATCH v3 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
                     ` (12 subsequent siblings)
  27 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 4038 bytes --]

Adjust various places in our Win32 compatibility layer where we are not
assigning string constants to `const char *` variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 compat/basename.c | 18 ++++++++++++++++--
 compat/mingw.c    | 28 ++++++++++++++++------------
 compat/winansi.c  |  2 +-
 3 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/compat/basename.c b/compat/basename.c
index 96bd9533b4..5aa320306b 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -1,6 +1,8 @@
 #include "../git-compat-util.h"
 #include "../strbuf.h"
 
+static char current_directory[] = ".";
+
 /* Adapted from libiberty's basename.c.  */
 char *gitbasename (char *path)
 {
@@ -10,7 +12,13 @@ char *gitbasename (char *path)
 		skip_dos_drive_prefix(&path);
 
 	if (!path || !*path)
-		return ".";
+		/*
+		 * basename(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) "";
 
 	for (base = path; *path; path++) {
 		if (!is_dir_sep(*path))
@@ -34,7 +42,13 @@ char *gitdirname(char *path)
 	int dos_drive_prefix;
 
 	if (!p)
-		return ".";
+		/*
+		 * dirname(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) "";
 
 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
 		goto dot;
diff --git a/compat/mingw.c b/compat/mingw.c
index 6b06ea540f..d378cd04cb 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2279,7 +2279,11 @@ struct passwd *getpwuid(int uid)
 	p->pw_name = user_name;
 	p->pw_gecos = get_extended_user_info(NameDisplay);
 	if (!p->pw_gecos)
-		p->pw_gecos = "unknown";
+		/*
+		 * Data returned by getpwuid(3P) is treated as internal and
+		 * must never be written to or freed.
+		 */
+		p->pw_gecos = (char *) "unknown";
 	p->pw_dir = NULL;
 
 	initialized = 1;
@@ -2800,16 +2804,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 			strbuf_addf(report, "'%s' is on a file system that does "
 				    "not record ownership\n", path);
 		} else if (report) {
-			LPSTR str1, str2, str3, str4, to_free1 = NULL,
-			    to_free3 = NULL, to_local_free2 = NULL,
-			    to_local_free4 = NULL;
+			PCSTR str1, str2, str3, str4;
+			LPSTR to_free1 = NULL, to_free3 = NULL,
+			    to_local_free2 = NULL, to_local_free4 = NULL;
 
-			if (user_sid_to_user_name(sid, &str1))
-				to_free1 = str1;
+			if (user_sid_to_user_name(sid, &to_free1))
+				str1 = to_free1;
 			else
 				str1 = "(inconvertible)";
-			if (ConvertSidToStringSidA(sid, &str2))
-				to_local_free2 = str2;
+			if (ConvertSidToStringSidA(sid, &to_local_free2))
+				str2 = to_local_free2;
 			else
 				str2 = "(inconvertible)";
 
@@ -2822,13 +2826,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 				str4 = "(invalid)";
 			} else {
 				if (user_sid_to_user_name(current_user_sid,
-							  &str3))
-					to_free3 = str3;
+							  &to_free3))
+					str3 = to_free3;
 				else
 					str3 = "(inconvertible)";
 				if (ConvertSidToStringSidA(current_user_sid,
-							   &str4))
-					to_local_free4 = str4;
+							   &to_local_free4))
+					str4 = to_local_free4;
 				else
 					str4 = "(inconvertible)";
 			}
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f684..575813bde8 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
 	/* convert utf-8 to utf-16 */
 	int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
 	if (wlen < 0) {
-		wchar_t *err = L"[invalid]";
+		const wchar_t *err = L"[invalid]";
 		WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
 		return;
 	}
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 16/27] http: do not assign string constant to non-const field
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (14 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
                     ` (11 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1260 bytes --]

In `write_accept_language()`, we put all acceptable languages into an
array. While all entries in that array are allocated strings, the final
entry in that array is a string constant. This is fine because we
explicitly skip over the last entry when freeing the array, but will
cause warnings once we enable `-Wwrite-strings`.

Adapt the code to also allocate the final entry.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 http.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/http.c b/http.c
index 67cc47d28f..2dea2d03da 100644
--- a/http.c
+++ b/http.c
@@ -1974,7 +1974,7 @@ static void write_accept_language(struct strbuf *buf)
 
 		/* add '*' */
 		REALLOC_ARRAY(language_tags, num_langs + 1);
-		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+		language_tags[num_langs++] = xstrdup("*");
 
 		/* compute decimal_places */
 		for (max_q = 1, decimal_places = 0;
@@ -2004,8 +2004,7 @@ static void write_accept_language(struct strbuf *buf)
 		}
 	}
 
-	/* free language tags -- last one is a static '*' */
-	for (i = 0; i < num_langs - 1; i++)
+	for (i = 0; i < num_langs; i++)
 		free(language_tags[i]);
 	free(language_tags);
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 17/27] parse-options: cast long name for OPTION_ALIAS
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (15 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 18/27] send-pack: always allocate receive status Patrick Steinhardt
                     ` (10 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 792 bytes --]

We assign the long name for OPTION_ALIAS options to a non-constant value
field. We know that the variable will never be written to, but this will
cause warnings once we enable `-Wwrite-strings`.

Cast away the constness to be prepared for this change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 parse-options.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/parse-options.h b/parse-options.h
index bd62e20268..ae15342390 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
 	.type = OPTION_ALIAS, \
 	.short_name = (s), \
 	.long_name = (l), \
-	.value = (source_long_name), \
+	.value = (char *)(source_long_name), \
 }
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 18/27] send-pack: always allocate receive status
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (16 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
                     ` (9 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1586 bytes --]

In `receive_status()`, we record the reason why ref updates have been
rejected by the remote via the `remote_status`. But while we allocate
the assigned string when a reason was given, we assign a string constant
when no reason was given.

This has been working fine so far due to two reasons:

  - We don't ever free the refs in git-send-pack(1)'

  - Remotes always give a reason, at least as implemented by Git proper.

Adapt the code to always allocate the receive status string and free the
refs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/send-pack.c | 2 ++
 send-pack.c         | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaad09..17cae6bbbd 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
+	free_refs(remote_refs);
+	free_refs(local_refs);
 	return ret;
 }
diff --git a/send-pack.c b/send-pack.c
index 37f59d4f66..88e96d000b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -259,7 +259,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
 			if (p)
 				hint->remote_status = xstrdup(p);
 			else
-				hint->remote_status = "failed";
+				hint->remote_status = xstrdup("failed");
 		} else {
 			hint->status = REF_STATUS_OK;
 			hint->remote_status = xstrdup_or_null(p);
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 19/27] remote-curl: avoid assigning string constant to non-const variable
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (17 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 18/27] send-pack: always allocate receive status Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
                     ` (8 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 7752 bytes --]

When processing remote options, we split the option line into two by
searching for a space. If there is one, we replace the space with '\0',
otherwise we implicitly assume that the value is "true" and thus assign
a string constant.

As the return value of strchr(3P) weirdly enough is a `char *` even
though it gets a `const char *` as input, the assigned-to variable also
is a non-constant. This is fine though because the argument is in fact
an allocated string, and thus we are allowed to modify it. But this will
break once we enable `-Wwrite-strings`.

Refactor the code stop splitting the fields with '\0' altogether.
Instead, we can pass the length of the option name to `set_option()` and
then use strncmp(3P) instead of strcmp(3P).

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c | 53 ++++++++++++++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index cae98384da..d0f767df8e 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -58,9 +58,9 @@ struct options {
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
 
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
 {
-	if (!strcmp(name, "verbosity")) {
+	if (!strncmp(name, "verbosity", namelen)) {
 		char *end;
 		int v = strtol(value, &end, 10);
 		if (value == end || *end)
@@ -68,7 +68,7 @@ static int set_option(const char *name, const char *value)
 		options.verbosity = v;
 		return 0;
 	}
-	else if (!strcmp(name, "progress")) {
+	else if (!strncmp(name, "progress", namelen)) {
 		if (!strcmp(value, "true"))
 			options.progress = 1;
 		else if (!strcmp(value, "false"))
@@ -77,7 +77,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "depth")) {
+	else if (!strncmp(name, "depth", namelen)) {
 		char *end;
 		unsigned long v = strtoul(value, &end, 10);
 		if (value == end || *end)
@@ -85,15 +85,15 @@ static int set_option(const char *name, const char *value)
 		options.depth = v;
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-since")) {
+	else if (!strncmp(name, "deepen-since", namelen)) {
 		options.deepen_since = xstrdup(value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-not")) {
+	else if (!strncmp(name, "deepen-not", namelen)) {
 		string_list_append(&options.deepen_not, value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-relative")) {
+	else if (!strncmp(name, "deepen-relative", namelen)) {
 		if (!strcmp(value, "true"))
 			options.deepen_relative = 1;
 		else if (!strcmp(value, "false"))
@@ -102,7 +102,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "followtags")) {
+	else if (!strncmp(name, "followtags", namelen)) {
 		if (!strcmp(value, "true"))
 			options.followtags = 1;
 		else if (!strcmp(value, "false"))
@@ -111,7 +111,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "dry-run")) {
+	else if (!strncmp(name, "dry-run", namelen)) {
 		if (!strcmp(value, "true"))
 			options.dry_run = 1;
 		else if (!strcmp(value, "false"))
@@ -120,7 +120,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "check-connectivity")) {
+	else if (!strncmp(name, "check-connectivity", namelen)) {
 		if (!strcmp(value, "true"))
 			options.check_self_contained_and_connected = 1;
 		else if (!strcmp(value, "false"))
@@ -129,7 +129,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "cas")) {
+	else if (!strncmp(name, "cas", namelen)) {
 		struct strbuf val = STRBUF_INIT;
 		strbuf_addstr(&val, "--force-with-lease=");
 		if (*value != '"')
@@ -139,7 +139,7 @@ static int set_option(const char *name, const char *value)
 		string_list_append(&cas_options, val.buf);
 		strbuf_release(&val);
 		return 0;
-	} else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+	} else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
 		if (!strcmp(value, "true"))
 			options.force_if_includes = 1;
 		else if (!strcmp(value, "false"))
@@ -147,7 +147,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "cloning")) {
+	} else if (!strncmp(name, "cloning", namelen)) {
 		if (!strcmp(value, "true"))
 			options.cloning = 1;
 		else if (!strcmp(value, "false"))
@@ -155,7 +155,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "update-shallow")) {
+	} else if (!strncmp(name, "update-shallow", namelen)) {
 		if (!strcmp(value, "true"))
 			options.update_shallow = 1;
 		else if (!strcmp(value, "false"))
@@ -163,7 +163,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "pushcert")) {
+	} else if (!strncmp(name, "pushcert", namelen)) {
 		if (!strcmp(value, "true"))
 			options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
 		else if (!strcmp(value, "false"))
@@ -173,7 +173,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "atomic")) {
+	} else if (!strncmp(name, "atomic", namelen)) {
 		if (!strcmp(value, "true"))
 			options.atomic = 1;
 		else if (!strcmp(value, "false"))
@@ -181,7 +181,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "push-option")) {
+	} else if (!strncmp(name, "push-option", namelen)) {
 		if (*value != '"')
 			string_list_append(&options.push_options, value);
 		else {
@@ -192,7 +192,7 @@ static int set_option(const char *name, const char *value)
 						 strbuf_detach(&unquoted, NULL));
 		}
 		return 0;
-	} else if (!strcmp(name, "family")) {
+	} else if (!strncmp(name, "family", namelen)) {
 		if (!strcmp(value, "ipv4"))
 			git_curl_ipresolve = CURL_IPRESOLVE_V4;
 		else if (!strcmp(value, "ipv6"))
@@ -202,16 +202,16 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "from-promisor")) {
+	} else if (!strncmp(name, "from-promisor", namelen)) {
 		options.from_promisor = 1;
 		return 0;
-	} else if (!strcmp(name, "refetch")) {
+	} else if (!strncmp(name, "refetch", namelen)) {
 		options.refetch = 1;
 		return 0;
-	} else if (!strcmp(name, "filter")) {
+	} else if (!strncmp(name, "filter", namelen)) {
 		options.filter = xstrdup(value);
 		return 0;
-	} else if (!strcmp(name, "object-format")) {
+	} else if (!strncmp(name, "object-format", namelen)) {
 		options.object_format = 1;
 		if (strcmp(value, "true"))
 			die(_("unknown value for object-format: %s"), value);
@@ -1588,15 +1588,16 @@ int cmd_main(int argc, const char **argv)
 			parse_push(&buf);
 
 		} else if (skip_prefix(buf.buf, "option ", &arg)) {
-			char *value = strchr(arg, ' ');
+			const char *value = strchrnul(arg, ' ');
+			size_t arglen = value - arg;
 			int result;
 
-			if (value)
-				*value++ = '\0';
+			if (*value)
+				value++; /* skip over SP */
 			else
 				value = "true";
 
-			result = set_option(arg, value);
+			result = set_option(arg, arglen, value);
 			if (!result)
 				printf("ok\n");
 			else if (result < 0)
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 20/27] revision: always store allocated strings in output encoding
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (18 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
                     ` (7 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2531 bytes --]

The `git_log_output_encoding` variable can be set via the `--encoding=`
option. When doing so, we conditionally either assign it to the passed
value, or if the value is "none" we assign it the empty string.
Depending on which of the both code paths we pick though, the variable
may end up being assigned either an allocated string or a string
constant.

This is somewhat risky and may easily lead to bugs when a different code
path may want to reassign a new value to it, freeing the previous value.
We already to this when parsing the "i18n.logoutputencoding" config in
`git_default_i18n_config()`. But because the config is typically parsed
before we parse command line options this has been fine so far.

Regardless of that, safeguard the code such that the variable always
contains an allocated string. While at it, also free the old value in
case there was any to plug a potential memory leak.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 revision.c             | 3 ++-
 t/t3900-i18n-commit.sh | 1 +
 t/t3901-i18n-patch.sh  | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 7ddf0f151a..2ee6886078 100644
--- a/revision.c
+++ b/revision.c
@@ -2650,10 +2650,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		free(git_log_output_encoding);
 		if (strcmp(optarg, "none"))
 			git_log_output_encoding = xstrdup(optarg);
 		else
-			git_log_output_encoding = "";
+			git_log_output_encoding = xstrdup("");
 		return argcount;
 	} else if (!strcmp(arg, "--reverse")) {
 		revs->reverse ^= 1;
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09cfd9..db7b403bc1 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
 
 test_description='commit and log output encodings'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78829..5f0b9afc3f 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@ test_description='i18n settings and format-patch | am pipe'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_encoding () {
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 21/27] mailmap: always store allocated strings in mailmap blob
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (19 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
                     ` (6 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Same as with the preceding commit, the `git_mailmap_blob` may sometimes
contain an allocated string and sometimes it may contain a string
constant. This is risky and can easily lead to bugs in case the variable
is getting re-assigned, where the code may then try to free the previous
value to avoid memory leaks.

Safeguard the code by always storing allocated strings in the variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 mailmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mailmap.c b/mailmap.c
index b2efe29b3d..3d1e092fef 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -216,7 +216,7 @@ int read_mailmap(struct string_list *map)
 	map->cmp = namemap_cmp;
 
 	if (!git_mailmap_blob && is_bare_repository())
-		git_mailmap_blob = "HEAD:.mailmap";
+		git_mailmap_blob = xstrdup("HEAD:.mailmap");
 
 	if (!startup_info->have_repository || !is_bare_repository())
 		err |= read_mailmap_file(map, ".mailmap",
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 22/27] imap-send: drop global `imap_server_conf` variable
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (20 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
                     ` (5 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 7200 bytes --]

In "imap-send.c", we have a global `sturct imap_server_conf` variable
that keeps track of the configuration of the IMAP server. This variable
is being populated mostly via the Git configuration.

Refactor the code to allocate the structure on the stack instead of
having it globally. This change allows us to track its lifetime more
closely.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 57 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 8b723b34a5..67a7a6c456 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -82,10 +82,6 @@ struct imap_server_conf {
 	char *auth_method;
 };
 
-static struct imap_server_conf server = {
-	.ssl_verify = 1,
-};
-
 struct imap_socket {
 	int fd[2];
 	SSL *ssl;
@@ -110,6 +106,7 @@ struct imap {
 };
 
 struct imap_store {
+	const struct imap_server_conf *cfg;
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	int uidvalidity;
@@ -194,8 +191,8 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
 
 #ifdef NO_OPENSSL
 static int ssl_socket_connect(struct imap_socket *sock UNUSED,
-			      int use_tls_only UNUSED,
-			      int verify UNUSED)
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only UNUSED)
 {
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
@@ -250,7 +247,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
 		     cname, hostname);
 }
 
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock,
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 	const SSL_METHOD *meth;
@@ -279,7 +278,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	if (use_tls_only)
 		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
-	if (verify)
+	if (cfg->ssl_verify)
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
 
 	if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +305,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	 * OpenSSL does not document this function, but the implementation
 	 * returns 1 on success, 0 on failure after calling SSLerr().
 	 */
-	ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+	ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
 	if (ret != 1)
-		warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+		warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
 #endif
 
 	ret = SSL_connect(sock->ssl);
@@ -317,12 +316,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 		return -1;
 	}
 
-	if (verify) {
+	if (cfg->ssl_verify) {
 		/* make sure the hostname matches that of the certificate */
 		cert = SSL_get_peer_certificate(sock->ssl);
 		if (!cert)
 			return error("unable to get peer certificate.");
-		if (verify_hostname(cert, server.host) < 0)
+		if (verify_hostname(cert, cfg->host) < 0)
 			return -1;
 	}
 
@@ -895,7 +894,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 	int ret;
 	char *response;
 
-	response = cram(prompt, server.user, server.pass);
+	response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
 
 	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
 	if (ret != strlen(response))
@@ -935,6 +934,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 
 	CALLOC_ARRAY(ctx, 1);
 
+	ctx->cfg = srvc;
 	ctx->imap = CALLOC_ARRAY(imap, 1);
 	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
 	imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1035,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		imap->buf.sock.fd[1] = dup(s);
 
 		if (srvc->use_ssl &&
-		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+		    ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
 			close(s);
 			goto bail;
 		}
@@ -1068,8 +1068,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		if (!srvc->use_ssl && CAP(STARTTLS)) {
 			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
 				goto bail;
-			if (ssl_socket_connect(&imap->buf.sock, 1,
-					       srvc->ssl_verify))
+			if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
 				goto bail;
 			/* capabilities may have changed, so get the new capabilities */
 			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1299,23 +1298,24 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 static int git_imap_config(const char *var, const char *val,
 			   const struct config_context *ctx, void *cb)
 {
+	struct imap_server_conf *cfg = cb;
 
 	if (!strcmp("imap.sslverify", var))
-		server.ssl_verify = git_config_bool(var, val);
+		cfg->ssl_verify = git_config_bool(var, val);
 	else if (!strcmp("imap.preformattedhtml", var))
-		server.use_html = git_config_bool(var, val);
+		cfg->use_html = git_config_bool(var, val);
 	else if (!strcmp("imap.folder", var))
-		return git_config_string(&server.folder, var, val);
+		return git_config_string(&cfg->folder, var, val);
 	else if (!strcmp("imap.user", var))
-		return git_config_string(&server.user, var, val);
+		return git_config_string(&cfg->user, var, val);
 	else if (!strcmp("imap.pass", var))
-		return git_config_string(&server.pass, var, val);
+		return git_config_string(&cfg->pass, var, val);
 	else if (!strcmp("imap.tunnel", var))
-		return git_config_string(&server.tunnel, var, val);
+		return git_config_string(&cfg->tunnel, var, val);
 	else if (!strcmp("imap.authmethod", var))
-		return git_config_string(&server.auth_method, var, val);
+		return git_config_string(&cfg->auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val, ctx->kvi);
+		cfg->port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
@@ -1324,11 +1324,11 @@ static int git_imap_config(const char *var, const char *val,
 				val += 5;
 			else if (starts_with(val, "imaps:")) {
 				val += 6;
-				server.use_ssl = 1;
+				cfg->use_ssl = 1;
 			}
 			if (starts_with(val, "//"))
 				val += 2;
-			server.host = xstrdup(val);
+			cfg->host = xstrdup(val);
 		}
 	} else
 		return git_default_config(var, val, ctx, cb);
@@ -1497,12 +1497,15 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
 
 int cmd_main(int argc, const char **argv)
 {
+	struct imap_server_conf server = {
+		.ssl_verify = 1,
+	};
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
 
 	setup_git_directory_gently(&nongit_ok);
-	git_config(git_imap_config, NULL);
+	git_config(git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 23/27] imap-send: fix leaking memory in `imap_server_conf`
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (21 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
                     ` (4 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 4324 bytes --]

We never free any of the config strings that we populate into the
`struct imap_server_conf`. Fix this by creating a common exit path where
we can free resources.

While at it, drop the unused member `imap_server_conf::name`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 65 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 23 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 67a7a6c456..da3e7ec17e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,7 +69,6 @@ static void imap_warn(const char *, ...);
 static char *next_arg(char **);
 
 struct imap_server_conf {
-	const char *name;
 	char *tunnel;
 	char *host;
 	int port;
@@ -1300,23 +1299,28 @@ static int git_imap_config(const char *var, const char *val,
 {
 	struct imap_server_conf *cfg = cb;
 
-	if (!strcmp("imap.sslverify", var))
+	if (!strcmp("imap.sslverify", var)) {
 		cfg->ssl_verify = git_config_bool(var, val);
-	else if (!strcmp("imap.preformattedhtml", var))
+	} else if (!strcmp("imap.preformattedhtml", var)) {
 		cfg->use_html = git_config_bool(var, val);
-	else if (!strcmp("imap.folder", var))
+	} else if (!strcmp("imap.folder", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->folder, var, val);
-	else if (!strcmp("imap.user", var))
+	} else if (!strcmp("imap.user", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->user, var, val);
-	else if (!strcmp("imap.pass", var))
+	} else if (!strcmp("imap.pass", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->pass, var, val);
-	else if (!strcmp("imap.tunnel", var))
+	} else if (!strcmp("imap.tunnel", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->tunnel, var, val);
-	else if (!strcmp("imap.authmethod", var))
+	} else if (!strcmp("imap.authmethod", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->auth_method, var, val);
-	else if (!strcmp("imap.port", var))
+	} else if (!strcmp("imap.port", var)) {
 		cfg->port = git_config_int(var, val, ctx->kvi);
-	else if (!strcmp("imap.host", var)) {
+	} else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
 		} else {
@@ -1330,8 +1334,9 @@ static int git_imap_config(const char *var, const char *val,
 				val += 2;
 			cfg->host = xstrdup(val);
 		}
-	} else
+	} else {
 		return git_default_config(var, val, ctx, cb);
+	}
 
 	return 0;
 }
@@ -1503,6 +1508,7 @@ int cmd_main(int argc, const char **argv)
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
+	int ret;
 
 	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, &server);
@@ -1529,42 +1535,55 @@ int cmd_main(int argc, const char **argv)
 
 	if (!server.folder) {
 		fprintf(stderr, "no imap store specified\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
 			fprintf(stderr, "no imap host specified\n");
-			return 1;
+			ret = 1;
+			goto out;
 		}
-		server.host = "tunnel";
+		server.host = xstrdup("tunnel");
 	}
 
 	/* read the messages */
 	if (strbuf_read(&all_msgs, 0, 0) < 0) {
 		error_errno(_("could not read from stdin"));
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	if (all_msgs.len == 0) {
 		fprintf(stderr, "nothing to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	total = count_messages(&all_msgs);
 	if (!total) {
 		fprintf(stderr, "no messages to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	/* write it to the imap server */
 
 	if (server.tunnel)
-		return append_msgs_to_imap(&server, &all_msgs, total);
-
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
 #ifdef USE_CURL_FOR_IMAP_SEND
-	if (use_curl)
-		return curl_append_msgs_to_imap(&server, &all_msgs, total);
+	else if (use_curl)
+		ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
 #endif
-
-	return append_msgs_to_imap(&server, &all_msgs, total);
+	else
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
+
+out:
+	free(server.tunnel);
+	free(server.host);
+	free(server.folder);
+	free(server.user);
+	free(server.pass);
+	free(server.auth_method);
+	return ret;
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (22 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:40   ` [PATCH v3 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
                     ` (3 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 4136 bytes --]

The `struct rebase_options::default_backend` field is a non-constant
string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
Refactor the code to initialize and release options via two functions
`rebase_options_init()` and `rebase_options_release()`. Like this, we
can easily adapt the former funnction to use `xstrdup()` on the default
value without hiding it away in a macro.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 67 ++++++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 28 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 14d4f0a5e6..11f276012c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -131,25 +131,40 @@ struct rebase_options {
 	int config_update_refs;
 };
 
-#define REBASE_OPTIONS_INIT {			  	\
-		.type = REBASE_UNSPECIFIED,	  	\
-		.empty = EMPTY_UNSPECIFIED,	  	\
-		.keep_empty = 1,			\
-		.default_backend = "merge",	  	\
-		.flags = REBASE_NO_QUIET, 		\
-		.git_am_opts = STRVEC_INIT,		\
-		.exec = STRING_LIST_INIT_NODUP,		\
-		.git_format_patch_opt = STRBUF_INIT,	\
-		.fork_point = -1,			\
-		.reapply_cherry_picks = -1,             \
-		.allow_empty_message = 1,               \
-		.autosquash = -1,                       \
-		.rebase_merges = -1,                    \
-		.config_rebase_merges = -1,             \
-		.update_refs = -1,                      \
-		.config_update_refs = -1,               \
-		.strategy_opts = STRING_LIST_INIT_NODUP,\
-	}
+static void rebase_options_init(struct rebase_options *opts)
+{
+	memset(opts, 0, sizeof(*opts));
+	opts->type = REBASE_UNSPECIFIED;
+	opts->empty = EMPTY_UNSPECIFIED;
+	opts->default_backend = xstrdup("merge");
+	opts->keep_empty = 1;
+	opts->flags = REBASE_NO_QUIET;
+	strvec_init(&opts->git_am_opts);
+	string_list_init_nodup(&opts->exec);
+	strbuf_init(&opts->git_format_patch_opt, 0);
+	opts->fork_point = -1;
+	opts->reapply_cherry_picks = -1;
+	opts->allow_empty_message = 1;
+	opts->autosquash = -1;
+	opts->rebase_merges = -1;
+	opts->config_rebase_merges = -1;
+	opts->update_refs = -1;
+	opts->config_update_refs = -1;
+	string_list_init_nodup(&opts->strategy_opts);
+}
+
+static void rebase_options_release(struct rebase_options *opts)
+{
+	free(opts->default_backend);
+	free(opts->reflog_action);
+	free(opts->head_name);
+	strvec_clear(&opts->git_am_opts);
+	free(opts->gpg_sign_opt);
+	string_list_clear(&opts->exec, 0);
+	free(opts->strategy);
+	string_list_clear(&opts->strategy_opts, 0);
+	strbuf_release(&opts->git_format_patch_opt);
+}
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 {
@@ -796,6 +811,7 @@ static int rebase_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "rebase.backend")) {
+		FREE_AND_NULL(opts->default_backend);
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
@@ -1045,7 +1061,7 @@ static int check_exec_cmd(const char *cmd)
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
-	struct rebase_options options = REBASE_OPTIONS_INIT;
+	struct rebase_options options;
 	const char *branch_name;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
@@ -1178,6 +1194,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	};
 	int i;
 
+	rebase_options_init(&options);
+
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_rebase_usage,
 				   builtin_rebase_options);
@@ -1833,14 +1851,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 cleanup:
 	strbuf_release(&buf);
 	strbuf_release(&revisions);
-	free(options.reflog_action);
-	free(options.head_name);
-	strvec_clear(&options.git_am_opts);
-	free(options.gpg_sign_opt);
-	string_list_clear(&options.exec, 0);
-	free(options.strategy);
-	string_list_clear(&options.strategy_opts, 0);
-	strbuf_release(&options.git_format_patch_opt);
+	rebase_options_release(&options);
 	free(squash_onto_name);
 	free(keep_base_onto_name);
 	return !!ret;
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 25/27] builtin/rebase: always store allocated string in `options.strategy`
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (23 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
@ 2024-06-03  9:40   ` Patrick Steinhardt
  2024-06-03  9:41   ` [PATCH v3 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
                     ` (2 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2338 bytes --]

The `struct rebase_options::strategy` field is a `char *`, but we do end
up assigning string constants to it in two cases:

  - When being passed a `--strategy=` option via the command line.

  - When being passed a strategy option via `--strategy-option=`, but
    not a strategy.

This will cause warnings once we enable `-Wwrite-strings`.

Ideally, we'd just convert the field to be a `const char *`. But we also
assign to this field via the GIT_TEST_MERGE_ALGORITHM envvar, which we
have to strdup(3P) into it.

Instead, refactor the code to make sure that we only ever assign
allocated strings to this field.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 11f276012c..26068cf542 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1063,6 +1063,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options;
 	const char *branch_name;
+	const char *strategy_opt = NULL;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
 	int ok_to_skip_pre_rebase = 0;
@@ -1177,7 +1178,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
-		OPT_STRING('s', "strategy", &options.strategy,
+		OPT_STRING('s', "strategy", &strategy_opt,
 			   N_("strategy"), N_("use the given merge strategy")),
 		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
 				N_("option"),
@@ -1488,13 +1489,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	if (options.strategy_opts.nr && !options.strategy)
-		options.strategy = "ort";
-
-	if (options.strategy) {
-		options.strategy = xstrdup(options.strategy);
+	if (strategy_opt)
+		options.strategy = xstrdup(strategy_opt);
+	else if (options.strategy_opts.nr && !options.strategy)
+		options.strategy = xstrdup("ort");
+	if (options.strategy)
 		imply_merge(&options, "--strategy");
-	}
 
 	if (options.root && !options.onto_name)
 		imply_merge(&options, "--root without --onto");
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 26/27] builtin/merge: always store allocated strings in `pull_twohead`
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (24 preceding siblings ...)
  2024-06-03  9:40   ` [PATCH v3 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
@ 2024-06-03  9:41   ` Patrick Steinhardt
  2024-06-03  9:41   ` [PATCH v3 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
  2024-06-03 16:59   ` [PATCH v3 00/27] Compile with `-Wwrite-strings` Junio C Hamano
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:41 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 2451 bytes --]

The `pull_twohead` configuration may sometimes contain an allocated
string, and sometimes it may contain a string constant. Refactor this to
instead always store an allocated string such that we can release its
resources without risk.

While at it, manage the lifetime of other config strings, as well. Note
that we explicitly don't free `cleanup_arg` here. This is because the
variable may be assigned a string constant via command line options.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/merge.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/builtin/merge.c b/builtin/merge.c
index daed2d4e1e..fb3eb15b89 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -611,17 +611,19 @@ static int git_merge_config(const char *k, const char *v,
 		return 0;
 	}
 
-	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
 		show_diffstat = git_config_bool(k, v);
-	else if (!strcmp(k, "merge.verifysignatures"))
+	} else if (!strcmp(k, "merge.verifysignatures")) {
 		verify_signatures = git_config_bool(k, v);
-	else if (!strcmp(k, "pull.twohead"))
+	} else if (!strcmp(k, "pull.twohead")) {
+		FREE_AND_NULL(pull_twohead);
 		return git_config_string(&pull_twohead, k, v);
-	else if (!strcmp(k, "pull.octopus"))
+	} else if (!strcmp(k, "pull.octopus")) {
+		FREE_AND_NULL(pull_octopus);
 		return git_config_string(&pull_octopus, k, v);
-	else if (!strcmp(k, "commit.cleanup"))
+	} else if (!strcmp(k, "commit.cleanup")) {
 		return git_config_string(&cleanup_arg, k, v);
-	else if (!strcmp(k, "merge.ff")) {
+	} else if (!strcmp(k, "merge.ff")) {
 		int boolval = git_parse_maybe_bool(v);
 		if (0 <= boolval) {
 			fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -1294,7 +1296,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (!pull_twohead) {
 		char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
 		if (default_strategy && !strcmp(default_strategy, "ort"))
-			pull_twohead = "ort";
+			pull_twohead = xstrdup("ort");
 	}
 
 	init_diff_ui_defaults();
@@ -1793,6 +1795,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
+	free(pull_twohead);
+	free(pull_octopus);
 	discard_index(the_repository->index);
 	return ret;
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v3 27/27] config.mak.dev: enable `-Wwrite-strings` warning
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (25 preceding siblings ...)
  2024-06-03  9:41   ` [PATCH v3 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
@ 2024-06-03  9:41   ` Patrick Steinhardt
  2024-06-03 16:59   ` [PATCH v3 00/27] Compile with `-Wwrite-strings` Junio C Hamano
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-03  9:41 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

Writing to string constants is undefined behaviour and must be avoided
in C. Even so, the compiler does not help us with this by default
because those constants are not in fact marked as `const`. This makes it
rather easy to accidentally assign a constant to a non-const variable or
field and then later on try to either free it or write to it.

Enable `-Wwrite-strings` to catch such mistakes. With this warning
enabled, the type of string constants is changed to `const char[]` and
will thus cause compiler warnings when being assigned to non-const
fields and variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.mak.dev | 1 +
 1 file changed, 1 insertion(+)

diff --git a/config.mak.dev b/config.mak.dev
index 981304727c..1ce4c70613 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -37,6 +37,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
 DEVELOPER_CFLAGS += -Wstrict-prototypes
 DEVELOPER_CFLAGS += -Wunused
 DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
 DEVELOPER_CFLAGS += -fno-common
 
 ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH v3 15/27] compat/win32: fix const-correctness with string constants
  2024-06-03  9:40   ` [PATCH v3 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
@ 2024-06-03 16:57     ` Eric Sunshine
  2024-06-03 19:04       ` Junio C Hamano
  0 siblings, 1 reply; 205+ messages in thread
From: Eric Sunshine @ 2024-06-03 16:57 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King, Junio C Hamano

On Mon, Jun 3, 2024 at 5:46 AM Patrick Steinhardt <ps@pks.im> wrote:
> Adjust various places in our Win32 compatibility layer where we are not
> assigning string constants to `const char *` variables.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> diff --git a/compat/basename.c b/compat/basename.c
> @@ -1,6 +1,8 @@
> +static char current_directory[] = ".";
> @@ -10,7 +12,13 @@ char *gitbasename (char *path)
>         if (!path || !*path)
> -               return ".";
> +               /*
> +                * basename(3P) is mis-specified because it returns a
> +                * non-constant pointer even though it is specified to return a
> +                * pointer to internal memory at times. The cast is a result of
> +                * that.
> +                */
> +               return (char *) "";

The change from returning "." to returning "" is unexplained by the
commit message. Did you mean to return the newly-introduced
`current_directory` instead?

> @@ -34,7 +42,13 @@ char *gitdirname(char *path)
>         if (!p)
> -               return ".";
> +               /*
> +                * dirname(3P) is mis-specified because it returns a
> +                * non-constant pointer even though it is specified to return a
> +                * pointer to internal memory at times. The cast is a result of
> +                * that.
> +                */
> +               return (char *) "";

Ditto.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v3 00/27] Compile with `-Wwrite-strings`
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
                     ` (26 preceding siblings ...)
  2024-06-03  9:41   ` [PATCH v3 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
@ 2024-06-03 16:59   ` Junio C Hamano
  27 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-06-03 16:59 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> Changes compared to v2:
>
>   - I have split up the second patch into multiple patches. This was
>     done so that we can have a deeper look at the respective sites that
>     need adjustment.

We will see how well this helps reviewer.

>   - I dropped the local string arrays that I used in v2 and instead now
>     use string constants with casts in some places where we expect that
>     those should never be written to. This has the benefit that we would
>     segfault in case that expectation is violated, instead of silently
>     allowing a write to those local arrays.

Nice.

>   - I adapted multiple commit messages to not only mention freeing of
>     string constants, but also that writing to string constants is
>     illegal.

Nice again.

> Due to the split-up patch the range-diff got quite messy and was barely
> readable anymore. I thus included an interdiff instead, which should be
> way easier to read.

Triply nice.

Thanks.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v3 03/27] refs/reftable: stop micro-optimizing refname allocations on copy
  2024-06-03  9:39   ` [PATCH v3 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
@ 2024-06-03 18:08     ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-06-03 18:08 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> When copying refs, we execute `write_copy_table()` to write the new
> table. As the names arge given to use via `arg->newname` and

arge???

> `arg->oldname`, respectively, we optimize away some allocations by
> assigning those fields to the reftable records we are about to write.
> This requires us to cast the input to `char *` pointers as they are in
> fact constant strings. Later on, we then unset the refname for all of
> the records before calling `reftable_log_record_release()` on them.
>
> We also do this when assigning the "HEAD" constant, but here we do not
> cast because its type is `char[]` by default. It's about to be turned
> into `const char *` though once we enable `-Wwrite-strings` and will
> thus cause another warning.
>
> It's quite dubious whether this micro-optimization really helps. We're
> about to write to disk anyway, which is going to be way slower than a
> small handful of allocations. Let's drop the optimization altogther and
> instead copy arguments to simplify the code and avoid the future warning
> with `-Wwrite-strings`.

It certainly makes the final clean-up part simpler, which is a good
sign.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v3 05/27] refspec: remove global tag refspec structure
  2024-06-03  9:39   ` [PATCH v3 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-06-03 18:11     ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-06-03 18:11 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King

Patrick Steinhardt <ps@pks.im> writes:

> We have a global tag refspec structure that is used by both git-clone(1)
> and git-fetch(1). Initialization fo the structure will break once we

"fo" -> "of".

> enable `-Wwrite-strings`, even though the breakage is harmless. While we
> could just add casts, the structure isn't really required in the first
> place as we can simply initialize the structures at the respective
> callsites.
>
> Refactor the code accordingly.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  builtin/clone.c |  8 ++++++--
>  builtin/fetch.c | 11 ++++++++---
>  refspec.c       | 13 -------------
>  refspec.h       |  1 -
>  4 files changed, 14 insertions(+), 19 deletions(-)
>
> diff --git a/builtin/clone.c b/builtin/clone.c
> index 92ab7d7165..bde1d284a2 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
>  	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
>  	struct ref *local_refs = head;
>  	struct ref **tail = head ? &head->next : &local_refs;
> +	struct refspec_item tag_refspec;
> +
> +	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
>  
>  	if (option_single_branch) {
>  		struct ref *remote_head = NULL;
> @@ -545,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
>  					      &tail, 0);
>  
>  			/* if --branch=tag, pull the requested tag explicitly */
> -			get_fetch_map(remote_head, tag_refspec, &tail, 0);
> +			get_fetch_map(remote_head, &tag_refspec, &tail, 0);
>  		}
>  		free_refs(remote_head);
>  	} else {
> @@ -555,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
>  	}
>  
>  	if (!option_mirror && !option_single_branch && !option_no_tags)
> -		get_fetch_map(refs, tag_refspec, &tail, 0);
> +		get_fetch_map(refs, &tag_refspec, &tail, 0);
>  
> +	refspec_item_clear(&tag_refspec);
>  	return local_refs;
>  }
>  
> diff --git a/builtin/fetch.c b/builtin/fetch.c
> index 75255dc600..06b60867f5 100644
> --- a/builtin/fetch.c
> +++ b/builtin/fetch.c
> @@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
>  		}
>  	}
>  
> -	if (tags == TAGS_SET)
> +	if (tags == TAGS_SET) {
> +		struct refspec_item tag_refspec;
> +
>  		/* also fetch all tags */
> -		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
> -	else if (tags == TAGS_DEFAULT && *autotags)
> +		refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
> +		get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
> +		refspec_item_clear(&tag_refspec);
> +	} else if (tags == TAGS_DEFAULT && *autotags) {
>  		find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
> +	}
>  
>  	/* Now append any refs to be updated opportunistically: */
>  	*tail = orefs;
> diff --git a/refspec.c b/refspec.c
> index d60932f4de..1df5de6c2f 100644
> --- a/refspec.c
> +++ b/refspec.c
> @@ -7,19 +7,6 @@
>  #include "refspec.h"
>  #include "strbuf.h"
>  
> -static struct refspec_item s_tag_refspec = {
> -	.force = 0,
> -	.pattern = 1,
> -	.matching = 0,
> -	.exact_sha1 = 0,
> -	.negative = 0,
> -	.src = "refs/tags/*",
> -	.dst = "refs/tags/*",
> -};
> -
> -/* See TAG_REFSPEC for the string version */
> -const struct refspec_item *tag_refspec = &s_tag_refspec;
> -
>  /*
>   * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
>   * Returns 1 if successful and 0 if the refspec is invalid.
> diff --git a/refspec.h b/refspec.h
> index 8c0c446993..754be45cee 100644
> --- a/refspec.h
> +++ b/refspec.h
> @@ -2,7 +2,6 @@
>  #define REFSPEC_H
>  
>  #define TAG_REFSPEC "refs/tags/*:refs/tags/*"
> -extern const struct refspec_item *tag_refspec;
>  
>  /**
>   * A struct refspec_item holds the parsed interpretation of a refspec.  If it

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v3 15/27] compat/win32: fix const-correctness with string constants
  2024-06-03 16:57     ` Eric Sunshine
@ 2024-06-03 19:04       ` Junio C Hamano
  2024-06-04  6:42         ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-06-03 19:04 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Patrick Steinhardt, git, Jeff King

Eric Sunshine <sunshine@sunshineco.com> writes:

> On Mon, Jun 3, 2024 at 5:46 AM Patrick Steinhardt <ps@pks.im> wrote:
>> Adjust various places in our Win32 compatibility layer where we are not
>> assigning string constants to `const char *` variables.
>>
>> Signed-off-by: Patrick Steinhardt <ps@pks.im>
>> ---
>> diff --git a/compat/basename.c b/compat/basename.c
>> @@ -1,6 +1,8 @@
>> +static char current_directory[] = ".";
>> @@ -10,7 +12,13 @@ char *gitbasename (char *path)
>>         if (!path || !*path)
>> -               return ".";
>> +               /*
>> +                * basename(3P) is mis-specified because it returns a
>> +                * non-constant pointer even though it is specified to return a
>> +                * pointer to internal memory at times. The cast is a result of
>> +                * that.
>> +                */
>> +               return (char *) "";
>
> The change from returning "." to returning "" is unexplained by the
> commit message. Did you mean to return the newly-introduced
> `current_directory` instead?

I suspect that these places wanted to return (char *) "." instead,
without introducing a new variable.




^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v3 15/27] compat/win32: fix const-correctness with string constants
  2024-06-03 19:04       ` Junio C Hamano
@ 2024-06-04  6:42         ` Patrick Steinhardt
  0 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04  6:42 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Eric Sunshine, git, Jeff King

[-- Attachment #1: Type: text/plain, Size: 1373 bytes --]

On Mon, Jun 03, 2024 at 12:04:37PM -0700, Junio C Hamano wrote:
> Eric Sunshine <sunshine@sunshineco.com> writes:
> 
> > On Mon, Jun 3, 2024 at 5:46 AM Patrick Steinhardt <ps@pks.im> wrote:
> >> Adjust various places in our Win32 compatibility layer where we are not
> >> assigning string constants to `const char *` variables.
> >>
> >> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> >> ---
> >> diff --git a/compat/basename.c b/compat/basename.c
> >> @@ -1,6 +1,8 @@
> >> +static char current_directory[] = ".";
> >> @@ -10,7 +12,13 @@ char *gitbasename (char *path)
> >>         if (!path || !*path)
> >> -               return ".";
> >> +               /*
> >> +                * basename(3P) is mis-specified because it returns a
> >> +                * non-constant pointer even though it is specified to return a
> >> +                * pointer to internal memory at times. The cast is a result of
> >> +                * that.
> >> +                */
> >> +               return (char *) "";
> >
> > The change from returning "." to returning "" is unexplained by the
> > commit message. Did you mean to return the newly-introduced
> > `current_directory` instead?
> 
> I suspect that these places wanted to return (char *) "." instead,
> without introducing a new variable.

Oof, that's bad. Thanks for catching this!

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v4 00/27] Compile with `-Wwrite-strings`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (21 preceding siblings ...)
  2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
@ 2024-06-04 12:36 ` Patrick Steinhardt
  2024-06-04 12:36   ` [PATCH v4 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
                     ` (26 more replies)
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
  24 siblings, 27 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:36 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 10354 bytes --]

Hi,

this is another minor reroll of my patch series that prepares the code
base to compile with `-Wwrite-strings`.

There are only some small changes compared to v3:

  - Fix typos in some commit messages.

  - Fix accidental conversion of "." to "" in basename/dirname
    compatibility code. Also, remove the unused variable.

Thanks for your reviews!

Patrick

Patrick Steinhardt (27):
  global: improve const correctness when assigning string constants
  global: convert intentionally-leaking config strings to consts
  refs/reftable: stop micro-optimizing refname allocations on copy
  reftable: cast away constness when assigning constants to records
  refspec: remove global tag refspec structure
  builtin/remote: cast away constness in `get_head_names()`
  diff: cast string constant in `fill_textconv()`
  line-log: stop assigning string constant to file parent buffer
  line-log: always allocate the output prefix
  entry: refactor how we remove items for delayed checkouts
  ident: add casts for fallback name and GECOS
  object-file: mark cached object buffers as const
  object-file: make `buf` parameter of `index_mem()` a constant
  pretty: add casts for decoration option pointers
  compat/win32: fix const-correctness with string constants
  http: do not assign string constant to non-const field
  parse-options: cast long name for OPTION_ALIAS
  send-pack: always allocate receive status
  remote-curl: avoid assigning string constant to non-const variable
  revision: always store allocated strings in output encoding
  mailmap: always store allocated strings in mailmap blob
  imap-send: drop global `imap_server_conf` variable
  imap-send: fix leaking memory in `imap_server_conf`
  builtin/rebase: do not assign default backend to non-constant field
  builtin/rebase: always store allocated string in `options.strategy`
  builtin/merge: always store allocated strings in `pull_twohead`
  config.mak.dev: enable `-Wwrite-strings` warning

 builtin/bisect.c             |   3 +-
 builtin/blame.c              |   2 +-
 builtin/bugreport.c          |   2 +-
 builtin/check-ignore.c       |   4 +-
 builtin/clone.c              |  14 ++--
 builtin/commit.c             |   6 +-
 builtin/diagnose.c           |   2 +-
 builtin/fetch.c              |  11 ++-
 builtin/log.c                |   2 +-
 builtin/mailsplit.c          |   4 +-
 builtin/merge.c              |  18 +++--
 builtin/pull.c               |  52 +++++++-------
 builtin/rebase.c             |  81 ++++++++++++----------
 builtin/receive-pack.c       |   4 +-
 builtin/remote.c             |  12 ++--
 builtin/revert.c             |   2 +-
 builtin/send-pack.c          |   2 +
 compat/basename.c            |  16 ++++-
 compat/mingw.c               |  28 ++++----
 compat/regex/regcomp.c       |   2 +-
 compat/winansi.c             |   2 +-
 config.mak.dev               |   1 +
 diff.c                       |   6 +-
 diffcore-rename.c            |   6 +-
 entry.c                      |  14 ++--
 fmt-merge-msg.c              |   2 +-
 fsck.c                       |   2 +-
 fsck.h                       |   2 +-
 gpg-interface.c              |   6 +-
 http-backend.c               |   2 +-
 http.c                       |   5 +-
 ident.c                      |   4 +-
 imap-send.c                  | 130 ++++++++++++++++++++---------------
 line-log.c                   |  22 +++---
 mailmap.c                    |   2 +-
 merge-ll.c                   |  11 ++-
 object-file.c                |  23 ++++---
 parse-options.h              |   2 +-
 pretty.c                     |   6 +-
 refs.c                       |   2 +-
 refs.h                       |   2 +-
 refs/reftable-backend.c      |  28 ++++----
 refspec.c                    |  13 ----
 refspec.h                    |   1 -
 reftable/basics.c            |  15 ++--
 reftable/basics.h            |   4 +-
 reftable/basics_test.c       |   4 +-
 reftable/block_test.c        |   2 +-
 reftable/merged_test.c       |  44 ++++++------
 reftable/readwrite_test.c    |  32 ++++-----
 reftable/record.c            |   6 +-
 reftable/stack.c             |  10 +--
 reftable/stack_test.c        |  56 +++++++--------
 remote-curl.c                |  53 +++++++-------
 revision.c                   |   3 +-
 run-command.c                |   2 +-
 send-pack.c                  |   2 +-
 t/helper/test-hashmap.c      |   3 +-
 t/helper/test-json-writer.c  |  10 +--
 t/helper/test-regex.c        |   4 +-
 t/helper/test-rot13-filter.c |   5 +-
 t/t3900-i18n-commit.sh       |   1 +
 t/t3901-i18n-patch.sh        |   1 +
 t/unit-tests/t-strbuf.c      |  10 +--
 trailer.c                    |   2 +-
 userdiff.c                   |  10 +--
 userdiff.h                   |  12 ++--
 wt-status.c                  |   2 +-
 68 files changed, 468 insertions(+), 386 deletions(-)

Range-diff against v3:
 1:  e01fde88fe =  1:  e01fde88fe global: improve const correctness when assigning string constants
 2:  92cb0b28c6 =  2:  92cb0b28c6 global: convert intentionally-leaking config strings to consts
 3:  085d90c8b6 !  3:  379145478c refs/reftable: stop micro-optimizing refname allocations on copy
    @@ Commit message
         refs/reftable: stop micro-optimizing refname allocations on copy
     
         When copying refs, we execute `write_copy_table()` to write the new
    -    table. As the names arge given to use via `arg->newname` and
    +    table. As the names are given to us via `arg->newname` and
         `arg->oldname`, respectively, we optimize away some allocations by
    -    assigning those fields to the reftable records we are about to write.
    -    This requires us to cast the input to `char *` pointers as they are in
    -    fact constant strings. Later on, we then unset the refname for all of
    -    the records before calling `reftable_log_record_release()` on them.
    +    assigning those fields to the reftable records we are about to write
    +    directly, without duplicating them. This requires us to cast the input
    +    to `char *` pointers as they are in fact constant strings. Later on, we
    +    then unset the refname for all of the records before calling
    +    `reftable_log_record_release()` on them.
     
         We also do this when assigning the "HEAD" constant, but here we do not
         cast because its type is `char[]` by default. It's about to be turned
 4:  8692d9d9af =  4:  d0a2a2f6c5 reftable: cast away constness when assigning constants to records
 5:  db4d062014 !  5:  ead27d3d97 refspec: remove global tag refspec structure
    @@ Commit message
         refspec: remove global tag refspec structure
     
         We have a global tag refspec structure that is used by both git-clone(1)
    -    and git-fetch(1). Initialization fo the structure will break once we
    +    and git-fetch(1). Initialization of the structure will break once we
         enable `-Wwrite-strings`, even though the breakage is harmless. While we
         could just add casts, the structure isn't really required in the first
         place as we can simply initialize the structures at the respective
 6:  6a3c8d351e =  6:  7cb5df9182 builtin/remote: cast away constness in `get_head_names()`
 7:  750429472a =  7:  6e631a9ea4 diff: cast string constant in `fill_textconv()`
 8:  cc8fa1896d =  8:  ac164651a3 line-log: stop assigning string constant to file parent buffer
 9:  03dbdd235b =  9:  b717af02f0 line-log: always allocate the output prefix
10:  6fcb7d6685 = 10:  b46dd3210d entry: refactor how we remove items for delayed checkouts
11:  81e20a7bb8 = 11:  030dbd0288 ident: add casts for fallback name and GECOS
12:  384b4c8967 = 12:  ecca8e973d object-file: mark cached object buffers as const
13:  a1e8e77641 = 13:  62f0e47f94 object-file: make `buf` parameter of `index_mem()` a constant
14:  4d95abe9cc = 14:  e057ead2b4 pretty: add casts for decoration option pointers
15:  3d92528125 ! 15:  06b6120d26 compat/win32: fix const-correctness with string constants
    @@ Commit message
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## compat/basename.c ##
    -@@
    - #include "../git-compat-util.h"
    - #include "../strbuf.h"
    - 
    -+static char current_directory[] = ".";
    -+
    - /* Adapted from libiberty's basename.c.  */
    - char *gitbasename (char *path)
    - {
     @@ compat/basename.c: char *gitbasename (char *path)
      		skip_dos_drive_prefix(&path);
      
    @@ compat/basename.c: char *gitbasename (char *path)
     +		 * pointer to internal memory at times. The cast is a result of
     +		 * that.
     +		 */
    -+		return (char *) "";
    ++		return (char *) ".";
      
      	for (base = path; *path; path++) {
      		if (!is_dir_sep(*path))
    @@ compat/basename.c: char *gitdirname(char *path)
     +		 * pointer to internal memory at times. The cast is a result of
     +		 * that.
     +		 */
    -+		return (char *) "";
    ++		return (char *) ".";
      
      	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
      		goto dot;
16:  8a98078439 = 16:  a8ef39d73d http: do not assign string constant to non-const field
17:  26c4c76c86 = 17:  9d596a07c5 parse-options: cast long name for OPTION_ALIAS
18:  e3227bd3f3 = 18:  4019b532f9 send-pack: always allocate receive status
19:  af82e49682 = 19:  f2f1ada143 remote-curl: avoid assigning string constant to non-const variable
20:  077f10d0dc = 20:  27660b908c revision: always store allocated strings in output encoding
21:  fb240598b4 = 21:  ef43c1b18f mailmap: always store allocated strings in mailmap blob
22:  291030faa7 = 22:  0a69ce4b44 imap-send: drop global `imap_server_conf` variable
23:  9b376a313f = 23:  9ccafd286b imap-send: fix leaking memory in `imap_server_conf`
24:  0a84d66e68 = 24:  e19457d20c builtin/rebase: do not assign default backend to non-constant field
25:  5c8bee3695 = 25:  f548241960 builtin/rebase: always store allocated string in `options.strategy`
26:  84c0149c8f = 26:  78ac075644 builtin/merge: always store allocated strings in `pull_twohead`
27:  3427547134 = 27:  0cd4ce07d8 config.mak.dev: enable `-Wwrite-strings` warning
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v4 01/27] global: improve const correctness when assigning string constants
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
@ 2024-06-04 12:36   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
                     ` (25 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:36 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 27645 bytes --]

We're about to enable `-Wwrite-strings`, which changes the type of
string constants to `const char[]`. Fix various sites where we assign
such constants to non-const variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/bisect.c             |  3 ++-
 builtin/blame.c              |  2 +-
 builtin/bugreport.c          |  2 +-
 builtin/check-ignore.c       |  4 +--
 builtin/clone.c              |  6 ++---
 builtin/commit.c             |  6 ++---
 builtin/diagnose.c           |  2 +-
 builtin/log.c                |  2 +-
 builtin/mailsplit.c          |  4 +--
 builtin/pull.c               | 52 ++++++++++++++++++------------------
 builtin/receive-pack.c       |  4 +--
 builtin/revert.c             |  2 +-
 compat/regex/regcomp.c       |  2 +-
 diff.c                       |  4 +--
 diffcore-rename.c            |  6 ++---
 fmt-merge-msg.c              |  2 +-
 fsck.c                       |  2 +-
 fsck.h                       |  2 +-
 gpg-interface.c              |  2 +-
 http-backend.c               |  2 +-
 imap-send.c                  |  6 ++---
 pretty.c                     |  2 +-
 refs.c                       |  2 +-
 refs.h                       |  2 +-
 reftable/basics.c            | 15 +++++------
 reftable/basics.h            |  4 +--
 reftable/basics_test.c       |  4 +--
 reftable/record.c            |  6 ++---
 reftable/stack.c             | 10 ++++---
 reftable/stack_test.c        |  8 +++---
 run-command.c                |  2 +-
 t/helper/test-hashmap.c      |  3 ++-
 t/helper/test-json-writer.c  | 10 +++----
 t/helper/test-regex.c        |  4 +--
 t/helper/test-rot13-filter.c |  5 ++--
 t/unit-tests/t-strbuf.c      | 10 ++++---
 trailer.c                    |  2 +-
 wt-status.c                  |  2 +-
 38 files changed, 106 insertions(+), 102 deletions(-)

diff --git a/builtin/bisect.c b/builtin/bisect.c
index a58432b9d9..dabce9b542 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
 	return bisect_clean_state();
 }
 
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+		       const char *fmt, const char *state,
 		       struct commit *commit)
 {
 	struct pretty_print_context pp = {0};
diff --git a/builtin/blame.c b/builtin/blame.c
index 838cd476be..98c7629b6a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
-	char *tmp, *endp;
+	const char *tmp, *endp;
 	const char *namebuf, *mailbuf;
 
 	tmp = strstr(inbuf, what);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 25f860a0d9..b3cc77af53 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode diagnose = DIAGNOSE_NONE;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	const char *user_relative_path = NULL;
 	char *prefixed_filename;
 	size_t output_path_len;
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 6c43430ec4..2bda6a1d46 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -35,8 +35,8 @@ static const struct option check_ignore_options[] = {
 
 static void output_pattern(const char *path, struct path_pattern *pattern)
 {
-	char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
-	char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+	const char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
+	const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
 	if (!nul_term_line) {
 		if (!verbose) {
 			write_name_quoted(path, stdout, '\n');
diff --git a/builtin/clone.c b/builtin/clone.c
index 23993b905b..92ab7d7165 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -71,7 +71,7 @@ static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
 static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
 static int option_sparse_checkout;
@@ -177,8 +177,8 @@ static struct option builtin_clone_options[] = {
 
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
-	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
-	static char *bundle_suffix[] = { ".bundle", "" };
+	static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static const char *bundle_suffix[] = { ".bundle", "" };
 	size_t baselen = path->len;
 	struct stat st;
 	int i;
diff --git a/builtin/commit.c b/builtin/commit.c
index f53e7e86ff..75c741173e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,7 +113,7 @@ static char *template_file;
  * the commit message and/or authorship.
  */
 static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
 static char *fixup_message, *fixup_commit, *squash_message;
 static const char *fixup_prefix;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +121,8 @@ static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
 static struct strvec trailer_args = STRVEC_INIT;
 
 /*
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2b55..4857a4395b 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode mode = DIAGNOSE_STATS;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	char *prefixed_filename;
 
 	const struct option diagnose_options[] = {
diff --git a/builtin/log.c b/builtin/log.c
index 78a247d8a9..b8846a9458 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1283,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 	o2->flags = flags2;
 }
 
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb8ae..fe6dbc5d05 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
 	DIR *dir;
 	struct dirent *dent;
 	char *name = NULL;
-	char *subs[] = { "cur", "new", NULL };
-	char **sub;
+	const char *subs[] = { "cur", "new", NULL };
+	const char **sub;
 	int ret = -1;
 
 	for (sub = subs; *sub; ++sub) {
diff --git a/builtin/pull.c b/builtin/pull.c
index d622202bce..2d0429f14f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
 
 /* Shared options */
 static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
 
 /* Options passed to git-merge or git-rebase */
 static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
 static int opt_autostash = -1;
 static int config_autostash;
 static int check_trust_level = 1;
 static struct strvec opt_strategies = STRVEC_INIT;
 static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
 static int opt_allow_unrelated_histories;
 
 /* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
 static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
 static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
 static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
 static struct strvec opt_fetch = STRVEC_INIT;
 
 static struct option pull_options[] = {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 01c1f04ece..c8d12ee0a7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1249,7 +1249,7 @@ static int run_proc_receive_hook(struct command *commands,
 	return code;
 }
 
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
 	N_("By default, updating the current branch in a non-bare repository\n"
 	   "is denied, because it will make the index and work tree inconsistent\n"
 	   "with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
 	rp_error("%s", _(refuse_unconfigured_deny_msg));
 }
 
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
 	N_("By default, deleting the current branch is denied, because the next\n"
 	   "'git clone' won't result in any file checked out, causing confusion.\n"
 	   "\n"
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2c68..7bf2b4e11d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 
 	/* Check for incompatible command line arguments */
 	if (cmd) {
-		char *this_operation;
+		const char *this_operation;
 		if (cmd == 'q')
 			this_operation = "--quit";
 		else if (cmd == 'c')
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 2bc0f1187a..6c5d455e92 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
   unsigned int table_size;
 #ifndef _LIBC
-  char *codeset_name;
+  const char *codeset_name;
 #endif
 
   memset (dfa, '\0', sizeof (re_dfa_t));
diff --git a/diff.c b/diff.c
index e70301df76..ffd867ef6c 100644
--- a/diff.c
+++ b/diff.c
@@ -3764,7 +3764,7 @@ static void builtin_diff(const char *name_a,
 	return;
 }
 
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 {
 	if (!is_renamed) {
 		if (p->status == DIFF_STATUS_ADDED) {
@@ -4076,7 +4076,7 @@ static int reuse_worktree_file(struct index_state *istate,
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
 	struct strbuf buf = STRBUF_INIT;
-	char *dirty = "";
+	const char *dirty = "";
 
 	/* Are we looking at the work tree? */
 	if (s->dirty_submodule)
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bcac7..0e1adb87df 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -406,7 +406,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
 	return highest_destination_dir;
 }
 
-static char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
 
 static int dir_rename_already_determinable(struct strintmap *counts)
 {
@@ -429,8 +429,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
 }
 
 static void increment_count(struct dir_rename_info *info,
-			    char *old_dir,
-			    char *new_dir)
+			    const char *old_dir,
+			    const char *new_dir)
 {
 	struct strintmap *counts;
 	struct strmap_entry *e;
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 7d144b803a..5af63ab5ab 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -447,7 +447,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
 				const char *current_branch)
 {
 	int i = 0;
-	char *sep = "";
+	const char *sep = "";
 
 	strbuf_addstr(out, "Merge ");
 	for (i = 0; i < srcs.nr; i++) {
diff --git a/fsck.c b/fsck.c
index 7dff41413e..61cd48aa25 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1231,7 +1231,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 }
 
 int fsck_buffer(const struct object_id *oid, enum object_type type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options)
 {
 	if (type == OBJ_BLOB)
diff --git a/fsck.h b/fsck.h
index 17fa2dda5d..4f0c4e6479 100644
--- a/fsck.h
+++ b/fsck.h
@@ -202,7 +202,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
  * struct.
  */
 int fsck_buffer(const struct object_id *oid, enum object_type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options);
 
 /*
diff --git a/gpg-interface.c b/gpg-interface.c
index 5193223714..71a9382a61 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -727,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
 			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
-	char *fmtname = NULL;
+	const char *fmtname = NULL;
 	char *trust;
 	int ret;
 
diff --git a/http-backend.c b/http-backend.c
index 5b65287ac9..5b4dca65ed 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -753,7 +753,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
 
 int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
-	char *method = getenv("REQUEST_METHOD");
+	const char *method = getenv("REQUEST_METHOD");
 	const char *proto_header;
 	char *dir;
 	struct service_cmd *cmd = NULL;
diff --git a/imap-send.c b/imap-send.c
index a5d1510180..8b723b34a5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1215,9 +1215,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
 static void wrap_in_html(struct strbuf *msg)
 {
 	struct strbuf buf = STRBUF_INIT;
-	static char *content_type = "Content-Type: text/html;\n";
-	static char *pre_open = "<pre>\n";
-	static char *pre_close = "</pre>\n";
+	static const char *content_type = "Content-Type: text/html;\n";
+	static const char *pre_open = "<pre>\n";
+	static const char *pre_close = "</pre>\n";
 	const char *body = strstr(msg->buf, "\n\n");
 
 	if (!body)
diff --git a/pretty.c b/pretty.c
index 22a81506b7..ec05db5655 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1325,7 +1325,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
 static size_t parse_describe_args(const char *start, struct strvec *args)
 {
 	struct {
-		char *name;
+		const char *name;
 		enum {
 			DESCRIBE_ARG_BOOL,
 			DESCRIBE_ARG_INTEGER,
diff --git a/refs.c b/refs.c
index 8260c27cde..292e8d947e 100644
--- a/refs.c
+++ b/refs.c
@@ -159,7 +159,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
 {
 	struct ref_namespace_info *info = &ref_namespace[namespace];
 	if (info->ref_updated)
-		free(info->ref);
+		free((char *)info->ref);
 	info->ref = ref;
 	info->ref_updated = 1;
 }
diff --git a/refs.h b/refs.h
index 34568ee1fb..923f751d18 100644
--- a/refs.h
+++ b/refs.h
@@ -975,7 +975,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
  */
 
 struct ref_namespace_info {
-	char *ref;
+	const char *ref;
 	enum decoration_type decoration;
 
 	/*
diff --git a/reftable/basics.c b/reftable/basics.c
index fea711db7e..0058619ca6 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -67,9 +67,9 @@ void free_names(char **a)
 	reftable_free(a);
 }
 
-size_t names_length(char **names)
+size_t names_length(const char **names)
 {
-	char **p = names;
+	const char **p = names;
 	while (*p)
 		p++;
 	return p - names;
@@ -102,15 +102,12 @@ void parse_names(char *buf, int size, char ***namesp)
 	*namesp = names;
 }
 
-int names_equal(char **a, char **b)
+int names_equal(const char **a, const char **b)
 {
-	int i = 0;
-	for (; a[i] && b[i]; i++) {
-		if (strcmp(a[i], b[i])) {
+	size_t i = 0;
+	for (; a[i] && b[i]; i++)
+		if (strcmp(a[i], b[i]))
 			return 0;
-		}
-	}
-
 	return a[i] == b[i];
 }
 
diff --git a/reftable/basics.h b/reftable/basics.h
index 523ecd5307..c8fec68d4e 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -42,10 +42,10 @@ void free_names(char **a);
 void parse_names(char *buf, int size, char ***namesp);
 
 /* compares two NULL-terminated arrays of strings. */
-int names_equal(char **a, char **b);
+int names_equal(const char **a, const char **b);
 
 /* returns the array size of a NULL-terminated array of strings. */
-size_t names_length(char **names);
+size_t names_length(const char **names);
 
 /* Allocation routines; they invoke the functions set through
  * reftable_set_alloc() */
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 997c4d9e01..13bc761817 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -58,8 +58,8 @@ static void test_binsearch(void)
 
 static void test_names_length(void)
 {
-	char *a[] = { "a", "b", NULL };
-	EXPECT(names_length(a) == 2);
+	const char *names[] = { "a", "b", NULL };
+	EXPECT(names_length(names) == 2);
 }
 
 static void test_parse_names_normal(void)
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e913..a2cba5ef74 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
 	return start_len - in.len;
 }
 
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
 {
 	struct string_view start = s;
 	int l = strlen(str);
@@ -969,9 +969,9 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
 	return REFTABLE_FORMAT_ERROR;
 }
 
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
 {
-	char *empty = "";
+	const char *empty = "";
 	if (!a)
 		a = empty;
 
diff --git a/reftable/stack.c b/reftable/stack.c
index a59ebe038d..09549c51c9 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -204,7 +204,8 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
 	return cur;
 }
 
-static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
+static int reftable_stack_reload_once(struct reftable_stack *st,
+				      const char **names,
 				      int reuse_open)
 {
 	size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
@@ -222,7 +223,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
 	while (*names) {
 		struct reftable_reader *rd = NULL;
-		char *name = *names++;
+		const char *name = *names++;
 
 		/* this is linear; we assume compaction keeps the number of
 		   tables under control so this is not quadratic. */
@@ -354,7 +355,7 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 				goto out;
 		}
 
-		err = reftable_stack_reload_once(st, names, reuse_open);
+		err = reftable_stack_reload_once(st, (const char **) names, reuse_open);
 		if (!err)
 			break;
 		if (err != REFTABLE_NOT_EXIST_ERROR)
@@ -368,7 +369,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 		err = read_lines(st->list_file, &names_after);
 		if (err < 0)
 			goto out;
-		if (names_equal(names_after, names)) {
+		if (names_equal((const char **) names_after,
+				(const char **) names)) {
 			err = REFTABLE_NOT_EXIST_ERROR;
 			goto out;
 		}
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 7889f818d1..07d89b45da 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
 	char out[1024] = "line1\n\nline2\nline3";
 	int n, err;
 	char **names = NULL;
-	char *want[] = { "line1", "line2", "line3" };
+	const char *want[] = { "line1", "line2", "line3" };
 	int i = 0;
 
 	EXPECT(fd > 0);
@@ -116,9 +116,9 @@ static void test_parse_names(void)
 
 static void test_names_equal(void)
 {
-	char *a[] = { "a", "b", "c", NULL };
-	char *b[] = { "a", "b", "d", NULL };
-	char *c[] = { "a", "b", NULL };
+	const char *a[] = { "a", "b", "c", NULL };
+	const char *b[] = { "a", "b", "d", NULL };
+	const char *c[] = { "a", "b", NULL };
 
 	EXPECT(names_equal(a, a));
 	EXPECT(!names_equal(a, b));
diff --git a/run-command.c b/run-command.c
index 1b821042b4..7600531fb6 100644
--- a/run-command.c
+++ b/run-command.c
@@ -663,7 +663,7 @@ int start_command(struct child_process *cmd)
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
 	int failed_errno;
-	char *str;
+	const char *str;
 
 	/*
 	 * In case of errors we must keep the promise to close FDs
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d49c..2912899558 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
 }
 
 static struct test_entry *alloc_test_entry(unsigned int hash,
-					   char *key, char *value)
+					   const char *key,
+					   const char *value)
 {
 	size_t klen = strlen(key);
 	size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f597..ed52eb76bf 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
 	jw_end(&arr4);
 }
 
-static char *expect_nest1 =
+static const char *expect_nest1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
 static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
 	jw_release(&arr1);
 }
 
-static char *expect_inline1 =
+static const char *expect_inline1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
 	("{\n"
 	 "  \"obj1\": {\n"
 	 "    \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
 	jw_end(&inline1);
 }
 
-static char *expect_inline2 =
+static const char *expect_inline2 =
 	"[[1,2],[3,4],{\"a\":\"abc\"}]";
 
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
 	("[\n"
 	 "  [\n"
 	 "    1,\n"
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042eafc2..366bd70976 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
 
 static int test_regex_bug(void)
 {
-	char *pat = "[^={} \t]+";
-	char *str = "={}\nfred";
+	const char *pat = "[^={} \t]+";
+	const char *str = "={}\nfred";
 	regex_t r;
 	regmatch_t m[1];
 
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c622..7e1d9e0ee4 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
 	strmap_clear(&delay, 0);
 }
 
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
 {
 	struct delay_entry *entry = xcalloc(1, sizeof(*entry));
 	entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
 static void command_loop(void)
 {
 	for (;;) {
-		char *buf, *output;
+		char *buf;
+		const char *output;
 		char *pathname;
 		struct delay_entry *entry;
 		struct strbuf input = STRBUF_INIT;
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4441..6027dafef7 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
 #include "strbuf.h"
 
 /* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+		  const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
 }
 
 /* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+			    const char *init_str, const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
 	strbuf_release(&buf);
 }
 
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
 {
 	const char *p_ch = data;
 	const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
 	check_char(buf->buf[buf->len], ==, '\0');
 }
 
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
 {
 	const char *text = data;
 	size_t len = strlen(text);
diff --git a/trailer.c b/trailer.c
index 2bcb9ba8f7..72e5136c73 100644
--- a/trailer.c
+++ b/trailer.c
@@ -63,7 +63,7 @@ struct arg_item {
 
 static LIST_HEAD(conf_head);
 
-static char *separators = ":";
+static const char *separators = ":";
 
 static int configured;
 
diff --git a/wt-status.c b/wt-status.c
index ff4be071ca..7912545e4e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2408,7 +2408,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
 		int mode;
 		struct object_id oid;
 	} stages[3];
-	char *key;
+	const char *key;
 	char submodule_token[5];
 	char unmerged_prefix = 'u';
 	char eol_char = s->null_termination ? '\0' : '\n';
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 02/27] global: convert intentionally-leaking config strings to consts
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
  2024-06-04 12:36   ` [PATCH v4 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
                     ` (24 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 5084 bytes --]

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 the struct memers are never
    written to or free'd.

  - `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 written to or 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 written to or free'd.

All of these are intentionally kept alive and are never written to.
Furthermore, all of these are being assigned both string constants in
some places, and allocated strings in other places. This will cause
warnings once we enable `-Wwrite-strings`, so 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(-)

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;
 };
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 03/27] refs/reftable: stop micro-optimizing refname allocations on copy
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
  2024-06-04 12:36   ` [PATCH v4 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
                     ` (23 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 4918 bytes --]

When copying refs, we execute `write_copy_table()` to write the new
table. As the names are given to us via `arg->newname` and
`arg->oldname`, respectively, we optimize away some allocations by
assigning those fields to the reftable records we are about to write
directly, without duplicating them. This requires us to cast the input
to `char *` pointers as they are in fact constant strings. Later on, we
then unset the refname for all of the records before calling
`reftable_log_record_release()` on them.

We also do this when assigning the "HEAD" constant, but here we do not
cast because its type is `char[]` by default. It's about to be turned
into `const char *` though once we enable `-Wwrite-strings` and will
thus cause another warning.

It's quite dubious whether this micro-optimization really helps. We're
about to write to disk anyway, which is going to be way slower than a
small handful of allocations. Let's drop the optimization altogther and
instead copy arguments to simplify the code and avoid the future warning
with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 refs/reftable-backend.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1af86bbdec..e77faa2b9d 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1340,10 +1340,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	 * old reference.
 	 */
 	refs[0] = old_ref;
-	refs[0].refname = (char *)arg->newname;
+	refs[0].refname = xstrdup(arg->newname);
 	refs[0].update_index = creation_ts;
 	if (arg->delete_old) {
-		refs[1].refname = (char *)arg->oldname;
+		refs[1].refname = xstrdup(arg->oldname);
 		refs[1].value_type = REFTABLE_REF_DELETION;
 		refs[1].update_index = deletion_ts;
 	}
@@ -1366,7 +1366,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 		fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs[logs_nr].update_index = deletion_ts;
 		logs[logs_nr].value.update.message =
 			xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1387,7 +1387,13 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (append_head_reflog) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			logs[logs_nr] = logs[logs_nr - 1];
-			logs[logs_nr].refname = "HEAD";
+			logs[logs_nr].refname = xstrdup("HEAD");
+			logs[logs_nr].value.update.name =
+				xstrdup(logs[logs_nr].value.update.name);
+			logs[logs_nr].value.update.email =
+				xstrdup(logs[logs_nr].value.update.email);
+			logs[logs_nr].value.update.message =
+				xstrdup(logs[logs_nr].value.update.message);
 			logs_nr++;
 		}
 	}
@@ -1398,7 +1404,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 	memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 	fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-	logs[logs_nr].refname = (char *)arg->newname;
+	logs[logs_nr].refname = xstrdup(arg->newname);
 	logs[logs_nr].update_index = creation_ts;
 	logs[logs_nr].value.update.message =
 		xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1430,7 +1436,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		 */
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		logs[logs_nr] = old_log;
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs_nr++;
 
 		/*
@@ -1439,7 +1445,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (arg->delete_old) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
-			logs[logs_nr].refname = (char *)arg->oldname;
+			logs[logs_nr].refname = xstrdup(arg->oldname);
 			logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
 			logs[logs_nr].update_index = old_log.update_index;
 			logs_nr++;
@@ -1462,13 +1468,11 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	reftable_iterator_destroy(&it);
 	string_list_clear(&skip, 0);
 	strbuf_release(&errbuf);
-	for (i = 0; i < logs_nr; i++) {
-		if (!strcmp(logs[i].refname, "HEAD"))
-			continue;
-		logs[i].refname = NULL;
+	for (i = 0; i < logs_nr; i++)
 		reftable_log_record_release(&logs[i]);
-	}
 	free(logs);
+	for (i = 0; i < ARRAY_SIZE(refs); i++)
+		reftable_ref_record_release(&refs[i]);
 	reftable_ref_record_release(&old_ref);
 	reftable_log_record_release(&old_log);
 	return ret;
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 04/27] reftable: cast away constness when assigning constants to records
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (2 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
                     ` (22 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 14245 bytes --]

The reftable records are used in multiple ways throughout the reftable
library. In many of those cases they merely act as input to a function
without getting modified by it at all. Most importantly, this happens
when writing records and when querying for records.

We rely on this in our tests and thus assign string constants to those
fields, which is about to generate warnings as those fields are of type
`char *`. While we could go through the process and instead allocate
those strings in all of our tests, this feels quite unnecessary.

Instead, add casts to `char *` for all of those strings. As this is part
of our tests, this also nicely serves as a demonstration that nothing
writes or frees those string constants, which would otherwise lead to
segfaults.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 reftable/block_test.c     |  2 +-
 reftable/merged_test.c    | 44 +++++++++++++++++------------------
 reftable/readwrite_test.c | 32 +++++++++++++-------------
 reftable/stack_test.c     | 48 +++++++++++++++++++--------------------
 4 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfbc83..90aecd5a7c 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
 			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
-	rec.u.ref.refname = "";
+	rec.u.ref.refname = (char *) "";
 	rec.u.ref.value_type = REFTABLE_REF_DELETION;
 	n = block_writer_add(&bw, &rec);
 	EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82d1c..6d1159d12d 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -124,13 +124,13 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 static void test_merged_between(void)
 {
 	struct reftable_ref_record r1[] = { {
-		.refname = "b",
+		.refname = (char *) "b",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
@@ -165,38 +165,38 @@ static void test_merged(void)
 {
 	struct reftable_ref_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
 	struct reftable_ref_record r3[] = {
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 2 },
 		},
 		{
-			.refname = "d",
+			.refname = (char *) "d",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
@@ -291,46 +291,46 @@ static void test_merged_logs(void)
 {
 	struct reftable_log_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 2 },
 				/* deletion */
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message2",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message2",
 			}
 		},
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message1",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message1",
 			}
 		},
 	};
 	struct reftable_log_record r2[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.new_hash = { 3 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message3",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message3",
 			}
 		},
 	};
 	struct reftable_log_record r3[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_DELETION,
 		},
@@ -406,7 +406,7 @@ static void test_default_write_opts(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	struct reftable_ref_record rec = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.update_index = 1,
 	};
 	int err;
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd214c5..c55019232b 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -86,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
 		set_test_hash(log.value.update.new_hash, i);
-		log.value.update.message = "message";
+		log.value.update.message = (char *) "message";
 
 		n = reftable_writer_add_log(w, &log);
 		EXPECT(n == 0);
@@ -118,15 +118,15 @@ static void test_log_buffer_size(void)
 	int err;
 	int i;
 	struct reftable_log_record
-		log = { .refname = "refs/heads/master",
+		log = { .refname = (char *) "refs/heads/master",
 			.update_index = 0xa,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value = { .update = {
-					   .name = "Han-Wen Nienhuys",
-					   .email = "hanwen@google.com",
+					   .name = (char *) "Han-Wen Nienhuys",
+					   .email = (char *) "hanwen@google.com",
 					   .tz_offset = 100,
 					   .time = 0x5e430672,
-					   .message = "commit: 9\n",
+					   .message = (char *) "commit: 9\n",
 				   } } };
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
@@ -156,15 +156,15 @@ static void test_log_overflow(void)
 	};
 	int err;
 	struct reftable_log_record log = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 0xa,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "Han-Wen Nienhuys",
-				.email = "hanwen@google.com",
+				.name = (char *) "Han-Wen Nienhuys",
+				.email = (char *) "hanwen@google.com",
 				.tz_offset = 100,
 				.time = 0x5e430672,
 				.message = msg,
@@ -293,14 +293,14 @@ static void test_log_zlib_corruption(void)
 	char message[100] = { 0 };
 	int err, i, n;
 	struct reftable_log_record log = {
-		.refname = "refname",
+		.refname = (char *) "refname",
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.new_hash = { 1 },
 				.old_hash = { 2 },
-				.name = "My Name",
-				.email = "myname@invalid",
+				.name = (char *) "My Name",
+				.email = (char *) "myname@invalid",
 				.message = message,
 			},
 		},
@@ -728,7 +728,7 @@ static void test_write_empty_key(void)
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
-		.refname = "",
+		.refname = (char *) "",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_DELETION,
 	};
@@ -752,18 +752,18 @@ static void test_write_key_order(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record refs[2] = {
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}, {
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}
 	};
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 07d89b45da..4abf92636d 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -156,10 +156,10 @@ static void test_reftable_stack_add_one(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 	struct stat stat_result = { 0 };
@@ -216,16 +216,16 @@ static void test_reftable_stack_uptodate(void)
 
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "branch2",
+		.refname = (char *) "branch2",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 
@@ -264,10 +264,10 @@ static void test_reftable_stack_transaction_api(void)
 	struct reftable_addition *add = NULL;
 
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 
@@ -313,7 +313,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		char name[100];
 
@@ -356,7 +356,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
 {
 	struct reftable_ref_record ref = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = {0x01},
@@ -409,16 +409,16 @@ static void test_reftable_stack_update_index_check(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "name1",
+		.refname = (char *) "name1",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "name2",
+		.refname = (char *) "name2",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 	err = reftable_new_stack(&st, dir, cfg);
@@ -561,7 +561,7 @@ static void test_reftable_stack_log_normalize(void)
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
 	struct reftable_log_record input = {
-		.refname = "branch",
+		.refname = (char *) "branch",
 		.update_index = 1,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
@@ -582,11 +582,11 @@ static void test_reftable_stack_log_normalize(void)
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 
-	input.value.update.message = "one\ntwo";
+	input.value.update.message = (char *) "one\ntwo";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT(err == REFTABLE_API_ERROR);
 
-	input.value.update.message = "one";
+	input.value.update.message = (char *) "one";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
 
@@ -594,7 +594,7 @@ static void test_reftable_stack_log_normalize(void)
 	EXPECT_ERR(err);
 	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
 
-	input.value.update.message = "two\n";
+	input.value.update.message = (char *) "two\n";
 	arg.update_index = 2;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
@@ -697,9 +697,9 @@ static void test_reftable_stack_hash_id(void)
 	int err;
 
 	struct reftable_ref_record ref = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "target",
+		.value.symref = (char *) "target",
 		.update_index = 1,
 	};
 	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
@@ -879,7 +879,7 @@ static void test_reftable_stack_auto_compaction(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -913,7 +913,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 
 		/*
@@ -964,7 +964,7 @@ static void test_reftable_stack_compaction_concurrent(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -1014,7 +1014,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 05/27] refspec: remove global tag refspec structure
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (3 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
                     ` (21 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 3608 bytes --]

We have a global tag refspec structure that is used by both git-clone(1)
and git-fetch(1). Initialization of the structure will break once we
enable `-Wwrite-strings`, even though the breakage is harmless. While we
could just add casts, the structure isn't really required in the first
place as we can simply initialize the structures at the respective
callsites.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c |  8 ++++++--
 builtin/fetch.c | 11 ++++++++---
 refspec.c       | 13 -------------
 refspec.h       |  1 -
 4 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 92ab7d7165..bde1d284a2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
 	struct ref *local_refs = head;
 	struct ref **tail = head ? &head->next : &local_refs;
+	struct refspec_item tag_refspec;
+
+	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
 
 	if (option_single_branch) {
 		struct ref *remote_head = NULL;
@@ -545,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 					      &tail, 0);
 
 			/* if --branch=tag, pull the requested tag explicitly */
-			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+			get_fetch_map(remote_head, &tag_refspec, &tail, 0);
 		}
 		free_refs(remote_head);
 	} else {
@@ -555,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	}
 
 	if (!option_mirror && !option_single_branch && !option_no_tags)
-		get_fetch_map(refs, tag_refspec, &tail, 0);
+		get_fetch_map(refs, &tag_refspec, &tail, 0);
 
+	refspec_item_clear(&tag_refspec);
 	return local_refs;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 75255dc600..06b60867f5 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
 		}
 	}
 
-	if (tags == TAGS_SET)
+	if (tags == TAGS_SET) {
+		struct refspec_item tag_refspec;
+
 		/* also fetch all tags */
-		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-	else if (tags == TAGS_DEFAULT && *autotags)
+		refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+		get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+		refspec_item_clear(&tag_refspec);
+	} else if (tags == TAGS_DEFAULT && *autotags) {
 		find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+	}
 
 	/* Now append any refs to be updated opportunistically: */
 	*tail = orefs;
diff --git a/refspec.c b/refspec.c
index d60932f4de..1df5de6c2f 100644
--- a/refspec.c
+++ b/refspec.c
@@ -7,19 +7,6 @@
 #include "refspec.h"
 #include "strbuf.h"
 
-static struct refspec_item s_tag_refspec = {
-	.force = 0,
-	.pattern = 1,
-	.matching = 0,
-	.exact_sha1 = 0,
-	.negative = 0,
-	.src = "refs/tags/*",
-	.dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
 /*
  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
  * Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446993..754be45cee 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
 #define REFSPEC_H
 
 #define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
 
 /**
  * A struct refspec_item holds the parsed interpretation of a refspec.  If it
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 06/27] builtin/remote: cast away constness in `get_head_names()`
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (4 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
                     ` (20 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2509 bytes --]

In `get_head_names()`, we assign the "refs/heads/*" string constant to
`struct refspec_item::{src,dst}`, which are both non-constant pointers.
Ideally, we'd refactor the code such that both of these fields were
constant. But `struct refspec_item` is used for two different usecases
with conflicting requirements:

  - To query for a source or destination based on the given refspec. The
    caller either sets `src` or `dst` as the branch that we want to
    search for, and the respective other field gets populated. The
    fields should be constant when being used as a query parameter,
    which is owned by the caller, and non-constant when being used as an
    out parameter, which is owned by the refspec item. This is is
    contradictory in itself already.

  - To store refspec items with their respective source and destination
    branches, in which case both fields should be owned by the struct.

Ideally, we'd split up this interface to clearly separate between
querying and storing, which would enable us to clarify lifetimes of the
strings. This would be a much bigger undertaking though.

Instead, accept the status quo for now and cast away the constness of
the source and destination patterns. We know that those are not being
written to or freed, so while this is ugly it certainly is fine for now.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/remote.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/builtin/remote.c b/builtin/remote.c
index d52b1c0e10..b44f580b8c 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -493,12 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 {
 	struct ref *ref, *matches;
 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
-	struct refspec_item refspec;
+	struct refspec_item refspec = {
+		.force = 0,
+		.pattern = 1,
+		.src = (char *) "refs/heads/*",
+		.dst = (char *) "refs/heads/*",
+	};
 
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.force = 0;
-	refspec.pattern = 1;
-	refspec.src = refspec.dst = "refs/heads/*";
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 				    fetch_map, 1);
@@ -507,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 
 	free_refs(fetch_map);
 	free_refs(matches);
-
 	return 0;
 }
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 07/27] diff: cast string constant in `fill_textconv()`
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (5 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
                     ` (19 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1597 bytes --]

The `fill_textconv()` function is responsible for converting an input
file with a textconv driver, which is then passed to the caller. Weirdly
though, the function also handles the case where there is no textconv
driver at all. In that case, it will return either the contents of the
populated filespec, or an empty string if the filespec is invalid.

These two cases have differing memory ownership semantics. When there is
a textconv driver, then the result is an allocated string. Otherwise,
the result is either a string constant or owned by the filespec struct.
All callers are in fact aware of this weirdness and only end up freeing
the output buffer when they had a textconv driver.

Ideally, we'd split up this interface to only perform the conversion via
the textconv driver, and BUG in case the caller didn't provide one. This
would make memory ownership semantics much more straight forward. For
now though, let's simply cast the empty string constant to `char *` to
avoid a warning with `-Wwrite-strings`. This is equivalent to the same
cast that we already have in `fill_mmfile()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 diff.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/diff.c b/diff.c
index ffd867ef6c..cecda216cf 100644
--- a/diff.c
+++ b/diff.c
@@ -7235,7 +7235,7 @@ size_t fill_textconv(struct repository *r,
 
 	if (!driver) {
 		if (!DIFF_FILE_VALID(df)) {
-			*outbuf = "";
+			*outbuf = (char *) "";
 			return 0;
 		}
 		if (diff_populate_filespec(r, df, NULL))
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 08/27] line-log: stop assigning string constant to file parent buffer
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (6 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 09/27] line-log: always allocate the output prefix Patrick Steinhardt
                     ` (18 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1198 bytes --]

Stop assigning a string constant to the file parent buffer and instead
assign an allocated string. While the code is fine in practice, it will
break once we compile with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/line-log.c b/line-log.c
index 8ff6ccb772..bd3e663c24 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1032,6 +1032,7 @@ static int process_diff_filepair(struct rev_info *rev,
 	struct range_set tmp;
 	struct diff_ranges diff;
 	mmfile_t file_parent, file_target;
+	char *parent_data_to_free = NULL;
 
 	assert(pair->two->path);
 	while (rg) {
@@ -1056,7 +1057,7 @@ static int process_diff_filepair(struct rev_info *rev,
 		file_parent.ptr = pair->one->data;
 		file_parent.size = pair->one->size;
 	} else {
-		file_parent.ptr = "";
+		file_parent.ptr = parent_data_to_free = xstrdup("");
 		file_parent.size = 0;
 	}
 
@@ -1075,6 +1076,7 @@ static int process_diff_filepair(struct rev_info *rev,
 
 	diff_ranges_release(&diff);
 
+	free(parent_data_to_free);
 	return ((*diff_out)->parent.nr > 0);
 }
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 09/27] line-log: always allocate the output prefix
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (7 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
                     ` (17 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2111 bytes --]

The returned string by `output_prefix()` is sometimes a string constant
and sometimes an allocated string. This has been fine until now because
we always leak the allocated strings, and thus we never tried to free
the string constant.

Fix the code to always return an allocated string and free the returned
value at all callsites.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/line-log.c b/line-log.c
index bd3e663c24..67c80b39a0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
 
 static char *output_prefix(struct diff_options *opt)
 {
-	char *prefix = "";
-
 	if (opt->output_prefix) {
 		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
-		prefix = sb->buf;
+		return sb->buf;
+	} else {
+		return xstrdup("");
 	}
-
-	return prefix;
 }
 
 static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
 
 	if (!pair || !diff)
-		return;
+		goto out;
 
 	if (pair->one->oid_valid)
 		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 				   c_context, c_reset, opt->file);
 	}
 
+out:
 	free(p_ends);
 	free(t_ends);
+	free(prefix);
 }
 
 /*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
  */
 static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
 {
-	fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+	char *prefix = output_prefix(&rev->diffopt);
+
+	fprintf(rev->diffopt.file, "%s\n", prefix);
+	free(prefix);
+
 	while (range) {
 		dump_diff_hacky_one(rev, range);
 		range = range->next;
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 10/27] entry: refactor how we remove items for delayed checkouts
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (8 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 09/27] line-log: always allocate the output prefix Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
                     ` (16 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2328 bytes --]

When finalizing a delayed checkout, we sort out several strings from the
passed-in string list by first assigning the empty string to those
filters and then calling `string_list_remove_empty_items()`. Assigning
the empty string will cause compiler warnings though as the string is
a `char *` once we enable `-Wwrite-strings`.

Refactor the code to use a `NULL` pointer with `filter_string_list()`
instead to avoid this warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 entry.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/entry.c b/entry.c
index b8c257f6f9..f291d8eee6 100644
--- a/entry.c
+++ b/entry.c
@@ -167,6 +167,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
 	return !available;
 }
 
+static int string_is_not_null(struct string_list_item *item, void *data UNUSED)
+{
+	return !!item->string;
+}
+
 int finish_delayed_checkout(struct checkout *state, int show_progress)
 {
 	int errs = 0;
@@ -189,7 +194,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 			if (!async_query_available_blobs(filter->string, &available_paths)) {
 				/* Filter reported an error */
 				errs = 1;
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 			if (available_paths.nr <= 0) {
@@ -199,7 +204,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 				 * filter from the list (see
 				 * "string_list_remove_empty_items" call below).
 				 */
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 
@@ -225,7 +230,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					 * Do not ask the filter for available blobs,
 					 * again, as the filter is likely buggy.
 					 */
-					filter->string = "";
+					filter->string = NULL;
 					continue;
 				}
 				ce = index_file_exists(state->istate, path->string,
@@ -239,7 +244,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					errs = 1;
 			}
 		}
-		string_list_remove_empty_items(&dco->filters, 0);
+
+		filter_string_list(&dco->filters, 0, string_is_not_null, NULL);
 	}
 	stop_progress(&progress);
 	string_list_clear(&dco->filters, 0);
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 11/27] ident: add casts for fallback name and GECOS
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (9 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
                     ` (15 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1095 bytes --]

In `xgetpwuid_self()`, we return a fallback identity when it was not
possible to look up the current identity. This fallback identity needs
to be internal and must never be written to by the calles as specified
by getpwuid(3P). As both the `pw_name` and `pw_gecos` fields are marked
as non-constant though, it will cause a warning to assign constant
strings to them once compiling with `-Wwrite-strings`.

Add explicit casts to avoid the warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 ident.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ident.c b/ident.c
index cc7afdbf81..caf41fb2a9 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,9 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
 	pw = getpwuid(getuid());
 	if (!pw) {
 		static struct passwd fallback;
-		fallback.pw_name = "unknown";
+		fallback.pw_name = (char *) "unknown";
 #ifndef NO_GECOS_IN_PWENT
-		fallback.pw_gecos = "Unknown";
+		fallback.pw_gecos = (char *) "Unknown";
 #endif
 		pw = &fallback;
 		if (is_bogus)
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 12/27] object-file: mark cached object buffers as const
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (10 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-06  6:02     ` Junio C Hamano
  2024-06-04 12:37   ` [PATCH v4 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
                     ` (14 subsequent siblings)
  26 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1817 bytes --]

The buffers of cached objects are never modified, but are still stored
as a non-constant pointer. This will cause a compiler warning once we
enable the `-Wwrite-strings` compiler warning as we assign an empty
constant string when initializing the static `empty_tree` cached object.

Convert the field to be constant. This requires us to shuffle around
the code a bit because we memcpy(3P) into the allocated buffer in
`pretend_object_file()`. This is easily fixed though by allocating the
buffer into a temporary variable first.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/object-file.c b/object-file.c
index 610b1f465c..3afe9fce06 100644
--- a/object-file.c
+++ b/object-file.c
@@ -277,7 +277,7 @@ int hash_algo_by_length(int len)
 static struct cached_object {
 	struct object_id oid;
 	enum object_type type;
-	void *buf;
+	const void *buf;
 	unsigned long size;
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
@@ -1778,6 +1778,10 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 			struct object_id *oid)
 {
 	struct cached_object *co;
+	char *co_buf;
+
+	co_buf = xmalloc(len);
+	memcpy(co_buf, buf, len);
 
 	hash_object_file(the_hash_algo, buf, len, type, oid);
 	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
@@ -1787,8 +1791,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 	co = &cached_objects[cached_object_nr++];
 	co->size = len;
 	co->type = type;
-	co->buf = xmalloc(len);
-	memcpy(co->buf, buf, len);
+	co->buf = co_buf;
 	oidcpy(&co->oid, oid);
 	return 0;
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 13/27] object-file: make `buf` parameter of `index_mem()` a constant
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (11 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:37   ` [PATCH v4 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
                     ` (13 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1863 bytes --]

The `buf` parameter of `index_mem()` is a non-constant string. This will
break once we enable `-Wwrite-strings` because we also pass constants
from at least one callsite.

Adapt the parameter to be a constant. As we cannot free the buffer
without casting now, this also requires us to move the lifetime of the
nested buffer around.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/object-file.c b/object-file.c
index 3afe9fce06..b5b5a59dc6 100644
--- a/object-file.c
+++ b/object-file.c
@@ -2485,12 +2485,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
 }
 
 static int index_mem(struct index_state *istate,
-		     struct object_id *oid, void *buf, size_t size,
+		     struct object_id *oid,
+		     const void *buf, size_t size,
 		     enum object_type type,
 		     const char *path, unsigned flags)
 {
+	struct strbuf nbuf = STRBUF_INIT;
 	int ret = 0;
-	int re_allocated = 0;
 	int write_object = flags & HASH_WRITE_OBJECT;
 
 	if (!type)
@@ -2500,11 +2501,10 @@ static int index_mem(struct index_state *istate,
 	 * Convert blobs to git internal format
 	 */
 	if ((type == OBJ_BLOB) && path) {
-		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(istate, path, buf, size, &nbuf,
 				   get_conv_flags(flags))) {
-			buf = strbuf_detach(&nbuf, &size);
-			re_allocated = 1;
+			buf = nbuf.buf;
+			size = nbuf.len;
 		}
 	}
 	if (flags & HASH_FORMAT_CHECK) {
@@ -2521,8 +2521,8 @@ static int index_mem(struct index_state *istate,
 		ret = write_object_file(buf, size, type, oid);
 	else
 		hash_object_file(the_hash_algo, buf, size, type, oid);
-	if (re_allocated)
-		free(buf);
+
+	strbuf_release(&nbuf);
 	return ret;
 }
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 14/27] pretty: add casts for decoration option pointers
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (12 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
@ 2024-06-04 12:37   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
                     ` (12 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 947 bytes --]

The `struct decoration_options` have a prefix and suffix field which are
both non-constant, but we assign a constant pointer to them. This is
safe to do because we pass them to `format_decorations()`, which never
modifies these pointers, and then immediately discard the structure. Add
explicit casts to avoid compilation warnings with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 pretty.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pretty.c b/pretty.c
index ec05db5655..1df9d635fb 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1584,8 +1584,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 	case 'D':
 		{
 			const struct decoration_options opts = {
-				.prefix = "",
-				.suffix = ""
+				.prefix = (char *) "",
+				.suffix = (char *) "",
 			};
 
 			format_decorations(sb, commit, c->auto_color, &opts);
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 15/27] compat/win32: fix const-correctness with string constants
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (13 preceding siblings ...)
  2024-06-04 12:37   ` [PATCH v4 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
                     ` (11 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 3832 bytes --]

Adjust various places in our Win32 compatibility layer where we are not
assigning string constants to `const char *` variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 compat/basename.c | 16 ++++++++++++++--
 compat/mingw.c    | 28 ++++++++++++++++------------
 compat/winansi.c  |  2 +-
 3 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/compat/basename.c b/compat/basename.c
index 96bd9533b4..c33579ef61 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -10,7 +10,13 @@ char *gitbasename (char *path)
 		skip_dos_drive_prefix(&path);
 
 	if (!path || !*path)
-		return ".";
+		/*
+		 * basename(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) ".";
 
 	for (base = path; *path; path++) {
 		if (!is_dir_sep(*path))
@@ -34,7 +40,13 @@ char *gitdirname(char *path)
 	int dos_drive_prefix;
 
 	if (!p)
-		return ".";
+		/*
+		 * dirname(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) ".";
 
 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
 		goto dot;
diff --git a/compat/mingw.c b/compat/mingw.c
index 6b06ea540f..d378cd04cb 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2279,7 +2279,11 @@ struct passwd *getpwuid(int uid)
 	p->pw_name = user_name;
 	p->pw_gecos = get_extended_user_info(NameDisplay);
 	if (!p->pw_gecos)
-		p->pw_gecos = "unknown";
+		/*
+		 * Data returned by getpwuid(3P) is treated as internal and
+		 * must never be written to or freed.
+		 */
+		p->pw_gecos = (char *) "unknown";
 	p->pw_dir = NULL;
 
 	initialized = 1;
@@ -2800,16 +2804,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 			strbuf_addf(report, "'%s' is on a file system that does "
 				    "not record ownership\n", path);
 		} else if (report) {
-			LPSTR str1, str2, str3, str4, to_free1 = NULL,
-			    to_free3 = NULL, to_local_free2 = NULL,
-			    to_local_free4 = NULL;
+			PCSTR str1, str2, str3, str4;
+			LPSTR to_free1 = NULL, to_free3 = NULL,
+			    to_local_free2 = NULL, to_local_free4 = NULL;
 
-			if (user_sid_to_user_name(sid, &str1))
-				to_free1 = str1;
+			if (user_sid_to_user_name(sid, &to_free1))
+				str1 = to_free1;
 			else
 				str1 = "(inconvertible)";
-			if (ConvertSidToStringSidA(sid, &str2))
-				to_local_free2 = str2;
+			if (ConvertSidToStringSidA(sid, &to_local_free2))
+				str2 = to_local_free2;
 			else
 				str2 = "(inconvertible)";
 
@@ -2822,13 +2826,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 				str4 = "(invalid)";
 			} else {
 				if (user_sid_to_user_name(current_user_sid,
-							  &str3))
-					to_free3 = str3;
+							  &to_free3))
+					str3 = to_free3;
 				else
 					str3 = "(inconvertible)";
 				if (ConvertSidToStringSidA(current_user_sid,
-							   &str4))
-					to_local_free4 = str4;
+							   &to_local_free4))
+					str4 = to_local_free4;
 				else
 					str4 = "(inconvertible)";
 			}
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f684..575813bde8 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
 	/* convert utf-8 to utf-16 */
 	int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
 	if (wlen < 0) {
-		wchar_t *err = L"[invalid]";
+		const wchar_t *err = L"[invalid]";
 		WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
 		return;
 	}
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 16/27] http: do not assign string constant to non-const field
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (14 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
                     ` (10 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1260 bytes --]

In `write_accept_language()`, we put all acceptable languages into an
array. While all entries in that array are allocated strings, the final
entry in that array is a string constant. This is fine because we
explicitly skip over the last entry when freeing the array, but will
cause warnings once we enable `-Wwrite-strings`.

Adapt the code to also allocate the final entry.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 http.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/http.c b/http.c
index 67cc47d28f..2dea2d03da 100644
--- a/http.c
+++ b/http.c
@@ -1974,7 +1974,7 @@ static void write_accept_language(struct strbuf *buf)
 
 		/* add '*' */
 		REALLOC_ARRAY(language_tags, num_langs + 1);
-		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+		language_tags[num_langs++] = xstrdup("*");
 
 		/* compute decimal_places */
 		for (max_q = 1, decimal_places = 0;
@@ -2004,8 +2004,7 @@ static void write_accept_language(struct strbuf *buf)
 		}
 	}
 
-	/* free language tags -- last one is a static '*' */
-	for (i = 0; i < num_langs - 1; i++)
+	for (i = 0; i < num_langs; i++)
 		free(language_tags[i]);
 	free(language_tags);
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 17/27] parse-options: cast long name for OPTION_ALIAS
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (15 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 18/27] send-pack: always allocate receive status Patrick Steinhardt
                     ` (9 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 792 bytes --]

We assign the long name for OPTION_ALIAS options to a non-constant value
field. We know that the variable will never be written to, but this will
cause warnings once we enable `-Wwrite-strings`.

Cast away the constness to be prepared for this change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 parse-options.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/parse-options.h b/parse-options.h
index bd62e20268..ae15342390 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
 	.type = OPTION_ALIAS, \
 	.short_name = (s), \
 	.long_name = (l), \
-	.value = (source_long_name), \
+	.value = (char *)(source_long_name), \
 }
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 18/27] send-pack: always allocate receive status
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (16 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
                     ` (8 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1586 bytes --]

In `receive_status()`, we record the reason why ref updates have been
rejected by the remote via the `remote_status`. But while we allocate
the assigned string when a reason was given, we assign a string constant
when no reason was given.

This has been working fine so far due to two reasons:

  - We don't ever free the refs in git-send-pack(1)'

  - Remotes always give a reason, at least as implemented by Git proper.

Adapt the code to always allocate the receive status string and free the
refs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/send-pack.c | 2 ++
 send-pack.c         | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaad09..17cae6bbbd 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
+	free_refs(remote_refs);
+	free_refs(local_refs);
 	return ret;
 }
diff --git a/send-pack.c b/send-pack.c
index 37f59d4f66..88e96d000b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -259,7 +259,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
 			if (p)
 				hint->remote_status = xstrdup(p);
 			else
-				hint->remote_status = "failed";
+				hint->remote_status = xstrdup("failed");
 		} else {
 			hint->status = REF_STATUS_OK;
 			hint->remote_status = xstrdup_or_null(p);
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 19/27] remote-curl: avoid assigning string constant to non-const variable
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (17 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 18/27] send-pack: always allocate receive status Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
                     ` (7 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 7752 bytes --]

When processing remote options, we split the option line into two by
searching for a space. If there is one, we replace the space with '\0',
otherwise we implicitly assume that the value is "true" and thus assign
a string constant.

As the return value of strchr(3P) weirdly enough is a `char *` even
though it gets a `const char *` as input, the assigned-to variable also
is a non-constant. This is fine though because the argument is in fact
an allocated string, and thus we are allowed to modify it. But this will
break once we enable `-Wwrite-strings`.

Refactor the code stop splitting the fields with '\0' altogether.
Instead, we can pass the length of the option name to `set_option()` and
then use strncmp(3P) instead of strcmp(3P).

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c | 53 ++++++++++++++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index cae98384da..d0f767df8e 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -58,9 +58,9 @@ struct options {
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
 
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
 {
-	if (!strcmp(name, "verbosity")) {
+	if (!strncmp(name, "verbosity", namelen)) {
 		char *end;
 		int v = strtol(value, &end, 10);
 		if (value == end || *end)
@@ -68,7 +68,7 @@ static int set_option(const char *name, const char *value)
 		options.verbosity = v;
 		return 0;
 	}
-	else if (!strcmp(name, "progress")) {
+	else if (!strncmp(name, "progress", namelen)) {
 		if (!strcmp(value, "true"))
 			options.progress = 1;
 		else if (!strcmp(value, "false"))
@@ -77,7 +77,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "depth")) {
+	else if (!strncmp(name, "depth", namelen)) {
 		char *end;
 		unsigned long v = strtoul(value, &end, 10);
 		if (value == end || *end)
@@ -85,15 +85,15 @@ static int set_option(const char *name, const char *value)
 		options.depth = v;
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-since")) {
+	else if (!strncmp(name, "deepen-since", namelen)) {
 		options.deepen_since = xstrdup(value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-not")) {
+	else if (!strncmp(name, "deepen-not", namelen)) {
 		string_list_append(&options.deepen_not, value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-relative")) {
+	else if (!strncmp(name, "deepen-relative", namelen)) {
 		if (!strcmp(value, "true"))
 			options.deepen_relative = 1;
 		else if (!strcmp(value, "false"))
@@ -102,7 +102,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "followtags")) {
+	else if (!strncmp(name, "followtags", namelen)) {
 		if (!strcmp(value, "true"))
 			options.followtags = 1;
 		else if (!strcmp(value, "false"))
@@ -111,7 +111,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "dry-run")) {
+	else if (!strncmp(name, "dry-run", namelen)) {
 		if (!strcmp(value, "true"))
 			options.dry_run = 1;
 		else if (!strcmp(value, "false"))
@@ -120,7 +120,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "check-connectivity")) {
+	else if (!strncmp(name, "check-connectivity", namelen)) {
 		if (!strcmp(value, "true"))
 			options.check_self_contained_and_connected = 1;
 		else if (!strcmp(value, "false"))
@@ -129,7 +129,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "cas")) {
+	else if (!strncmp(name, "cas", namelen)) {
 		struct strbuf val = STRBUF_INIT;
 		strbuf_addstr(&val, "--force-with-lease=");
 		if (*value != '"')
@@ -139,7 +139,7 @@ static int set_option(const char *name, const char *value)
 		string_list_append(&cas_options, val.buf);
 		strbuf_release(&val);
 		return 0;
-	} else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+	} else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
 		if (!strcmp(value, "true"))
 			options.force_if_includes = 1;
 		else if (!strcmp(value, "false"))
@@ -147,7 +147,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "cloning")) {
+	} else if (!strncmp(name, "cloning", namelen)) {
 		if (!strcmp(value, "true"))
 			options.cloning = 1;
 		else if (!strcmp(value, "false"))
@@ -155,7 +155,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "update-shallow")) {
+	} else if (!strncmp(name, "update-shallow", namelen)) {
 		if (!strcmp(value, "true"))
 			options.update_shallow = 1;
 		else if (!strcmp(value, "false"))
@@ -163,7 +163,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "pushcert")) {
+	} else if (!strncmp(name, "pushcert", namelen)) {
 		if (!strcmp(value, "true"))
 			options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
 		else if (!strcmp(value, "false"))
@@ -173,7 +173,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "atomic")) {
+	} else if (!strncmp(name, "atomic", namelen)) {
 		if (!strcmp(value, "true"))
 			options.atomic = 1;
 		else if (!strcmp(value, "false"))
@@ -181,7 +181,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "push-option")) {
+	} else if (!strncmp(name, "push-option", namelen)) {
 		if (*value != '"')
 			string_list_append(&options.push_options, value);
 		else {
@@ -192,7 +192,7 @@ static int set_option(const char *name, const char *value)
 						 strbuf_detach(&unquoted, NULL));
 		}
 		return 0;
-	} else if (!strcmp(name, "family")) {
+	} else if (!strncmp(name, "family", namelen)) {
 		if (!strcmp(value, "ipv4"))
 			git_curl_ipresolve = CURL_IPRESOLVE_V4;
 		else if (!strcmp(value, "ipv6"))
@@ -202,16 +202,16 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "from-promisor")) {
+	} else if (!strncmp(name, "from-promisor", namelen)) {
 		options.from_promisor = 1;
 		return 0;
-	} else if (!strcmp(name, "refetch")) {
+	} else if (!strncmp(name, "refetch", namelen)) {
 		options.refetch = 1;
 		return 0;
-	} else if (!strcmp(name, "filter")) {
+	} else if (!strncmp(name, "filter", namelen)) {
 		options.filter = xstrdup(value);
 		return 0;
-	} else if (!strcmp(name, "object-format")) {
+	} else if (!strncmp(name, "object-format", namelen)) {
 		options.object_format = 1;
 		if (strcmp(value, "true"))
 			die(_("unknown value for object-format: %s"), value);
@@ -1588,15 +1588,16 @@ int cmd_main(int argc, const char **argv)
 			parse_push(&buf);
 
 		} else if (skip_prefix(buf.buf, "option ", &arg)) {
-			char *value = strchr(arg, ' ');
+			const char *value = strchrnul(arg, ' ');
+			size_t arglen = value - arg;
 			int result;
 
-			if (value)
-				*value++ = '\0';
+			if (*value)
+				value++; /* skip over SP */
 			else
 				value = "true";
 
-			result = set_option(arg, value);
+			result = set_option(arg, arglen, value);
 			if (!result)
 				printf("ok\n");
 			else if (result < 0)
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 20/27] revision: always store allocated strings in output encoding
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (18 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
                     ` (6 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2531 bytes --]

The `git_log_output_encoding` variable can be set via the `--encoding=`
option. When doing so, we conditionally either assign it to the passed
value, or if the value is "none" we assign it the empty string.
Depending on which of the both code paths we pick though, the variable
may end up being assigned either an allocated string or a string
constant.

This is somewhat risky and may easily lead to bugs when a different code
path may want to reassign a new value to it, freeing the previous value.
We already to this when parsing the "i18n.logoutputencoding" config in
`git_default_i18n_config()`. But because the config is typically parsed
before we parse command line options this has been fine so far.

Regardless of that, safeguard the code such that the variable always
contains an allocated string. While at it, also free the old value in
case there was any to plug a potential memory leak.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 revision.c             | 3 ++-
 t/t3900-i18n-commit.sh | 1 +
 t/t3901-i18n-patch.sh  | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 7ddf0f151a..2ee6886078 100644
--- a/revision.c
+++ b/revision.c
@@ -2650,10 +2650,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		free(git_log_output_encoding);
 		if (strcmp(optarg, "none"))
 			git_log_output_encoding = xstrdup(optarg);
 		else
-			git_log_output_encoding = "";
+			git_log_output_encoding = xstrdup("");
 		return argcount;
 	} else if (!strcmp(arg, "--reverse")) {
 		revs->reverse ^= 1;
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09cfd9..db7b403bc1 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
 
 test_description='commit and log output encodings'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78829..5f0b9afc3f 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@ test_description='i18n settings and format-patch | am pipe'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_encoding () {
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 21/27] mailmap: always store allocated strings in mailmap blob
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (19 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
                     ` (5 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Same as with the preceding commit, the `git_mailmap_blob` may sometimes
contain an allocated string and sometimes it may contain a string
constant. This is risky and can easily lead to bugs in case the variable
is getting re-assigned, where the code may then try to free the previous
value to avoid memory leaks.

Safeguard the code by always storing allocated strings in the variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 mailmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mailmap.c b/mailmap.c
index b2efe29b3d..3d1e092fef 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -216,7 +216,7 @@ int read_mailmap(struct string_list *map)
 	map->cmp = namemap_cmp;
 
 	if (!git_mailmap_blob && is_bare_repository())
-		git_mailmap_blob = "HEAD:.mailmap";
+		git_mailmap_blob = xstrdup("HEAD:.mailmap");
 
 	if (!startup_info->have_repository || !is_bare_repository())
 		err |= read_mailmap_file(map, ".mailmap",
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 22/27] imap-send: drop global `imap_server_conf` variable
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (20 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
                     ` (4 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 7200 bytes --]

In "imap-send.c", we have a global `sturct imap_server_conf` variable
that keeps track of the configuration of the IMAP server. This variable
is being populated mostly via the Git configuration.

Refactor the code to allocate the structure on the stack instead of
having it globally. This change allows us to track its lifetime more
closely.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 57 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 8b723b34a5..67a7a6c456 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -82,10 +82,6 @@ struct imap_server_conf {
 	char *auth_method;
 };
 
-static struct imap_server_conf server = {
-	.ssl_verify = 1,
-};
-
 struct imap_socket {
 	int fd[2];
 	SSL *ssl;
@@ -110,6 +106,7 @@ struct imap {
 };
 
 struct imap_store {
+	const struct imap_server_conf *cfg;
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	int uidvalidity;
@@ -194,8 +191,8 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
 
 #ifdef NO_OPENSSL
 static int ssl_socket_connect(struct imap_socket *sock UNUSED,
-			      int use_tls_only UNUSED,
-			      int verify UNUSED)
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only UNUSED)
 {
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
@@ -250,7 +247,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
 		     cname, hostname);
 }
 
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock,
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 	const SSL_METHOD *meth;
@@ -279,7 +278,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	if (use_tls_only)
 		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
-	if (verify)
+	if (cfg->ssl_verify)
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
 
 	if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +305,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	 * OpenSSL does not document this function, but the implementation
 	 * returns 1 on success, 0 on failure after calling SSLerr().
 	 */
-	ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+	ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
 	if (ret != 1)
-		warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+		warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
 #endif
 
 	ret = SSL_connect(sock->ssl);
@@ -317,12 +316,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 		return -1;
 	}
 
-	if (verify) {
+	if (cfg->ssl_verify) {
 		/* make sure the hostname matches that of the certificate */
 		cert = SSL_get_peer_certificate(sock->ssl);
 		if (!cert)
 			return error("unable to get peer certificate.");
-		if (verify_hostname(cert, server.host) < 0)
+		if (verify_hostname(cert, cfg->host) < 0)
 			return -1;
 	}
 
@@ -895,7 +894,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 	int ret;
 	char *response;
 
-	response = cram(prompt, server.user, server.pass);
+	response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
 
 	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
 	if (ret != strlen(response))
@@ -935,6 +934,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 
 	CALLOC_ARRAY(ctx, 1);
 
+	ctx->cfg = srvc;
 	ctx->imap = CALLOC_ARRAY(imap, 1);
 	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
 	imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1035,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		imap->buf.sock.fd[1] = dup(s);
 
 		if (srvc->use_ssl &&
-		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+		    ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
 			close(s);
 			goto bail;
 		}
@@ -1068,8 +1068,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		if (!srvc->use_ssl && CAP(STARTTLS)) {
 			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
 				goto bail;
-			if (ssl_socket_connect(&imap->buf.sock, 1,
-					       srvc->ssl_verify))
+			if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
 				goto bail;
 			/* capabilities may have changed, so get the new capabilities */
 			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1299,23 +1298,24 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 static int git_imap_config(const char *var, const char *val,
 			   const struct config_context *ctx, void *cb)
 {
+	struct imap_server_conf *cfg = cb;
 
 	if (!strcmp("imap.sslverify", var))
-		server.ssl_verify = git_config_bool(var, val);
+		cfg->ssl_verify = git_config_bool(var, val);
 	else if (!strcmp("imap.preformattedhtml", var))
-		server.use_html = git_config_bool(var, val);
+		cfg->use_html = git_config_bool(var, val);
 	else if (!strcmp("imap.folder", var))
-		return git_config_string(&server.folder, var, val);
+		return git_config_string(&cfg->folder, var, val);
 	else if (!strcmp("imap.user", var))
-		return git_config_string(&server.user, var, val);
+		return git_config_string(&cfg->user, var, val);
 	else if (!strcmp("imap.pass", var))
-		return git_config_string(&server.pass, var, val);
+		return git_config_string(&cfg->pass, var, val);
 	else if (!strcmp("imap.tunnel", var))
-		return git_config_string(&server.tunnel, var, val);
+		return git_config_string(&cfg->tunnel, var, val);
 	else if (!strcmp("imap.authmethod", var))
-		return git_config_string(&server.auth_method, var, val);
+		return git_config_string(&cfg->auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val, ctx->kvi);
+		cfg->port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
@@ -1324,11 +1324,11 @@ static int git_imap_config(const char *var, const char *val,
 				val += 5;
 			else if (starts_with(val, "imaps:")) {
 				val += 6;
-				server.use_ssl = 1;
+				cfg->use_ssl = 1;
 			}
 			if (starts_with(val, "//"))
 				val += 2;
-			server.host = xstrdup(val);
+			cfg->host = xstrdup(val);
 		}
 	} else
 		return git_default_config(var, val, ctx, cb);
@@ -1497,12 +1497,15 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
 
 int cmd_main(int argc, const char **argv)
 {
+	struct imap_server_conf server = {
+		.ssl_verify = 1,
+	};
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
 
 	setup_git_directory_gently(&nongit_ok);
-	git_config(git_imap_config, NULL);
+	git_config(git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
 
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 23/27] imap-send: fix leaking memory in `imap_server_conf`
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (21 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:38   ` [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
                     ` (3 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 4324 bytes --]

We never free any of the config strings that we populate into the
`struct imap_server_conf`. Fix this by creating a common exit path where
we can free resources.

While at it, drop the unused member `imap_server_conf::name`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 65 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 23 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 67a7a6c456..da3e7ec17e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,7 +69,6 @@ static void imap_warn(const char *, ...);
 static char *next_arg(char **);
 
 struct imap_server_conf {
-	const char *name;
 	char *tunnel;
 	char *host;
 	int port;
@@ -1300,23 +1299,28 @@ static int git_imap_config(const char *var, const char *val,
 {
 	struct imap_server_conf *cfg = cb;
 
-	if (!strcmp("imap.sslverify", var))
+	if (!strcmp("imap.sslverify", var)) {
 		cfg->ssl_verify = git_config_bool(var, val);
-	else if (!strcmp("imap.preformattedhtml", var))
+	} else if (!strcmp("imap.preformattedhtml", var)) {
 		cfg->use_html = git_config_bool(var, val);
-	else if (!strcmp("imap.folder", var))
+	} else if (!strcmp("imap.folder", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->folder, var, val);
-	else if (!strcmp("imap.user", var))
+	} else if (!strcmp("imap.user", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->user, var, val);
-	else if (!strcmp("imap.pass", var))
+	} else if (!strcmp("imap.pass", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->pass, var, val);
-	else if (!strcmp("imap.tunnel", var))
+	} else if (!strcmp("imap.tunnel", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->tunnel, var, val);
-	else if (!strcmp("imap.authmethod", var))
+	} else if (!strcmp("imap.authmethod", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->auth_method, var, val);
-	else if (!strcmp("imap.port", var))
+	} else if (!strcmp("imap.port", var)) {
 		cfg->port = git_config_int(var, val, ctx->kvi);
-	else if (!strcmp("imap.host", var)) {
+	} else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
 		} else {
@@ -1330,8 +1334,9 @@ static int git_imap_config(const char *var, const char *val,
 				val += 2;
 			cfg->host = xstrdup(val);
 		}
-	} else
+	} else {
 		return git_default_config(var, val, ctx, cb);
+	}
 
 	return 0;
 }
@@ -1503,6 +1508,7 @@ int cmd_main(int argc, const char **argv)
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
+	int ret;
 
 	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, &server);
@@ -1529,42 +1535,55 @@ int cmd_main(int argc, const char **argv)
 
 	if (!server.folder) {
 		fprintf(stderr, "no imap store specified\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
 			fprintf(stderr, "no imap host specified\n");
-			return 1;
+			ret = 1;
+			goto out;
 		}
-		server.host = "tunnel";
+		server.host = xstrdup("tunnel");
 	}
 
 	/* read the messages */
 	if (strbuf_read(&all_msgs, 0, 0) < 0) {
 		error_errno(_("could not read from stdin"));
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	if (all_msgs.len == 0) {
 		fprintf(stderr, "nothing to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	total = count_messages(&all_msgs);
 	if (!total) {
 		fprintf(stderr, "no messages to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	/* write it to the imap server */
 
 	if (server.tunnel)
-		return append_msgs_to_imap(&server, &all_msgs, total);
-
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
 #ifdef USE_CURL_FOR_IMAP_SEND
-	if (use_curl)
-		return curl_append_msgs_to_imap(&server, &all_msgs, total);
+	else if (use_curl)
+		ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
 #endif
-
-	return append_msgs_to_imap(&server, &all_msgs, total);
+	else
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
+
+out:
+	free(server.tunnel);
+	free(server.host);
+	free(server.folder);
+	free(server.user);
+	free(server.pass);
+	free(server.auth_method);
+	return ret;
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (22 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 14:06     ` Phillip Wood
  2024-06-04 12:38   ` [PATCH v4 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
                     ` (2 subsequent siblings)
  26 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 4136 bytes --]

The `struct rebase_options::default_backend` field is a non-constant
string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
Refactor the code to initialize and release options via two functions
`rebase_options_init()` and `rebase_options_release()`. Like this, we
can easily adapt the former funnction to use `xstrdup()` on the default
value without hiding it away in a macro.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 67 ++++++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 28 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 14d4f0a5e6..11f276012c 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -131,25 +131,40 @@ struct rebase_options {
 	int config_update_refs;
 };
 
-#define REBASE_OPTIONS_INIT {			  	\
-		.type = REBASE_UNSPECIFIED,	  	\
-		.empty = EMPTY_UNSPECIFIED,	  	\
-		.keep_empty = 1,			\
-		.default_backend = "merge",	  	\
-		.flags = REBASE_NO_QUIET, 		\
-		.git_am_opts = STRVEC_INIT,		\
-		.exec = STRING_LIST_INIT_NODUP,		\
-		.git_format_patch_opt = STRBUF_INIT,	\
-		.fork_point = -1,			\
-		.reapply_cherry_picks = -1,             \
-		.allow_empty_message = 1,               \
-		.autosquash = -1,                       \
-		.rebase_merges = -1,                    \
-		.config_rebase_merges = -1,             \
-		.update_refs = -1,                      \
-		.config_update_refs = -1,               \
-		.strategy_opts = STRING_LIST_INIT_NODUP,\
-	}
+static void rebase_options_init(struct rebase_options *opts)
+{
+	memset(opts, 0, sizeof(*opts));
+	opts->type = REBASE_UNSPECIFIED;
+	opts->empty = EMPTY_UNSPECIFIED;
+	opts->default_backend = xstrdup("merge");
+	opts->keep_empty = 1;
+	opts->flags = REBASE_NO_QUIET;
+	strvec_init(&opts->git_am_opts);
+	string_list_init_nodup(&opts->exec);
+	strbuf_init(&opts->git_format_patch_opt, 0);
+	opts->fork_point = -1;
+	opts->reapply_cherry_picks = -1;
+	opts->allow_empty_message = 1;
+	opts->autosquash = -1;
+	opts->rebase_merges = -1;
+	opts->config_rebase_merges = -1;
+	opts->update_refs = -1;
+	opts->config_update_refs = -1;
+	string_list_init_nodup(&opts->strategy_opts);
+}
+
+static void rebase_options_release(struct rebase_options *opts)
+{
+	free(opts->default_backend);
+	free(opts->reflog_action);
+	free(opts->head_name);
+	strvec_clear(&opts->git_am_opts);
+	free(opts->gpg_sign_opt);
+	string_list_clear(&opts->exec, 0);
+	free(opts->strategy);
+	string_list_clear(&opts->strategy_opts, 0);
+	strbuf_release(&opts->git_format_patch_opt);
+}
 
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 {
@@ -796,6 +811,7 @@ static int rebase_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "rebase.backend")) {
+		FREE_AND_NULL(opts->default_backend);
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
@@ -1045,7 +1061,7 @@ static int check_exec_cmd(const char *cmd)
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
-	struct rebase_options options = REBASE_OPTIONS_INIT;
+	struct rebase_options options;
 	const char *branch_name;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
@@ -1178,6 +1194,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 	};
 	int i;
 
+	rebase_options_init(&options);
+
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_rebase_usage,
 				   builtin_rebase_options);
@@ -1833,14 +1851,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 cleanup:
 	strbuf_release(&buf);
 	strbuf_release(&revisions);
-	free(options.reflog_action);
-	free(options.head_name);
-	strvec_clear(&options.git_am_opts);
-	free(options.gpg_sign_opt);
-	string_list_clear(&options.exec, 0);
-	free(options.strategy);
-	string_list_clear(&options.strategy_opts, 0);
-	strbuf_release(&options.git_format_patch_opt);
+	rebase_options_release(&options);
 	free(squash_onto_name);
 	free(keep_base_onto_name);
 	return !!ret;
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 25/27] builtin/rebase: always store allocated string in `options.strategy`
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (23 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 14:10     ` Phillip Wood
  2024-06-04 12:38   ` [PATCH v4 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
  2024-06-04 12:39   ` [PATCH v4 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
  26 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2338 bytes --]

The `struct rebase_options::strategy` field is a `char *`, but we do end
up assigning string constants to it in two cases:

  - When being passed a `--strategy=` option via the command line.

  - When being passed a strategy option via `--strategy-option=`, but
    not a strategy.

This will cause warnings once we enable `-Wwrite-strings`.

Ideally, we'd just convert the field to be a `const char *`. But we also
assign to this field via the GIT_TEST_MERGE_ALGORITHM envvar, which we
have to strdup(3P) into it.

Instead, refactor the code to make sure that we only ever assign
allocated strings to this field.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 11f276012c..26068cf542 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1063,6 +1063,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options;
 	const char *branch_name;
+	const char *strategy_opt = NULL;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
 	int ok_to_skip_pre_rebase = 0;
@@ -1177,7 +1178,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
-		OPT_STRING('s', "strategy", &options.strategy,
+		OPT_STRING('s', "strategy", &strategy_opt,
 			   N_("strategy"), N_("use the given merge strategy")),
 		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
 				N_("option"),
@@ -1488,13 +1489,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	if (options.strategy_opts.nr && !options.strategy)
-		options.strategy = "ort";
-
-	if (options.strategy) {
-		options.strategy = xstrdup(options.strategy);
+	if (strategy_opt)
+		options.strategy = xstrdup(strategy_opt);
+	else if (options.strategy_opts.nr && !options.strategy)
+		options.strategy = xstrdup("ort");
+	if (options.strategy)
 		imply_merge(&options, "--strategy");
-	}
 
 	if (options.root && !options.onto_name)
 		imply_merge(&options, "--root without --onto");
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 26/27] builtin/merge: always store allocated strings in `pull_twohead`
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (24 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
@ 2024-06-04 12:38   ` Patrick Steinhardt
  2024-06-04 12:39   ` [PATCH v4 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2451 bytes --]

The `pull_twohead` configuration may sometimes contain an allocated
string, and sometimes it may contain a string constant. Refactor this to
instead always store an allocated string such that we can release its
resources without risk.

While at it, manage the lifetime of other config strings, as well. Note
that we explicitly don't free `cleanup_arg` here. This is because the
variable may be assigned a string constant via command line options.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/merge.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/builtin/merge.c b/builtin/merge.c
index daed2d4e1e..fb3eb15b89 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -611,17 +611,19 @@ static int git_merge_config(const char *k, const char *v,
 		return 0;
 	}
 
-	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
 		show_diffstat = git_config_bool(k, v);
-	else if (!strcmp(k, "merge.verifysignatures"))
+	} else if (!strcmp(k, "merge.verifysignatures")) {
 		verify_signatures = git_config_bool(k, v);
-	else if (!strcmp(k, "pull.twohead"))
+	} else if (!strcmp(k, "pull.twohead")) {
+		FREE_AND_NULL(pull_twohead);
 		return git_config_string(&pull_twohead, k, v);
-	else if (!strcmp(k, "pull.octopus"))
+	} else if (!strcmp(k, "pull.octopus")) {
+		FREE_AND_NULL(pull_octopus);
 		return git_config_string(&pull_octopus, k, v);
-	else if (!strcmp(k, "commit.cleanup"))
+	} else if (!strcmp(k, "commit.cleanup")) {
 		return git_config_string(&cleanup_arg, k, v);
-	else if (!strcmp(k, "merge.ff")) {
+	} else if (!strcmp(k, "merge.ff")) {
 		int boolval = git_parse_maybe_bool(v);
 		if (0 <= boolval) {
 			fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -1294,7 +1296,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (!pull_twohead) {
 		char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
 		if (default_strategy && !strcmp(default_strategy, "ort"))
-			pull_twohead = "ort";
+			pull_twohead = xstrdup("ort");
 	}
 
 	init_diff_ui_defaults();
@@ -1793,6 +1795,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
+	free(pull_twohead);
+	free(pull_octopus);
 	discard_index(the_repository->index);
 	return ret;
 }
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v4 27/27] config.mak.dev: enable `-Wwrite-strings` warning
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
                     ` (25 preceding siblings ...)
  2024-06-04 12:38   ` [PATCH v4 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
@ 2024-06-04 12:39   ` Patrick Steinhardt
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-04 12:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

Writing to string constants is undefined behaviour and must be avoided
in C. Even so, the compiler does not help us with this by default
because those constants are not in fact marked as `const`. This makes it
rather easy to accidentally assign a constant to a non-const variable or
field and then later on try to either free it or write to it.

Enable `-Wwrite-strings` to catch such mistakes. With this warning
enabled, the type of string constants is changed to `const char[]` and
will thus cause compiler warnings when being assigned to non-const
fields and variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.mak.dev | 1 +
 1 file changed, 1 insertion(+)

diff --git a/config.mak.dev b/config.mak.dev
index 981304727c..1ce4c70613 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -37,6 +37,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
 DEVELOPER_CFLAGS += -Wstrict-prototypes
 DEVELOPER_CFLAGS += -Wunused
 DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
 DEVELOPER_CFLAGS += -fno-common
 
 ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
-- 
2.45.1.410.g58bac47f8e.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-04 12:38   ` [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
@ 2024-06-04 14:06     ` Phillip Wood
  2024-06-05  5:40       ` Patrick Steinhardt
  0 siblings, 1 reply; 205+ messages in thread
From: Phillip Wood @ 2024-06-04 14:06 UTC (permalink / raw)
  To: Patrick Steinhardt, git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

Hi Patrick

On 04/06/2024 13:38, Patrick Steinhardt wrote:
> The `struct rebase_options::default_backend` field is a non-constant
> string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
> Refactor the code to initialize and release options via two functions
> `rebase_options_init()` and `rebase_options_release()`. Like this, we
> can easily adapt the former funnction to use `xstrdup()` on the default
> value without hiding it away in a macro.

Personally I'd be happy with

-		.default_backend = "merge",		\
+		.default_backend = xstrdup("merge"),	\

rather than adding an init function. I do agree that adding 
rebase_options_release() is a good idea and the rest of the changes look 
good to me

Thanks

Phillip

> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>   builtin/rebase.c | 67 ++++++++++++++++++++++++++++--------------------
>   1 file changed, 39 insertions(+), 28 deletions(-)
> 
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 14d4f0a5e6..11f276012c 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -131,25 +131,40 @@ struct rebase_options {
>   	int config_update_refs;
>   };
>   
> -#define REBASE_OPTIONS_INIT {			  	\
> -		.type = REBASE_UNSPECIFIED,	  	\
> -		.empty = EMPTY_UNSPECIFIED,	  	\
> -		.keep_empty = 1,			\
> -		.default_backend = "merge",	  	\
> -		.flags = REBASE_NO_QUIET, 		\
> -		.git_am_opts = STRVEC_INIT,		\
> -		.exec = STRING_LIST_INIT_NODUP,		\
> -		.git_format_patch_opt = STRBUF_INIT,	\
> -		.fork_point = -1,			\
> -		.reapply_cherry_picks = -1,             \
> -		.allow_empty_message = 1,               \
> -		.autosquash = -1,                       \
> -		.rebase_merges = -1,                    \
> -		.config_rebase_merges = -1,             \
> -		.update_refs = -1,                      \
> -		.config_update_refs = -1,               \
> -		.strategy_opts = STRING_LIST_INIT_NODUP,\
> -	}
> +static void rebase_options_init(struct rebase_options *opts)
> +{
> +	memset(opts, 0, sizeof(*opts));
> +	opts->type = REBASE_UNSPECIFIED;
> +	opts->empty = EMPTY_UNSPECIFIED;
> +	opts->default_backend = xstrdup("merge");
> +	opts->keep_empty = 1;
> +	opts->flags = REBASE_NO_QUIET;
> +	strvec_init(&opts->git_am_opts);
> +	string_list_init_nodup(&opts->exec);
> +	strbuf_init(&opts->git_format_patch_opt, 0);
> +	opts->fork_point = -1;
> +	opts->reapply_cherry_picks = -1;
> +	opts->allow_empty_message = 1;
> +	opts->autosquash = -1;
> +	opts->rebase_merges = -1;
> +	opts->config_rebase_merges = -1;
> +	opts->update_refs = -1;
> +	opts->config_update_refs = -1;
> +	string_list_init_nodup(&opts->strategy_opts);
> +}
> +
> +static void rebase_options_release(struct rebase_options *opts)
> +{
> +	free(opts->default_backend);
> +	free(opts->reflog_action);
> +	free(opts->head_name);
> +	strvec_clear(&opts->git_am_opts);
> +	free(opts->gpg_sign_opt);
> +	string_list_clear(&opts->exec, 0);
> +	free(opts->strategy);
> +	string_list_clear(&opts->strategy_opts, 0);
> +	strbuf_release(&opts->git_format_patch_opt);
> +}
>   
>   static struct replay_opts get_replay_opts(const struct rebase_options *opts)
>   {
> @@ -796,6 +811,7 @@ static int rebase_config(const char *var, const char *value,
>   	}
>   
>   	if (!strcmp(var, "rebase.backend")) {
> +		FREE_AND_NULL(opts->default_backend);
>   		return git_config_string(&opts->default_backend, var, value);
>   	}
>   
> @@ -1045,7 +1061,7 @@ static int check_exec_cmd(const char *cmd)
>   
>   int cmd_rebase(int argc, const char **argv, const char *prefix)
>   {
> -	struct rebase_options options = REBASE_OPTIONS_INIT;
> +	struct rebase_options options;
>   	const char *branch_name;
>   	int ret, flags, total_argc, in_progress = 0;
>   	int keep_base = 0;
> @@ -1178,6 +1194,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   	};
>   	int i;
>   
> +	rebase_options_init(&options);
> +
>   	if (argc == 2 && !strcmp(argv[1], "-h"))
>   		usage_with_options(builtin_rebase_usage,
>   				   builtin_rebase_options);
> @@ -1833,14 +1851,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   cleanup:
>   	strbuf_release(&buf);
>   	strbuf_release(&revisions);
> -	free(options.reflog_action);
> -	free(options.head_name);
> -	strvec_clear(&options.git_am_opts);
> -	free(options.gpg_sign_opt);
> -	string_list_clear(&options.exec, 0);
> -	free(options.strategy);
> -	string_list_clear(&options.strategy_opts, 0);
> -	strbuf_release(&options.git_format_patch_opt);
> +	rebase_options_release(&options);
>   	free(squash_onto_name);
>   	free(keep_base_onto_name);
>   	return !!ret;

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 25/27] builtin/rebase: always store allocated string in `options.strategy`
  2024-06-04 12:38   ` [PATCH v4 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
@ 2024-06-04 14:10     ` Phillip Wood
  0 siblings, 0 replies; 205+ messages in thread
From: Phillip Wood @ 2024-06-04 14:10 UTC (permalink / raw)
  To: Patrick Steinhardt, git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

Hi Patrick

On 04/06/2024 13:38, Patrick Steinhardt wrote:
> The `struct rebase_options::strategy` field is a `char *`, but we do end
> up assigning string constants to it in two cases:
> 
>    - When being passed a `--strategy=` option via the command line.
> 
>    - When being passed a strategy option via `--strategy-option=`, but
>      not a strategy.
> 
> This will cause warnings once we enable `-Wwrite-strings`.
> 
> Ideally, we'd just convert the field to be a `const char *`. But we also
> assign to this field via the GIT_TEST_MERGE_ALGORITHM envvar, which we
> have to strdup(3P) into it.
> 
> Instead, refactor the code to make sure that we only ever assign
> allocated strings to this field.

This looks sensible

Thanks

Phillip

> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>   builtin/rebase.c | 14 +++++++-------
>   1 file changed, 7 insertions(+), 7 deletions(-)
> 
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 11f276012c..26068cf542 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -1063,6 +1063,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   {
>   	struct rebase_options options;
>   	const char *branch_name;
> +	const char *strategy_opt = NULL;
>   	int ret, flags, total_argc, in_progress = 0;
>   	int keep_base = 0;
>   	int ok_to_skip_pre_rebase = 0;
> @@ -1177,7 +1178,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
>   		OPT_BOOL(0, "fork-point", &options.fork_point,
>   			 N_("use 'merge-base --fork-point' to refine upstream")),
> -		OPT_STRING('s', "strategy", &options.strategy,
> +		OPT_STRING('s', "strategy", &strategy_opt,
>   			   N_("strategy"), N_("use the given merge strategy")),
>   		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
>   				N_("option"),
> @@ -1488,13 +1489,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   		}
>   	}
>   
> -	if (options.strategy_opts.nr && !options.strategy)
> -		options.strategy = "ort";
> -
> -	if (options.strategy) {
> -		options.strategy = xstrdup(options.strategy);
> +	if (strategy_opt)
> +		options.strategy = xstrdup(strategy_opt);
> +	else if (options.strategy_opts.nr && !options.strategy)
> +		options.strategy = xstrdup("ort");
> +	if (options.strategy)
>   		imply_merge(&options, "--strategy");
> -	}
>   
>   	if (options.root && !options.onto_name)
>   		imply_merge(&options, "--root without --onto");

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-04 14:06     ` Phillip Wood
@ 2024-06-05  5:40       ` Patrick Steinhardt
  2024-06-05 13:06         ` Phillip Wood
  2024-06-05 16:11         ` Junio C Hamano
  0 siblings, 2 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-05  5:40 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1230 bytes --]

On Tue, Jun 04, 2024 at 03:06:38PM +0100, Phillip Wood wrote:
> Hi Patrick
> 
> On 04/06/2024 13:38, Patrick Steinhardt wrote:
> > The `struct rebase_options::default_backend` field is a non-constant
> > string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
> > Refactor the code to initialize and release options via two functions
> > `rebase_options_init()` and `rebase_options_release()`. Like this, we
> > can easily adapt the former funnction to use `xstrdup()` on the default
> > value without hiding it away in a macro.
> 
> Personally I'd be happy with
> 
> -		.default_backend = "merge",		\
> +		.default_backend = xstrdup("merge"),	\
> 
> rather than adding an init function. I do agree that adding
> rebase_options_release() is a good idea and the rest of the changes look
> good to me

Do we have any other cases where we allocate inside of a `_INIT` style
macro? If so, I'd go with that precedence and just allocate inside of
the macro. But if we don't, then I think I'm leaning more towards the
way I did it in this patch.

Happy to be convinced otherwise, I don't really feel all that strongly
about this. I'm merely aiming for the interface wth least surprises.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-05-31 15:27           ` Junio C Hamano
@ 2024-06-05 10:46             ` Jeff King
  2024-06-05 17:13               ` Junio C Hamano
  2024-06-06 10:36               ` Patrick Steinhardt
  0 siblings, 2 replies; 205+ messages in thread
From: Jeff King @ 2024-06-05 10:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, git

On Fri, May 31, 2024 at 08:27:13AM -0700, Junio C Hamano wrote:

> I wonder if we can do something to separate these two concerns
> apart, using a trick similar to what we often use with an extra
> variable "to_free".  Doing so would bloat the refspec_item, but
> unlike the references themselves, there won't be thousands of them,
> so it may not be an issue, perhaps?

I had a similar thought while looking at this spot a while ago, so I dug
this attempt out of my stash. It's quite ugly, as you need to keep the
storage pointer and the const pointer in sync. Especially because
there's a lot of clever pointer indirection via match_name_with_pattern().

So I don't think it's the best way to refactor this code, but I present
it here for your amusement/disgust. This is on top of what you have
queued in ps/no-writable-strings.

---
 branch.c         |  6 +++---
 builtin/fetch.c  |  8 ++++----
 builtin/remote.c |  6 +++---
 checkout.c       | 13 +++++++------
 refspec.c        |  6 ++++--
 refspec.h        |  6 ++++--
 remote.c         | 30 +++++++++++++++++-------------
 transport.c      |  2 +-
 8 files changed, 43 insertions(+), 34 deletions(-)

diff --git a/branch.c b/branch.c
index df5d24fec6..e1585b10c2 100644
--- a/branch.c
+++ b/branch.c
@@ -38,7 +38,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 	if (!remote_find_tracking(remote, &tracking->spec)) {
 		switch (++tracking->matches) {
 		case 1:
-			string_list_append_nodup(tracking->srcs, tracking->spec.src);
+			string_list_append_nodup(tracking->srcs, tracking->spec.src_storage);
 			tracking->remote = remote->name;
 			break;
 		case 2:
@@ -47,7 +47,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 			/* fall through */
 		default:
 			string_list_append(&ftb->ambiguous_remotes, remote->name);
-			free(tracking->spec.src);
+			free(tracking->spec.src_storage);
 			string_list_clear(tracking->srcs, 0);
 		break;
 		}
@@ -489,7 +489,7 @@ static int check_tracking_branch(struct remote *remote, void *cb_data)
 	memset(&query, 0, sizeof(struct refspec_item));
 	query.dst = tracking_branch;
 	res = !remote_find_tracking(remote, &query);
-	free(query.src);
+	free(query.src_storage);
 	return res;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 06b60867f5..00a67c99ef 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -443,7 +443,7 @@ static void filter_prefetch_refspec(struct refspec *rs)
 
 	for (i = 0; i < rs->nr; i++) {
 		struct strbuf new_dst = STRBUF_INIT;
-		char *old_dst;
+		const char *old_dst;
 		const char *sub = NULL;
 
 		if (rs->items[i].negative)
@@ -454,8 +454,8 @@ static void filter_prefetch_refspec(struct refspec *rs)
 				 ref_namespace[NAMESPACE_TAGS].ref))) {
 			int j;
 
-			free(rs->items[i].src);
-			free(rs->items[i].dst);
+			free(rs->items[i].src_storage);
+			free(rs->items[i].dst_storage);
 
 			for (j = i + 1; j < rs->nr; j++) {
 				rs->items[j - 1] = rs->items[j];
@@ -481,7 +481,7 @@ static void filter_prefetch_refspec(struct refspec *rs)
 		rs->items[i].dst = strbuf_detach(&new_dst, NULL);
 		rs->items[i].force = 1;
 
-		free(old_dst);
+		free(rs->items[i].dst_storage);
 	}
 }
 
diff --git a/builtin/remote.c b/builtin/remote.c
index b44f580b8c..87b97f5ef1 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -496,8 +496,8 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 	struct refspec_item refspec = {
 		.force = 0,
 		.pattern = 1,
-		.src = (char *) "refs/heads/*",
-		.dst = (char *) "refs/heads/*",
+		.src = "refs/heads/*",
+		.dst = "refs/heads/*",
 	};
 
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
@@ -981,7 +981,7 @@ static int append_ref_to_tracked_list(const char *refname,
 		return 0;
 
 	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
+	refspec.dst = refname;
 	if (!remote_find_tracking(states->remote, &refspec))
 		string_list_append(&states->tracked, abbrev_branch(refspec.src));
 
diff --git a/checkout.c b/checkout.c
index cfaea4bd10..327ab5f6e3 100644
--- a/checkout.c
+++ b/checkout.c
@@ -8,7 +8,7 @@
 #include "strbuf.h"
 
 struct tracking_name_data {
-	/* const */ char *src_ref;
+	const char *src_ref;
 	char *dst_ref;
 	struct object_id *dst_oid;
 	int num_matches;
@@ -27,7 +27,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
 	query.src = cb->src_ref;
 	if (remote_find_tracking(remote, &query) ||
 	    repo_get_oid(the_repository, query.dst, cb->dst_oid)) {
-		free(query.dst);
+		free(query.dst_storage);
 		return 0;
 	}
 	cb->num_matches++;
@@ -38,10 +38,10 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
 		cb->default_dst_oid = dst;
 	}
 	if (cb->dst_ref) {
-		free(query.dst);
+		free(query.dst_storage);
 		return 0;
 	}
-	cb->dst_ref = query.dst;
+	cb->dst_ref = query.dst_storage;
 	return 0;
 }
 
@@ -50,14 +50,15 @@ char *unique_tracking_name(const char *name, struct object_id *oid,
 {
 	struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
 	const char *default_remote = NULL;
+	char *src_ref;
 	if (!git_config_get_string_tmp("checkout.defaultremote", &default_remote))
 		cb_data.default_remote = default_remote;
-	cb_data.src_ref = xstrfmt("refs/heads/%s", name);
+	cb_data.src_ref = src_ref = xstrfmt("refs/heads/%s", name);
 	cb_data.dst_oid = oid;
 	for_each_remote(check_tracking_name, &cb_data);
 	if (dwim_remotes_matched)
 		*dwim_remotes_matched = cb_data.num_matches;
-	free(cb_data.src_ref);
+	free(src_ref);
 	if (cb_data.num_matches == 1) {
 		free(cb_data.default_dst_ref);
 		free(cb_data.default_dst_oid);
diff --git a/refspec.c b/refspec.c
index 1df5de6c2f..a7473c9628 100644
--- a/refspec.c
+++ b/refspec.c
@@ -163,8 +163,10 @@ void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
 
 void refspec_item_clear(struct refspec_item *item)
 {
-	FREE_AND_NULL(item->src);
-	FREE_AND_NULL(item->dst);
+	FREE_AND_NULL(item->src_storage);
+	item->src = NULL;
+	FREE_AND_NULL(item->dst_storage);
+	item->dst = NULL;
 	item->force = 0;
 	item->pattern = 0;
 	item->matching = 0;
diff --git a/refspec.h b/refspec.h
index 754be45cee..fbc4352397 100644
--- a/refspec.h
+++ b/refspec.h
@@ -24,8 +24,10 @@ struct refspec_item {
 	unsigned exact_sha1 : 1;
 	unsigned negative : 1;
 
-	char *src;
-	char *dst;
+	const char *src;
+	const char *dst;
+	char *src_storage;
+	char *dst_storage;
 };
 
 #define REFSPEC_FETCH 1
diff --git a/remote.c b/remote.c
index d319f28757..5cdc3a17cc 100644
--- a/remote.c
+++ b/remote.c
@@ -827,7 +827,8 @@ int remote_has_url(struct remote *remote, const char *url)
 }
 
 static int match_name_with_pattern(const char *key, const char *name,
-				   const char *value, char **result)
+				   const char *value, char **result,
+				   const char **result_const)
 {
 	const char *kstar = strchr(key, '*');
 	size_t klen;
@@ -850,6 +851,8 @@ static int match_name_with_pattern(const char *key, const char *name,
 		strbuf_add(&sb, name + klen, namelen - klen - ksuffixlen);
 		strbuf_addstr(&sb, vstar + 1);
 		*result = strbuf_detach(&sb, NULL);
+		if (result_const)
+			*result_const = *result;
 	}
 	return ret;
 }
@@ -858,7 +861,7 @@ static int refspec_match(const struct refspec_item *refspec,
 			 const char *name)
 {
 	if (refspec->pattern)
-		return match_name_with_pattern(refspec->src, name, NULL, NULL);
+		return match_name_with_pattern(refspec->src, name, NULL, NULL, NULL);
 
 	return !strcmp(refspec->src, name);
 }
@@ -927,7 +930,7 @@ static int query_matches_negative_refspec(struct refspec *rs, struct refspec_ite
 			const char *key = refspec->dst ? refspec->dst : refspec->src;
 			const char *value = refspec->src;
 
-			if (match_name_with_pattern(key, needle, value, &expn_name))
+			if (match_name_with_pattern(key, needle, value, &expn_name, NULL))
 				string_list_append_nodup(&reversed, expn_name);
 		} else if (refspec->matching) {
 			/* For the special matching refspec, any query should match */
@@ -967,12 +970,12 @@ static void query_refspecs_multiple(struct refspec *rs,
 		const char *key = find_src ? refspec->dst : refspec->src;
 		const char *value = find_src ? refspec->src : refspec->dst;
 		const char *needle = find_src ? query->dst : query->src;
-		char **result = find_src ? &query->src : &query->dst;
+		char **result = find_src ? &query->src_storage : &query->dst_storage;
 
 		if (!refspec->dst || refspec->negative)
 			continue;
 		if (refspec->pattern) {
-			if (match_name_with_pattern(key, needle, value, result))
+			if (match_name_with_pattern(key, needle, value, result, NULL))
 				string_list_append_nodup(results, *result);
 		} else if (!strcmp(needle, key)) {
 			string_list_append(results, value);
@@ -985,7 +988,8 @@ int query_refspecs(struct refspec *rs, struct refspec_item *query)
 	int i;
 	int find_src = !query->src;
 	const char *needle = find_src ? query->dst : query->src;
-	char **result = find_src ? &query->src : &query->dst;
+	char **result = find_src ? &query->src_storage : &query->dst_storage;
+	const char **result_const = find_src ? &query->src : &query->dst;
 
 	if (find_src && !query->dst)
 		BUG("query_refspecs: need either src or dst");
@@ -1001,12 +1005,12 @@ int query_refspecs(struct refspec *rs, struct refspec_item *query)
 		if (!refspec->dst || refspec->negative)
 			continue;
 		if (refspec->pattern) {
-			if (match_name_with_pattern(key, needle, value, result)) {
+			if (match_name_with_pattern(key, needle, value, result, result_const)) {
 				query->force = refspec->force;
 				return 0;
 			}
 		} else if (!strcmp(needle, key)) {
-			*result = xstrdup(value);
+			*result_const = *result = xstrdup(value);
 			query->force = refspec->force;
 			return 0;
 		}
@@ -1019,12 +1023,12 @@ char *apply_refspecs(struct refspec *rs, const char *name)
 	struct refspec_item query;
 
 	memset(&query, 0, sizeof(struct refspec_item));
-	query.src = (char *)name;
+	query.src = name;
 
 	if (query_refspecs(rs, &query))
 		return NULL;
 
-	return query.dst;
+	return query.dst_storage;
 }
 
 int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)
@@ -1399,9 +1403,9 @@ static char *get_ref_match(const struct refspec *rs, const struct ref *ref,
 			const char *dst_side = item->dst ? item->dst : item->src;
 			int match;
 			if (direction == FROM_SRC)
-				match = match_name_with_pattern(item->src, ref->name, dst_side, &name);
+				match = match_name_with_pattern(item->src, ref->name, dst_side, &name, NULL);
 			else
-				match = match_name_with_pattern(dst_side, ref->name, item->src, &name);
+				match = match_name_with_pattern(dst_side, ref->name, item->src, &name, NULL);
 			if (match) {
 				matching_refs = i;
 				break;
@@ -2020,7 +2024,7 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
 		if (strchr(ref->name, '^'))
 			continue; /* a dereference item */
 		if (match_name_with_pattern(refspec->src, ref->name,
-					    refspec->dst, &expn_name) &&
+					    refspec->dst, &expn_name, NULL) &&
 		    !ignore_symref_update(expn_name, &scratch)) {
 			struct ref *cpy = copy_ref(ref);
 
diff --git a/transport.c b/transport.c
index 83ddea8fbc..0cedda425c 100644
--- a/transport.c
+++ b/transport.c
@@ -550,7 +550,7 @@ static void update_one_tracking_ref(struct remote *remote, char *refname,
 			refs_update_ref(get_main_ref_store(the_repository),
 					"update by push", rs.dst, new_oid,
 					NULL, 0, 0);
-		free(rs.dst);
+		free(rs.dst_storage);
 	}
 }
 

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-05  5:40       ` Patrick Steinhardt
@ 2024-06-05 13:06         ` Phillip Wood
  2024-06-06  9:50           ` Patrick Steinhardt
  2024-06-05 16:11         ` Junio C Hamano
  1 sibling, 1 reply; 205+ messages in thread
From: Phillip Wood @ 2024-06-05 13:06 UTC (permalink / raw)
  To: Patrick Steinhardt, phillip.wood
  Cc: git, Jeff King, Junio C Hamano, Eric Sunshine

On 05/06/2024 06:40, Patrick Steinhardt wrote:
> On Tue, Jun 04, 2024 at 03:06:38PM +0100, Phillip Wood wrote:

> Do we have any other cases where we allocate inside of a `_INIT` style
> macro? If so, I'd go with that precedence and just allocate inside of
> the macro. But if we don't, then I think I'm leaning more towards the
> way I did it in this patch.

I recently added an allocation to REPLAY_OPTS_INIT, I'm not sure if 
there are any others. In general code that does

	struct foo = FOO_INIT;

and does not call

	foo_release();

is asking for trouble so I don't feel that allocations in _INIT macros 
are generally a problem. The only reason not to have allocations in an 
_INIT macro is if it might be used to initialize a file scope or global 
variable but that's not the case here.

> Happy to be convinced otherwise, I don't really feel all that strongly
> about this. I'm merely aiming for the interface wth least surprises.

I'm not that fussed either, but I do prefer our _INIT macros over 
_init() functions as I think they're nicer to use and easier to write 
(no need to worry about memset() to zero out the struct).

Best Wishes

Phillip


^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-05  5:40       ` Patrick Steinhardt
  2024-06-05 13:06         ` Phillip Wood
@ 2024-06-05 16:11         ` Junio C Hamano
  1 sibling, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-06-05 16:11 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: phillip.wood, git, Jeff King, Eric Sunshine

Patrick Steinhardt <ps@pks.im> writes:

>> Personally I'd be happy with
>> 
>> -		.default_backend = "merge",		\
>> +		.default_backend = xstrdup("merge"),	\
>> 
>> rather than adding an init function. I do agree that adding
>> rebase_options_release() is a good idea and the rest of the changes look
>> good to me
>
> Do we have any other cases where we allocate inside of a `_INIT` style
> macro? If so, I'd go with that precedence and just allocate inside of
> the macro. But if we don't, then I think I'm leaning more towards the
> way I did it in this patch.
>
> Happy to be convinced otherwise, I don't really feel all that strongly
> about this. I'm merely aiming for the interface wth least surprises.

FWIW, I am OK either way.

Having _init() and _release() may look symmetrical and there _might_
be cases where an initialization with a simple _INIT macro may not
suffice and we must have _init() function.

But otherwise, especially with .designated_initializers, the _INIT
macro makes it slightly easier to read (but not all much for this
particular case, whose initial values have too many non-zero values
and pretty much everything must be spelled out).

Thanks.

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-06-05 10:46             ` Jeff King
@ 2024-06-05 17:13               ` Junio C Hamano
  2024-06-08 10:59                 ` Jeff King
  2024-06-06 10:36               ` Patrick Steinhardt
  1 sibling, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-06-05 17:13 UTC (permalink / raw)
  To: Jeff King; +Cc: Patrick Steinhardt, git

Jeff King <peff@peff.net> writes:

> On Fri, May 31, 2024 at 08:27:13AM -0700, Junio C Hamano wrote:
>
>> I wonder if we can do something to separate these two concerns
>> apart, using a trick similar to what we often use with an extra
>> variable "to_free".  Doing so would bloat the refspec_item, but
>> unlike the references themselves, there won't be thousands of them,
>> so it may not be an issue, perhaps?
>
> I had a similar thought while looking at this spot a while ago, so I dug
> this attempt out of my stash. It's quite ugly, as you need to keep the
> storage pointer and the const pointer in sync. Especially because
> there's a lot of clever pointer indirection via match_name_with_pattern().

Ah, true.  The patch itself does not look _too_ bad, but that may
simply be because the original is bad enough ;-)

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 12/27] object-file: mark cached object buffers as const
  2024-06-04 12:37   ` [PATCH v4 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
@ 2024-06-06  6:02     ` Junio C Hamano
  2024-06-06  6:10       ` Junio C Hamano
  0 siblings, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-06-06  6:02 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King, Eric Sunshine

Patrick Steinhardt <ps@pks.im> writes:

> The buffers of cached objects are never modified, but are still stored
> as a non-constant pointer. This will cause a compiler warning once we
> enable the `-Wwrite-strings` compiler warning as we assign an empty
> constant string when initializing the static `empty_tree` cached object.
>
> Convert the field to be constant. This requires us to shuffle around
> the code a bit because we memcpy(3P) into the allocated buffer in
> `pretend_object_file()`. This is easily fixed though by allocating the
> buffer into a temporary variable first.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  object-file.c | 9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)

I haven't dug into the exact details, but I noticed that t6130,
t7010, and t8002 started breaking linux-leaks/linux-reftable-leaks
CI jobs for 'seen'.  'seen' excluding ps/no-writable-strings seems
to pass the tests, and bisection indicates that 'seen' excluding
ps/no-writable-strings after this step [12/27] still passes, but
once this step is included, the leak tests break.

> diff --git a/object-file.c b/object-file.c
> index 610b1f465c..3afe9fce06 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -277,7 +277,7 @@ int hash_algo_by_length(int len)
>  static struct cached_object {
>  	struct object_id oid;
>  	enum object_type type;
> -	void *buf;
> +	const void *buf;
>  	unsigned long size;
>  } *cached_objects;
>  static int cached_object_nr, cached_object_alloc;
> @@ -1778,6 +1778,10 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
>  			struct object_id *oid)
>  {
>  	struct cached_object *co;
> +	char *co_buf;
> +
> +	co_buf = xmalloc(len);
> +	memcpy(co_buf, buf, len);
>  
>  	hash_object_file(the_hash_algo, buf, len, type, oid);
>  	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||

There is an early return around here.  Perhaps we are leaking co_buf
when we hit it?

> @@ -1787,8 +1791,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
>  	co = &cached_objects[cached_object_nr++];
>  	co->size = len;
>  	co->type = type;
> -	co->buf = xmalloc(len);
> -	memcpy(co->buf, buf, len);
> +	co->buf = co_buf;
>  	oidcpy(&co->oid, oid);
>  	return 0;
>  }

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 12/27] object-file: mark cached object buffers as const
  2024-06-06  6:02     ` Junio C Hamano
@ 2024-06-06  6:10       ` Junio C Hamano
  2024-06-06 10:03         ` Patrick Steinhardt
  2024-06-06 16:25         ` Junio C Hamano
  0 siblings, 2 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-06-06  6:10 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King, Eric Sunshine

Junio C Hamano <gitster@pobox.com> writes:

>> +	co_buf = xmalloc(len);
>> +	memcpy(co_buf, buf, len);
>>  
>>  	hash_object_file(the_hash_algo, buf, len, type, oid);
>>  	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
>
> There is an early return around here.  Perhaps we are leaking co_buf
> when we hit it?

Indeed, that seems to be the case.  With the attached at the tip of
the branch and rebuilding 'seen' seems to pass these 6130, 7010, 8002
tests with SANTIZE=leak.

From f307bbf7bd317d90db29bd1589b49e84b9e37e88 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <gitster@pobox.com>
Date: Wed, 5 Jun 2024 23:03:34 -0700
Subject: [PATCH] fixup! object-file: mark cached object buffers as const

---
 object-file.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/object-file.c b/object-file.c
index b5b5a59dc6..2d5bd3a211 100644
--- a/object-file.c
+++ b/object-file.c
@@ -1785,8 +1785,10 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 
 	hash_object_file(the_hash_algo, buf, len, type, oid);
 	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
-	    find_cached_object(oid))
+	    find_cached_object(oid)) {
+		free(co_buf);
 		return 0;
+	}
 	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
 	co = &cached_objects[cached_object_nr++];
 	co->size = len;
-- 
2.45.2-409-g7b0defb391


^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-05 13:06         ` Phillip Wood
@ 2024-06-06  9:50           ` Patrick Steinhardt
  0 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06  9:50 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1457 bytes --]

On Wed, Jun 05, 2024 at 02:06:55PM +0100, Phillip Wood wrote:
> On 05/06/2024 06:40, Patrick Steinhardt wrote:
> > On Tue, Jun 04, 2024 at 03:06:38PM +0100, Phillip Wood wrote:
> 
> > Do we have any other cases where we allocate inside of a `_INIT` style
> > macro? If so, I'd go with that precedence and just allocate inside of
> > the macro. But if we don't, then I think I'm leaning more towards the
> > way I did it in this patch.
> 
> I recently added an allocation to REPLAY_OPTS_INIT, I'm not sure if there
> are any others. In general code that does
> 
> 	struct foo = FOO_INIT;
> 
> and does not call
> 
> 	foo_release();
> 
> is asking for trouble so I don't feel that allocations in _INIT macros are
> generally a problem. The only reason not to have allocations in an _INIT
> macro is if it might be used to initialize a file scope or global variable
> but that's not the case here.
> 
> > Happy to be convinced otherwise, I don't really feel all that strongly
> > about this. I'm merely aiming for the interface wth least surprises.
> 
> I'm not that fussed either, but I do prefer our _INIT macros over _init()
> functions as I think they're nicer to use and easier to write (no need to
> worry about memset() to zero out the struct).

Okay, given that there is precedent, and given that Junio also seems to
slightly lean into the direction of the `_INIT` macro, I'll adapt this
as proposed by you.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 12/27] object-file: mark cached object buffers as const
  2024-06-06  6:10       ` Junio C Hamano
@ 2024-06-06 10:03         ` Patrick Steinhardt
  2024-06-06 16:25         ` Junio C Hamano
  1 sibling, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:03 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 709 bytes --]

On Wed, Jun 05, 2024 at 11:10:20PM -0700, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
> >> +	co_buf = xmalloc(len);
> >> +	memcpy(co_buf, buf, len);
> >>  
> >>  	hash_object_file(the_hash_algo, buf, len, type, oid);
> >>  	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
> >
> > There is an early return around here.  Perhaps we are leaking co_buf
> > when we hit it?
> 
> Indeed, that seems to be the case.  With the attached at the tip of
> the branch and rebuilding 'seen' seems to pass these 6130, 7010, 8002
> tests with SANTIZE=leak.

Indeed, thanks. Rolled into my local version now.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v5 00/27] Compile with `-Wwrite-strings`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (22 preceding siblings ...)
  2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
@ 2024-06-06 10:27 ` Patrick Steinhardt
  2024-06-06 10:27   ` [PATCH v5 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
                     ` (26 more replies)
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
  24 siblings, 27 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:27 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 13113 bytes --]

Hi,

this is another version of my patch series that prepares our code base
to compile with `-Wwrite-strings`. The effect of that warning is to turn
string constants from type `char []` to `const char []` so that we can
detect more readily when something may accidentally try to write to or
free a constant.

Changes compared to v5:

  - Plug a memory leak in `pretend_object_file()`.

  - Drop `rebase_options_init()` in favor of `REBASE_OPTIONS_INIT`.

Thanks!

Patrick

Patrick Steinhardt (27):
  global: improve const correctness when assigning string constants
  global: convert intentionally-leaking config strings to consts
  refs/reftable: stop micro-optimizing refname allocations on copy
  reftable: cast away constness when assigning constants to records
  refspec: remove global tag refspec structure
  builtin/remote: cast away constness in `get_head_names()`
  diff: cast string constant in `fill_textconv()`
  line-log: stop assigning string constant to file parent buffer
  line-log: always allocate the output prefix
  entry: refactor how we remove items for delayed checkouts
  ident: add casts for fallback name and GECOS
  object-file: mark cached object buffers as const
  object-file: make `buf` parameter of `index_mem()` a constant
  pretty: add casts for decoration option pointers
  compat/win32: fix const-correctness with string constants
  http: do not assign string constant to non-const field
  parse-options: cast long name for OPTION_ALIAS
  send-pack: always allocate receive status
  remote-curl: avoid assigning string constant to non-const variable
  revision: always store allocated strings in output encoding
  mailmap: always store allocated strings in mailmap blob
  imap-send: drop global `imap_server_conf` variable
  imap-send: fix leaking memory in `imap_server_conf`
  builtin/rebase: do not assign default backend to non-constant field
  builtin/rebase: always store allocated string in `options.strategy`
  builtin/merge: always store allocated strings in `pull_twohead`
  config.mak.dev: enable `-Wwrite-strings` warning

 builtin/bisect.c             |   3 +-
 builtin/blame.c              |   2 +-
 builtin/bugreport.c          |   2 +-
 builtin/check-ignore.c       |   4 +-
 builtin/clone.c              |  14 ++--
 builtin/commit.c             |   6 +-
 builtin/diagnose.c           |   2 +-
 builtin/fetch.c              |  11 ++-
 builtin/log.c                |   2 +-
 builtin/mailsplit.c          |   4 +-
 builtin/merge.c              |  18 +++--
 builtin/pull.c               |  52 +++++++-------
 builtin/rebase.c             |  39 ++++++-----
 builtin/receive-pack.c       |   4 +-
 builtin/remote.c             |  12 ++--
 builtin/revert.c             |   2 +-
 builtin/send-pack.c          |   2 +
 compat/basename.c            |  16 ++++-
 compat/mingw.c               |  28 ++++----
 compat/regex/regcomp.c       |   2 +-
 compat/winansi.c             |   2 +-
 config.mak.dev               |   1 +
 diff.c                       |   6 +-
 diffcore-rename.c            |   6 +-
 entry.c                      |  14 ++--
 fmt-merge-msg.c              |   2 +-
 fsck.c                       |   2 +-
 fsck.h                       |   2 +-
 gpg-interface.c              |   6 +-
 http-backend.c               |   2 +-
 http.c                       |   5 +-
 ident.c                      |   4 +-
 imap-send.c                  | 130 ++++++++++++++++++++---------------
 line-log.c                   |  22 +++---
 mailmap.c                    |   2 +-
 merge-ll.c                   |  11 ++-
 object-file.c                |  27 +++++---
 parse-options.h              |   2 +-
 pretty.c                     |   6 +-
 refs.c                       |   2 +-
 refs.h                       |   2 +-
 refs/reftable-backend.c      |  28 ++++----
 refspec.c                    |  13 ----
 refspec.h                    |   1 -
 reftable/basics.c            |  15 ++--
 reftable/basics.h            |   4 +-
 reftable/basics_test.c       |   4 +-
 reftable/block_test.c        |   2 +-
 reftable/merged_test.c       |  44 ++++++------
 reftable/readwrite_test.c    |  32 ++++-----
 reftable/record.c            |   6 +-
 reftable/stack.c             |  10 +--
 reftable/stack_test.c        |  56 +++++++--------
 remote-curl.c                |  53 +++++++-------
 revision.c                   |   3 +-
 run-command.c                |   2 +-
 send-pack.c                  |   2 +-
 t/helper/test-hashmap.c      |   3 +-
 t/helper/test-json-writer.c  |  10 +--
 t/helper/test-regex.c        |   4 +-
 t/helper/test-rot13-filter.c |   5 +-
 t/t3900-i18n-commit.sh       |   1 +
 t/t3901-i18n-patch.sh        |   1 +
 t/unit-tests/t-strbuf.c      |  10 +--
 trailer.c                    |   2 +-
 userdiff.c                   |  10 +--
 userdiff.h                   |  12 ++--
 wt-status.c                  |   2 +-
 68 files changed, 448 insertions(+), 368 deletions(-)

Range-diff against v4:
 1:  e01fde88fe =  1:  e01fde88fe global: improve const correctness when assigning string constants
 2:  92cb0b28c6 =  2:  92cb0b28c6 global: convert intentionally-leaking config strings to consts
 3:  379145478c =  3:  379145478c refs/reftable: stop micro-optimizing refname allocations on copy
 4:  d0a2a2f6c5 =  4:  d0a2a2f6c5 reftable: cast away constness when assigning constants to records
 5:  ead27d3d97 =  5:  ead27d3d97 refspec: remove global tag refspec structure
 6:  7cb5df9182 =  6:  7cb5df9182 builtin/remote: cast away constness in `get_head_names()`
 7:  6e631a9ea4 =  7:  6e631a9ea4 diff: cast string constant in `fill_textconv()`
 8:  ac164651a3 =  8:  ac164651a3 line-log: stop assigning string constant to file parent buffer
 9:  b717af02f0 =  9:  b717af02f0 line-log: always allocate the output prefix
10:  b46dd3210d = 10:  b46dd3210d entry: refactor how we remove items for delayed checkouts
11:  030dbd0288 = 11:  030dbd0288 ident: add casts for fallback name and GECOS
12:  ecca8e973d ! 12:  5cd014c22c object-file: mark cached object buffers as const
    @@ object-file.c: int pretend_object_file(void *buf, unsigned long len, enum object
      
      	hash_object_file(the_hash_algo, buf, len, type, oid);
      	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
    -@@ object-file.c: int pretend_object_file(void *buf, unsigned long len, enum object_type type,
    +-	    find_cached_object(oid))
    ++	    find_cached_object(oid)) {
    ++		free(co_buf);
    + 		return 0;
    ++	}
    + 	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
      	co = &cached_objects[cached_object_nr++];
      	co->size = len;
      	co->type = type;
13:  62f0e47f94 = 13:  69d904ddce object-file: make `buf` parameter of `index_mem()` a constant
14:  e057ead2b4 = 14:  ed8f07aa59 pretty: add casts for decoration option pointers
15:  06b6120d26 = 15:  5953ae1dac compat/win32: fix const-correctness with string constants
16:  a8ef39d73d = 16:  c80f6eff8c http: do not assign string constant to non-const field
17:  9d596a07c5 = 17:  3afd012a88 parse-options: cast long name for OPTION_ALIAS
18:  4019b532f9 = 18:  527755b648 send-pack: always allocate receive status
19:  f2f1ada143 = 19:  4598592d2f remote-curl: avoid assigning string constant to non-const variable
20:  27660b908c = 20:  38fcea2845 revision: always store allocated strings in output encoding
21:  ef43c1b18f = 21:  f990bbeb85 mailmap: always store allocated strings in mailmap blob
22:  0a69ce4b44 = 22:  fff2379832 imap-send: drop global `imap_server_conf` variable
23:  9ccafd286b = 23:  9ab84e459a imap-send: fix leaking memory in `imap_server_conf`
24:  e19457d20c ! 24:  81c69da2e8 builtin/rebase: do not assign default backend to non-constant field
    @@ Commit message
     
         The `struct rebase_options::default_backend` field is a non-constant
         string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
    -    Refactor the code to initialize and release options via two functions
    -    `rebase_options_init()` and `rebase_options_release()`. Like this, we
    -    can easily adapt the former funnction to use `xstrdup()` on the default
    -    value without hiding it away in a macro.
    +    Fix this by using `xstrdup()` to assign the variable and introduce a new
    +    function `rebase_options_release()` that releases memory held by the
    +    structure, including the newly-allocated variable.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: struct rebase_options {
    - 	int config_update_refs;
    - };
    - 
    --#define REBASE_OPTIONS_INIT {			  	\
    --		.type = REBASE_UNSPECIFIED,	  	\
    --		.empty = EMPTY_UNSPECIFIED,	  	\
    --		.keep_empty = 1,			\
    + 		.type = REBASE_UNSPECIFIED,	  	\
    + 		.empty = EMPTY_UNSPECIFIED,	  	\
    + 		.keep_empty = 1,			\
     -		.default_backend = "merge",	  	\
    --		.flags = REBASE_NO_QUIET, 		\
    --		.git_am_opts = STRVEC_INIT,		\
    --		.exec = STRING_LIST_INIT_NODUP,		\
    --		.git_format_patch_opt = STRBUF_INIT,	\
    --		.fork_point = -1,			\
    --		.reapply_cherry_picks = -1,             \
    --		.allow_empty_message = 1,               \
    --		.autosquash = -1,                       \
    --		.rebase_merges = -1,                    \
    --		.config_rebase_merges = -1,             \
    --		.update_refs = -1,                      \
    --		.config_update_refs = -1,               \
    --		.strategy_opts = STRING_LIST_INIT_NODUP,\
    --	}
    -+static void rebase_options_init(struct rebase_options *opts)
    -+{
    -+	memset(opts, 0, sizeof(*opts));
    -+	opts->type = REBASE_UNSPECIFIED;
    -+	opts->empty = EMPTY_UNSPECIFIED;
    -+	opts->default_backend = xstrdup("merge");
    -+	opts->keep_empty = 1;
    -+	opts->flags = REBASE_NO_QUIET;
    -+	strvec_init(&opts->git_am_opts);
    -+	string_list_init_nodup(&opts->exec);
    -+	strbuf_init(&opts->git_format_patch_opt, 0);
    -+	opts->fork_point = -1;
    -+	opts->reapply_cherry_picks = -1;
    -+	opts->allow_empty_message = 1;
    -+	opts->autosquash = -1;
    -+	opts->rebase_merges = -1;
    -+	opts->config_rebase_merges = -1;
    -+	opts->update_refs = -1;
    -+	opts->config_update_refs = -1;
    -+	string_list_init_nodup(&opts->strategy_opts);
    -+}
    -+
    ++		.default_backend = xstrdup("merge"),  	\
    + 		.flags = REBASE_NO_QUIET, 		\
    + 		.git_am_opts = STRVEC_INIT,		\
    + 		.exec = STRING_LIST_INIT_NODUP,		\
    +@@ builtin/rebase.c: struct rebase_options {
    + 		.strategy_opts = STRING_LIST_INIT_NODUP,\
    + 	}
    + 
     +static void rebase_options_release(struct rebase_options *opts)
     +{
     +	free(opts->default_backend);
    @@ builtin/rebase.c: struct rebase_options {
     +	string_list_clear(&opts->strategy_opts, 0);
     +	strbuf_release(&opts->git_format_patch_opt);
     +}
    - 
    ++
      static struct replay_opts get_replay_opts(const struct rebase_options *opts)
      {
    + 	struct replay_opts replay = REPLAY_OPTS_INIT;
     @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value,
      	}
      
    @@ builtin/rebase.c: static int rebase_config(const char *var, const char *value,
      		return git_config_string(&opts->default_backend, var, value);
      	}
      
    -@@ builtin/rebase.c: static int check_exec_cmd(const char *cmd)
    - 
    - int cmd_rebase(int argc, const char **argv, const char *prefix)
    - {
    --	struct rebase_options options = REBASE_OPTIONS_INIT;
    -+	struct rebase_options options;
    - 	const char *branch_name;
    - 	int ret, flags, total_argc, in_progress = 0;
    - 	int keep_base = 0;
    -@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    - 	};
    - 	int i;
    - 
    -+	rebase_options_init(&options);
    -+
    - 	if (argc == 2 && !strcmp(argv[1], "-h"))
    - 		usage_with_options(builtin_rebase_usage,
    - 				   builtin_rebase_options);
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
      cleanup:
      	strbuf_release(&buf);
25:  f548241960 ! 25:  6819bf6116 builtin/rebase: always store allocated string in `options.strategy`
    @@ Commit message
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
      {
    - 	struct rebase_options options;
    + 	struct rebase_options options = REBASE_OPTIONS_INIT;
      	const char *branch_name;
     +	const char *strategy_opt = NULL;
      	int ret, flags, total_argc, in_progress = 0;
26:  78ac075644 = 26:  a1d2149429 builtin/merge: always store allocated strings in `pull_twohead`
27:  0cd4ce07d8 = 27:  c714b67199 config.mak.dev: enable `-Wwrite-strings` warning
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v5 01/27] global: improve const correctness when assigning string constants
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
@ 2024-06-06 10:27   ` Patrick Steinhardt
  2024-06-06 10:27   ` [PATCH v5 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
                     ` (25 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:27 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 27645 bytes --]

We're about to enable `-Wwrite-strings`, which changes the type of
string constants to `const char[]`. Fix various sites where we assign
such constants to non-const variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/bisect.c             |  3 ++-
 builtin/blame.c              |  2 +-
 builtin/bugreport.c          |  2 +-
 builtin/check-ignore.c       |  4 +--
 builtin/clone.c              |  6 ++---
 builtin/commit.c             |  6 ++---
 builtin/diagnose.c           |  2 +-
 builtin/log.c                |  2 +-
 builtin/mailsplit.c          |  4 +--
 builtin/pull.c               | 52 ++++++++++++++++++------------------
 builtin/receive-pack.c       |  4 +--
 builtin/revert.c             |  2 +-
 compat/regex/regcomp.c       |  2 +-
 diff.c                       |  4 +--
 diffcore-rename.c            |  6 ++---
 fmt-merge-msg.c              |  2 +-
 fsck.c                       |  2 +-
 fsck.h                       |  2 +-
 gpg-interface.c              |  2 +-
 http-backend.c               |  2 +-
 imap-send.c                  |  6 ++---
 pretty.c                     |  2 +-
 refs.c                       |  2 +-
 refs.h                       |  2 +-
 reftable/basics.c            | 15 +++++------
 reftable/basics.h            |  4 +--
 reftable/basics_test.c       |  4 +--
 reftable/record.c            |  6 ++---
 reftable/stack.c             | 10 ++++---
 reftable/stack_test.c        |  8 +++---
 run-command.c                |  2 +-
 t/helper/test-hashmap.c      |  3 ++-
 t/helper/test-json-writer.c  | 10 +++----
 t/helper/test-regex.c        |  4 +--
 t/helper/test-rot13-filter.c |  5 ++--
 t/unit-tests/t-strbuf.c      | 10 ++++---
 trailer.c                    |  2 +-
 wt-status.c                  |  2 +-
 38 files changed, 106 insertions(+), 102 deletions(-)

diff --git a/builtin/bisect.c b/builtin/bisect.c
index a58432b9d9..dabce9b542 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
 	return bisect_clean_state();
 }
 
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+		       const char *fmt, const char *state,
 		       struct commit *commit)
 {
 	struct pretty_print_context pp = {0};
diff --git a/builtin/blame.c b/builtin/blame.c
index 838cd476be..98c7629b6a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
-	char *tmp, *endp;
+	const char *tmp, *endp;
 	const char *namebuf, *mailbuf;
 
 	tmp = strstr(inbuf, what);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 25f860a0d9..b3cc77af53 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode diagnose = DIAGNOSE_NONE;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	const char *user_relative_path = NULL;
 	char *prefixed_filename;
 	size_t output_path_len;
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 6c43430ec4..2bda6a1d46 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -35,8 +35,8 @@ static const struct option check_ignore_options[] = {
 
 static void output_pattern(const char *path, struct path_pattern *pattern)
 {
-	char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
-	char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+	const char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
+	const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
 	if (!nul_term_line) {
 		if (!verbose) {
 			write_name_quoted(path, stdout, '\n');
diff --git a/builtin/clone.c b/builtin/clone.c
index 23993b905b..92ab7d7165 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -71,7 +71,7 @@ static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
 static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
 static int option_sparse_checkout;
@@ -177,8 +177,8 @@ static struct option builtin_clone_options[] = {
 
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
-	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
-	static char *bundle_suffix[] = { ".bundle", "" };
+	static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static const char *bundle_suffix[] = { ".bundle", "" };
 	size_t baselen = path->len;
 	struct stat st;
 	int i;
diff --git a/builtin/commit.c b/builtin/commit.c
index f53e7e86ff..75c741173e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,7 +113,7 @@ static char *template_file;
  * the commit message and/or authorship.
  */
 static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
 static char *fixup_message, *fixup_commit, *squash_message;
 static const char *fixup_prefix;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +121,8 @@ static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
 static struct strvec trailer_args = STRVEC_INIT;
 
 /*
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2b55..4857a4395b 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode mode = DIAGNOSE_STATS;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	char *prefixed_filename;
 
 	const struct option diagnose_options[] = {
diff --git a/builtin/log.c b/builtin/log.c
index 78a247d8a9..b8846a9458 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1283,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 	o2->flags = flags2;
 }
 
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb8ae..fe6dbc5d05 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
 	DIR *dir;
 	struct dirent *dent;
 	char *name = NULL;
-	char *subs[] = { "cur", "new", NULL };
-	char **sub;
+	const char *subs[] = { "cur", "new", NULL };
+	const char **sub;
 	int ret = -1;
 
 	for (sub = subs; *sub; ++sub) {
diff --git a/builtin/pull.c b/builtin/pull.c
index d622202bce..2d0429f14f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
 
 /* Shared options */
 static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
 
 /* Options passed to git-merge or git-rebase */
 static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
 static int opt_autostash = -1;
 static int config_autostash;
 static int check_trust_level = 1;
 static struct strvec opt_strategies = STRVEC_INIT;
 static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
 static int opt_allow_unrelated_histories;
 
 /* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
 static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
 static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
 static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
 static struct strvec opt_fetch = STRVEC_INIT;
 
 static struct option pull_options[] = {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 01c1f04ece..c8d12ee0a7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1249,7 +1249,7 @@ static int run_proc_receive_hook(struct command *commands,
 	return code;
 }
 
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
 	N_("By default, updating the current branch in a non-bare repository\n"
 	   "is denied, because it will make the index and work tree inconsistent\n"
 	   "with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
 	rp_error("%s", _(refuse_unconfigured_deny_msg));
 }
 
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
 	N_("By default, deleting the current branch is denied, because the next\n"
 	   "'git clone' won't result in any file checked out, causing confusion.\n"
 	   "\n"
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2c68..7bf2b4e11d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 
 	/* Check for incompatible command line arguments */
 	if (cmd) {
-		char *this_operation;
+		const char *this_operation;
 		if (cmd == 'q')
 			this_operation = "--quit";
 		else if (cmd == 'c')
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 2bc0f1187a..6c5d455e92 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
   unsigned int table_size;
 #ifndef _LIBC
-  char *codeset_name;
+  const char *codeset_name;
 #endif
 
   memset (dfa, '\0', sizeof (re_dfa_t));
diff --git a/diff.c b/diff.c
index e70301df76..ffd867ef6c 100644
--- a/diff.c
+++ b/diff.c
@@ -3764,7 +3764,7 @@ static void builtin_diff(const char *name_a,
 	return;
 }
 
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 {
 	if (!is_renamed) {
 		if (p->status == DIFF_STATUS_ADDED) {
@@ -4076,7 +4076,7 @@ static int reuse_worktree_file(struct index_state *istate,
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
 	struct strbuf buf = STRBUF_INIT;
-	char *dirty = "";
+	const char *dirty = "";
 
 	/* Are we looking at the work tree? */
 	if (s->dirty_submodule)
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bcac7..0e1adb87df 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -406,7 +406,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
 	return highest_destination_dir;
 }
 
-static char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
 
 static int dir_rename_already_determinable(struct strintmap *counts)
 {
@@ -429,8 +429,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
 }
 
 static void increment_count(struct dir_rename_info *info,
-			    char *old_dir,
-			    char *new_dir)
+			    const char *old_dir,
+			    const char *new_dir)
 {
 	struct strintmap *counts;
 	struct strmap_entry *e;
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 7d144b803a..5af63ab5ab 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -447,7 +447,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
 				const char *current_branch)
 {
 	int i = 0;
-	char *sep = "";
+	const char *sep = "";
 
 	strbuf_addstr(out, "Merge ");
 	for (i = 0; i < srcs.nr; i++) {
diff --git a/fsck.c b/fsck.c
index 7dff41413e..61cd48aa25 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1231,7 +1231,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 }
 
 int fsck_buffer(const struct object_id *oid, enum object_type type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options)
 {
 	if (type == OBJ_BLOB)
diff --git a/fsck.h b/fsck.h
index 17fa2dda5d..4f0c4e6479 100644
--- a/fsck.h
+++ b/fsck.h
@@ -202,7 +202,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
  * struct.
  */
 int fsck_buffer(const struct object_id *oid, enum object_type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options);
 
 /*
diff --git a/gpg-interface.c b/gpg-interface.c
index 5193223714..71a9382a61 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -727,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
 			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
-	char *fmtname = NULL;
+	const char *fmtname = NULL;
 	char *trust;
 	int ret;
 
diff --git a/http-backend.c b/http-backend.c
index 5b65287ac9..5b4dca65ed 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -753,7 +753,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
 
 int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
-	char *method = getenv("REQUEST_METHOD");
+	const char *method = getenv("REQUEST_METHOD");
 	const char *proto_header;
 	char *dir;
 	struct service_cmd *cmd = NULL;
diff --git a/imap-send.c b/imap-send.c
index a5d1510180..8b723b34a5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1215,9 +1215,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
 static void wrap_in_html(struct strbuf *msg)
 {
 	struct strbuf buf = STRBUF_INIT;
-	static char *content_type = "Content-Type: text/html;\n";
-	static char *pre_open = "<pre>\n";
-	static char *pre_close = "</pre>\n";
+	static const char *content_type = "Content-Type: text/html;\n";
+	static const char *pre_open = "<pre>\n";
+	static const char *pre_close = "</pre>\n";
 	const char *body = strstr(msg->buf, "\n\n");
 
 	if (!body)
diff --git a/pretty.c b/pretty.c
index 22a81506b7..ec05db5655 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1325,7 +1325,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
 static size_t parse_describe_args(const char *start, struct strvec *args)
 {
 	struct {
-		char *name;
+		const char *name;
 		enum {
 			DESCRIBE_ARG_BOOL,
 			DESCRIBE_ARG_INTEGER,
diff --git a/refs.c b/refs.c
index 8260c27cde..292e8d947e 100644
--- a/refs.c
+++ b/refs.c
@@ -159,7 +159,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
 {
 	struct ref_namespace_info *info = &ref_namespace[namespace];
 	if (info->ref_updated)
-		free(info->ref);
+		free((char *)info->ref);
 	info->ref = ref;
 	info->ref_updated = 1;
 }
diff --git a/refs.h b/refs.h
index 34568ee1fb..923f751d18 100644
--- a/refs.h
+++ b/refs.h
@@ -975,7 +975,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
  */
 
 struct ref_namespace_info {
-	char *ref;
+	const char *ref;
 	enum decoration_type decoration;
 
 	/*
diff --git a/reftable/basics.c b/reftable/basics.c
index fea711db7e..0058619ca6 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -67,9 +67,9 @@ void free_names(char **a)
 	reftable_free(a);
 }
 
-size_t names_length(char **names)
+size_t names_length(const char **names)
 {
-	char **p = names;
+	const char **p = names;
 	while (*p)
 		p++;
 	return p - names;
@@ -102,15 +102,12 @@ void parse_names(char *buf, int size, char ***namesp)
 	*namesp = names;
 }
 
-int names_equal(char **a, char **b)
+int names_equal(const char **a, const char **b)
 {
-	int i = 0;
-	for (; a[i] && b[i]; i++) {
-		if (strcmp(a[i], b[i])) {
+	size_t i = 0;
+	for (; a[i] && b[i]; i++)
+		if (strcmp(a[i], b[i]))
 			return 0;
-		}
-	}
-
 	return a[i] == b[i];
 }
 
diff --git a/reftable/basics.h b/reftable/basics.h
index 523ecd5307..c8fec68d4e 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -42,10 +42,10 @@ void free_names(char **a);
 void parse_names(char *buf, int size, char ***namesp);
 
 /* compares two NULL-terminated arrays of strings. */
-int names_equal(char **a, char **b);
+int names_equal(const char **a, const char **b);
 
 /* returns the array size of a NULL-terminated array of strings. */
-size_t names_length(char **names);
+size_t names_length(const char **names);
 
 /* Allocation routines; they invoke the functions set through
  * reftable_set_alloc() */
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 997c4d9e01..13bc761817 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -58,8 +58,8 @@ static void test_binsearch(void)
 
 static void test_names_length(void)
 {
-	char *a[] = { "a", "b", NULL };
-	EXPECT(names_length(a) == 2);
+	const char *names[] = { "a", "b", NULL };
+	EXPECT(names_length(names) == 2);
 }
 
 static void test_parse_names_normal(void)
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e913..a2cba5ef74 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
 	return start_len - in.len;
 }
 
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
 {
 	struct string_view start = s;
 	int l = strlen(str);
@@ -969,9 +969,9 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
 	return REFTABLE_FORMAT_ERROR;
 }
 
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
 {
-	char *empty = "";
+	const char *empty = "";
 	if (!a)
 		a = empty;
 
diff --git a/reftable/stack.c b/reftable/stack.c
index a59ebe038d..09549c51c9 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -204,7 +204,8 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
 	return cur;
 }
 
-static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
+static int reftable_stack_reload_once(struct reftable_stack *st,
+				      const char **names,
 				      int reuse_open)
 {
 	size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
@@ -222,7 +223,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
 	while (*names) {
 		struct reftable_reader *rd = NULL;
-		char *name = *names++;
+		const char *name = *names++;
 
 		/* this is linear; we assume compaction keeps the number of
 		   tables under control so this is not quadratic. */
@@ -354,7 +355,7 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 				goto out;
 		}
 
-		err = reftable_stack_reload_once(st, names, reuse_open);
+		err = reftable_stack_reload_once(st, (const char **) names, reuse_open);
 		if (!err)
 			break;
 		if (err != REFTABLE_NOT_EXIST_ERROR)
@@ -368,7 +369,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 		err = read_lines(st->list_file, &names_after);
 		if (err < 0)
 			goto out;
-		if (names_equal(names_after, names)) {
+		if (names_equal((const char **) names_after,
+				(const char **) names)) {
 			err = REFTABLE_NOT_EXIST_ERROR;
 			goto out;
 		}
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 7889f818d1..07d89b45da 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
 	char out[1024] = "line1\n\nline2\nline3";
 	int n, err;
 	char **names = NULL;
-	char *want[] = { "line1", "line2", "line3" };
+	const char *want[] = { "line1", "line2", "line3" };
 	int i = 0;
 
 	EXPECT(fd > 0);
@@ -116,9 +116,9 @@ static void test_parse_names(void)
 
 static void test_names_equal(void)
 {
-	char *a[] = { "a", "b", "c", NULL };
-	char *b[] = { "a", "b", "d", NULL };
-	char *c[] = { "a", "b", NULL };
+	const char *a[] = { "a", "b", "c", NULL };
+	const char *b[] = { "a", "b", "d", NULL };
+	const char *c[] = { "a", "b", NULL };
 
 	EXPECT(names_equal(a, a));
 	EXPECT(!names_equal(a, b));
diff --git a/run-command.c b/run-command.c
index 1b821042b4..7600531fb6 100644
--- a/run-command.c
+++ b/run-command.c
@@ -663,7 +663,7 @@ int start_command(struct child_process *cmd)
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
 	int failed_errno;
-	char *str;
+	const char *str;
 
 	/*
 	 * In case of errors we must keep the promise to close FDs
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d49c..2912899558 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
 }
 
 static struct test_entry *alloc_test_entry(unsigned int hash,
-					   char *key, char *value)
+					   const char *key,
+					   const char *value)
 {
 	size_t klen = strlen(key);
 	size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f597..ed52eb76bf 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
 	jw_end(&arr4);
 }
 
-static char *expect_nest1 =
+static const char *expect_nest1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
 static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
 	jw_release(&arr1);
 }
 
-static char *expect_inline1 =
+static const char *expect_inline1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
 	("{\n"
 	 "  \"obj1\": {\n"
 	 "    \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
 	jw_end(&inline1);
 }
 
-static char *expect_inline2 =
+static const char *expect_inline2 =
 	"[[1,2],[3,4],{\"a\":\"abc\"}]";
 
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
 	("[\n"
 	 "  [\n"
 	 "    1,\n"
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042eafc2..366bd70976 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
 
 static int test_regex_bug(void)
 {
-	char *pat = "[^={} \t]+";
-	char *str = "={}\nfred";
+	const char *pat = "[^={} \t]+";
+	const char *str = "={}\nfred";
 	regex_t r;
 	regmatch_t m[1];
 
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c622..7e1d9e0ee4 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
 	strmap_clear(&delay, 0);
 }
 
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
 {
 	struct delay_entry *entry = xcalloc(1, sizeof(*entry));
 	entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
 static void command_loop(void)
 {
 	for (;;) {
-		char *buf, *output;
+		char *buf;
+		const char *output;
 		char *pathname;
 		struct delay_entry *entry;
 		struct strbuf input = STRBUF_INIT;
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4441..6027dafef7 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
 #include "strbuf.h"
 
 /* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+		  const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
 }
 
 /* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+			    const char *init_str, const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
 	strbuf_release(&buf);
 }
 
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
 {
 	const char *p_ch = data;
 	const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
 	check_char(buf->buf[buf->len], ==, '\0');
 }
 
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
 {
 	const char *text = data;
 	size_t len = strlen(text);
diff --git a/trailer.c b/trailer.c
index 2bcb9ba8f7..72e5136c73 100644
--- a/trailer.c
+++ b/trailer.c
@@ -63,7 +63,7 @@ struct arg_item {
 
 static LIST_HEAD(conf_head);
 
-static char *separators = ":";
+static const char *separators = ":";
 
 static int configured;
 
diff --git a/wt-status.c b/wt-status.c
index ff4be071ca..7912545e4e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2408,7 +2408,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
 		int mode;
 		struct object_id oid;
 	} stages[3];
-	char *key;
+	const char *key;
 	char submodule_token[5];
 	char unmerged_prefix = 'u';
 	char eol_char = s->null_termination ? '\0' : '\n';
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 02/27] global: convert intentionally-leaking config strings to consts
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
  2024-06-06 10:27   ` [PATCH v5 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
@ 2024-06-06 10:27   ` Patrick Steinhardt
  2024-06-06 10:27   ` [PATCH v5 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
                     ` (24 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:27 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 5084 bytes --]

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 the struct memers are never
    written to or free'd.

  - `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 written to or 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 written to or free'd.

All of these are intentionally kept alive and are never written to.
Furthermore, all of these are being assigned both string constants in
some places, and allocated strings in other places. This will cause
warnings once we enable `-Wwrite-strings`, so 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(-)

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;
 };
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 03/27] refs/reftable: stop micro-optimizing refname allocations on copy
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
  2024-06-06 10:27   ` [PATCH v5 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
  2024-06-06 10:27   ` [PATCH v5 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
@ 2024-06-06 10:27   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
                     ` (23 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:27 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 4918 bytes --]

When copying refs, we execute `write_copy_table()` to write the new
table. As the names are given to us via `arg->newname` and
`arg->oldname`, respectively, we optimize away some allocations by
assigning those fields to the reftable records we are about to write
directly, without duplicating them. This requires us to cast the input
to `char *` pointers as they are in fact constant strings. Later on, we
then unset the refname for all of the records before calling
`reftable_log_record_release()` on them.

We also do this when assigning the "HEAD" constant, but here we do not
cast because its type is `char[]` by default. It's about to be turned
into `const char *` though once we enable `-Wwrite-strings` and will
thus cause another warning.

It's quite dubious whether this micro-optimization really helps. We're
about to write to disk anyway, which is going to be way slower than a
small handful of allocations. Let's drop the optimization altogther and
instead copy arguments to simplify the code and avoid the future warning
with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 refs/reftable-backend.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1af86bbdec..e77faa2b9d 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1340,10 +1340,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	 * old reference.
 	 */
 	refs[0] = old_ref;
-	refs[0].refname = (char *)arg->newname;
+	refs[0].refname = xstrdup(arg->newname);
 	refs[0].update_index = creation_ts;
 	if (arg->delete_old) {
-		refs[1].refname = (char *)arg->oldname;
+		refs[1].refname = xstrdup(arg->oldname);
 		refs[1].value_type = REFTABLE_REF_DELETION;
 		refs[1].update_index = deletion_ts;
 	}
@@ -1366,7 +1366,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 		fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs[logs_nr].update_index = deletion_ts;
 		logs[logs_nr].value.update.message =
 			xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1387,7 +1387,13 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (append_head_reflog) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			logs[logs_nr] = logs[logs_nr - 1];
-			logs[logs_nr].refname = "HEAD";
+			logs[logs_nr].refname = xstrdup("HEAD");
+			logs[logs_nr].value.update.name =
+				xstrdup(logs[logs_nr].value.update.name);
+			logs[logs_nr].value.update.email =
+				xstrdup(logs[logs_nr].value.update.email);
+			logs[logs_nr].value.update.message =
+				xstrdup(logs[logs_nr].value.update.message);
 			logs_nr++;
 		}
 	}
@@ -1398,7 +1404,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 	memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 	fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-	logs[logs_nr].refname = (char *)arg->newname;
+	logs[logs_nr].refname = xstrdup(arg->newname);
 	logs[logs_nr].update_index = creation_ts;
 	logs[logs_nr].value.update.message =
 		xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1430,7 +1436,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		 */
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		logs[logs_nr] = old_log;
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs_nr++;
 
 		/*
@@ -1439,7 +1445,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (arg->delete_old) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
-			logs[logs_nr].refname = (char *)arg->oldname;
+			logs[logs_nr].refname = xstrdup(arg->oldname);
 			logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
 			logs[logs_nr].update_index = old_log.update_index;
 			logs_nr++;
@@ -1462,13 +1468,11 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	reftable_iterator_destroy(&it);
 	string_list_clear(&skip, 0);
 	strbuf_release(&errbuf);
-	for (i = 0; i < logs_nr; i++) {
-		if (!strcmp(logs[i].refname, "HEAD"))
-			continue;
-		logs[i].refname = NULL;
+	for (i = 0; i < logs_nr; i++)
 		reftable_log_record_release(&logs[i]);
-	}
 	free(logs);
+	for (i = 0; i < ARRAY_SIZE(refs); i++)
+		reftable_ref_record_release(&refs[i]);
 	reftable_ref_record_release(&old_ref);
 	reftable_log_record_release(&old_log);
 	return ret;
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 04/27] reftable: cast away constness when assigning constants to records
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (2 preceding siblings ...)
  2024-06-06 10:27   ` [PATCH v5 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
                     ` (22 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 14245 bytes --]

The reftable records are used in multiple ways throughout the reftable
library. In many of those cases they merely act as input to a function
without getting modified by it at all. Most importantly, this happens
when writing records and when querying for records.

We rely on this in our tests and thus assign string constants to those
fields, which is about to generate warnings as those fields are of type
`char *`. While we could go through the process and instead allocate
those strings in all of our tests, this feels quite unnecessary.

Instead, add casts to `char *` for all of those strings. As this is part
of our tests, this also nicely serves as a demonstration that nothing
writes or frees those string constants, which would otherwise lead to
segfaults.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 reftable/block_test.c     |  2 +-
 reftable/merged_test.c    | 44 +++++++++++++++++------------------
 reftable/readwrite_test.c | 32 +++++++++++++-------------
 reftable/stack_test.c     | 48 +++++++++++++++++++--------------------
 4 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfbc83..90aecd5a7c 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
 			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
-	rec.u.ref.refname = "";
+	rec.u.ref.refname = (char *) "";
 	rec.u.ref.value_type = REFTABLE_REF_DELETION;
 	n = block_writer_add(&bw, &rec);
 	EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82d1c..6d1159d12d 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -124,13 +124,13 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 static void test_merged_between(void)
 {
 	struct reftable_ref_record r1[] = { {
-		.refname = "b",
+		.refname = (char *) "b",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
@@ -165,38 +165,38 @@ static void test_merged(void)
 {
 	struct reftable_ref_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
 	struct reftable_ref_record r3[] = {
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 2 },
 		},
 		{
-			.refname = "d",
+			.refname = (char *) "d",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
@@ -291,46 +291,46 @@ static void test_merged_logs(void)
 {
 	struct reftable_log_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 2 },
 				/* deletion */
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message2",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message2",
 			}
 		},
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message1",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message1",
 			}
 		},
 	};
 	struct reftable_log_record r2[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.new_hash = { 3 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message3",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message3",
 			}
 		},
 	};
 	struct reftable_log_record r3[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_DELETION,
 		},
@@ -406,7 +406,7 @@ static void test_default_write_opts(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	struct reftable_ref_record rec = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.update_index = 1,
 	};
 	int err;
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd214c5..c55019232b 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -86,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
 		set_test_hash(log.value.update.new_hash, i);
-		log.value.update.message = "message";
+		log.value.update.message = (char *) "message";
 
 		n = reftable_writer_add_log(w, &log);
 		EXPECT(n == 0);
@@ -118,15 +118,15 @@ static void test_log_buffer_size(void)
 	int err;
 	int i;
 	struct reftable_log_record
-		log = { .refname = "refs/heads/master",
+		log = { .refname = (char *) "refs/heads/master",
 			.update_index = 0xa,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value = { .update = {
-					   .name = "Han-Wen Nienhuys",
-					   .email = "hanwen@google.com",
+					   .name = (char *) "Han-Wen Nienhuys",
+					   .email = (char *) "hanwen@google.com",
 					   .tz_offset = 100,
 					   .time = 0x5e430672,
-					   .message = "commit: 9\n",
+					   .message = (char *) "commit: 9\n",
 				   } } };
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
@@ -156,15 +156,15 @@ static void test_log_overflow(void)
 	};
 	int err;
 	struct reftable_log_record log = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 0xa,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "Han-Wen Nienhuys",
-				.email = "hanwen@google.com",
+				.name = (char *) "Han-Wen Nienhuys",
+				.email = (char *) "hanwen@google.com",
 				.tz_offset = 100,
 				.time = 0x5e430672,
 				.message = msg,
@@ -293,14 +293,14 @@ static void test_log_zlib_corruption(void)
 	char message[100] = { 0 };
 	int err, i, n;
 	struct reftable_log_record log = {
-		.refname = "refname",
+		.refname = (char *) "refname",
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.new_hash = { 1 },
 				.old_hash = { 2 },
-				.name = "My Name",
-				.email = "myname@invalid",
+				.name = (char *) "My Name",
+				.email = (char *) "myname@invalid",
 				.message = message,
 			},
 		},
@@ -728,7 +728,7 @@ static void test_write_empty_key(void)
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
-		.refname = "",
+		.refname = (char *) "",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_DELETION,
 	};
@@ -752,18 +752,18 @@ static void test_write_key_order(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record refs[2] = {
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}, {
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}
 	};
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 07d89b45da..4abf92636d 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -156,10 +156,10 @@ static void test_reftable_stack_add_one(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 	struct stat stat_result = { 0 };
@@ -216,16 +216,16 @@ static void test_reftable_stack_uptodate(void)
 
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "branch2",
+		.refname = (char *) "branch2",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 
@@ -264,10 +264,10 @@ static void test_reftable_stack_transaction_api(void)
 	struct reftable_addition *add = NULL;
 
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 
@@ -313,7 +313,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		char name[100];
 
@@ -356,7 +356,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
 {
 	struct reftable_ref_record ref = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = {0x01},
@@ -409,16 +409,16 @@ static void test_reftable_stack_update_index_check(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "name1",
+		.refname = (char *) "name1",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "name2",
+		.refname = (char *) "name2",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 	err = reftable_new_stack(&st, dir, cfg);
@@ -561,7 +561,7 @@ static void test_reftable_stack_log_normalize(void)
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
 	struct reftable_log_record input = {
-		.refname = "branch",
+		.refname = (char *) "branch",
 		.update_index = 1,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
@@ -582,11 +582,11 @@ static void test_reftable_stack_log_normalize(void)
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 
-	input.value.update.message = "one\ntwo";
+	input.value.update.message = (char *) "one\ntwo";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT(err == REFTABLE_API_ERROR);
 
-	input.value.update.message = "one";
+	input.value.update.message = (char *) "one";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
 
@@ -594,7 +594,7 @@ static void test_reftable_stack_log_normalize(void)
 	EXPECT_ERR(err);
 	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
 
-	input.value.update.message = "two\n";
+	input.value.update.message = (char *) "two\n";
 	arg.update_index = 2;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
@@ -697,9 +697,9 @@ static void test_reftable_stack_hash_id(void)
 	int err;
 
 	struct reftable_ref_record ref = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "target",
+		.value.symref = (char *) "target",
 		.update_index = 1,
 	};
 	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
@@ -879,7 +879,7 @@ static void test_reftable_stack_auto_compaction(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -913,7 +913,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 
 		/*
@@ -964,7 +964,7 @@ static void test_reftable_stack_compaction_concurrent(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -1014,7 +1014,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 05/27] refspec: remove global tag refspec structure
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (3 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
                     ` (21 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 3608 bytes --]

We have a global tag refspec structure that is used by both git-clone(1)
and git-fetch(1). Initialization of the structure will break once we
enable `-Wwrite-strings`, even though the breakage is harmless. While we
could just add casts, the structure isn't really required in the first
place as we can simply initialize the structures at the respective
callsites.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c |  8 ++++++--
 builtin/fetch.c | 11 ++++++++---
 refspec.c       | 13 -------------
 refspec.h       |  1 -
 4 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 92ab7d7165..bde1d284a2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
 	struct ref *local_refs = head;
 	struct ref **tail = head ? &head->next : &local_refs;
+	struct refspec_item tag_refspec;
+
+	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
 
 	if (option_single_branch) {
 		struct ref *remote_head = NULL;
@@ -545,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 					      &tail, 0);
 
 			/* if --branch=tag, pull the requested tag explicitly */
-			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+			get_fetch_map(remote_head, &tag_refspec, &tail, 0);
 		}
 		free_refs(remote_head);
 	} else {
@@ -555,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	}
 
 	if (!option_mirror && !option_single_branch && !option_no_tags)
-		get_fetch_map(refs, tag_refspec, &tail, 0);
+		get_fetch_map(refs, &tag_refspec, &tail, 0);
 
+	refspec_item_clear(&tag_refspec);
 	return local_refs;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 75255dc600..06b60867f5 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
 		}
 	}
 
-	if (tags == TAGS_SET)
+	if (tags == TAGS_SET) {
+		struct refspec_item tag_refspec;
+
 		/* also fetch all tags */
-		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-	else if (tags == TAGS_DEFAULT && *autotags)
+		refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+		get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+		refspec_item_clear(&tag_refspec);
+	} else if (tags == TAGS_DEFAULT && *autotags) {
 		find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+	}
 
 	/* Now append any refs to be updated opportunistically: */
 	*tail = orefs;
diff --git a/refspec.c b/refspec.c
index d60932f4de..1df5de6c2f 100644
--- a/refspec.c
+++ b/refspec.c
@@ -7,19 +7,6 @@
 #include "refspec.h"
 #include "strbuf.h"
 
-static struct refspec_item s_tag_refspec = {
-	.force = 0,
-	.pattern = 1,
-	.matching = 0,
-	.exact_sha1 = 0,
-	.negative = 0,
-	.src = "refs/tags/*",
-	.dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
 /*
  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
  * Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446993..754be45cee 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
 #define REFSPEC_H
 
 #define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
 
 /**
  * A struct refspec_item holds the parsed interpretation of a refspec.  If it
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 06/27] builtin/remote: cast away constness in `get_head_names()`
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (4 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
                     ` (20 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2509 bytes --]

In `get_head_names()`, we assign the "refs/heads/*" string constant to
`struct refspec_item::{src,dst}`, which are both non-constant pointers.
Ideally, we'd refactor the code such that both of these fields were
constant. But `struct refspec_item` is used for two different usecases
with conflicting requirements:

  - To query for a source or destination based on the given refspec. The
    caller either sets `src` or `dst` as the branch that we want to
    search for, and the respective other field gets populated. The
    fields should be constant when being used as a query parameter,
    which is owned by the caller, and non-constant when being used as an
    out parameter, which is owned by the refspec item. This is is
    contradictory in itself already.

  - To store refspec items with their respective source and destination
    branches, in which case both fields should be owned by the struct.

Ideally, we'd split up this interface to clearly separate between
querying and storing, which would enable us to clarify lifetimes of the
strings. This would be a much bigger undertaking though.

Instead, accept the status quo for now and cast away the constness of
the source and destination patterns. We know that those are not being
written to or freed, so while this is ugly it certainly is fine for now.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/remote.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/builtin/remote.c b/builtin/remote.c
index d52b1c0e10..b44f580b8c 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -493,12 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 {
 	struct ref *ref, *matches;
 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
-	struct refspec_item refspec;
+	struct refspec_item refspec = {
+		.force = 0,
+		.pattern = 1,
+		.src = (char *) "refs/heads/*",
+		.dst = (char *) "refs/heads/*",
+	};
 
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.force = 0;
-	refspec.pattern = 1;
-	refspec.src = refspec.dst = "refs/heads/*";
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 				    fetch_map, 1);
@@ -507,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 
 	free_refs(fetch_map);
 	free_refs(matches);
-
 	return 0;
 }
 
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 07/27] diff: cast string constant in `fill_textconv()`
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (5 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
                     ` (19 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1597 bytes --]

The `fill_textconv()` function is responsible for converting an input
file with a textconv driver, which is then passed to the caller. Weirdly
though, the function also handles the case where there is no textconv
driver at all. In that case, it will return either the contents of the
populated filespec, or an empty string if the filespec is invalid.

These two cases have differing memory ownership semantics. When there is
a textconv driver, then the result is an allocated string. Otherwise,
the result is either a string constant or owned by the filespec struct.
All callers are in fact aware of this weirdness and only end up freeing
the output buffer when they had a textconv driver.

Ideally, we'd split up this interface to only perform the conversion via
the textconv driver, and BUG in case the caller didn't provide one. This
would make memory ownership semantics much more straight forward. For
now though, let's simply cast the empty string constant to `char *` to
avoid a warning with `-Wwrite-strings`. This is equivalent to the same
cast that we already have in `fill_mmfile()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 diff.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/diff.c b/diff.c
index ffd867ef6c..cecda216cf 100644
--- a/diff.c
+++ b/diff.c
@@ -7235,7 +7235,7 @@ size_t fill_textconv(struct repository *r,
 
 	if (!driver) {
 		if (!DIFF_FILE_VALID(df)) {
-			*outbuf = "";
+			*outbuf = (char *) "";
 			return 0;
 		}
 		if (diff_populate_filespec(r, df, NULL))
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 08/27] line-log: stop assigning string constant to file parent buffer
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (6 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 09/27] line-log: always allocate the output prefix Patrick Steinhardt
                     ` (18 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1198 bytes --]

Stop assigning a string constant to the file parent buffer and instead
assign an allocated string. While the code is fine in practice, it will
break once we compile with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/line-log.c b/line-log.c
index 8ff6ccb772..bd3e663c24 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1032,6 +1032,7 @@ static int process_diff_filepair(struct rev_info *rev,
 	struct range_set tmp;
 	struct diff_ranges diff;
 	mmfile_t file_parent, file_target;
+	char *parent_data_to_free = NULL;
 
 	assert(pair->two->path);
 	while (rg) {
@@ -1056,7 +1057,7 @@ static int process_diff_filepair(struct rev_info *rev,
 		file_parent.ptr = pair->one->data;
 		file_parent.size = pair->one->size;
 	} else {
-		file_parent.ptr = "";
+		file_parent.ptr = parent_data_to_free = xstrdup("");
 		file_parent.size = 0;
 	}
 
@@ -1075,6 +1076,7 @@ static int process_diff_filepair(struct rev_info *rev,
 
 	diff_ranges_release(&diff);
 
+	free(parent_data_to_free);
 	return ((*diff_out)->parent.nr > 0);
 }
 
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 09/27] line-log: always allocate the output prefix
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (7 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
                     ` (17 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2111 bytes --]

The returned string by `output_prefix()` is sometimes a string constant
and sometimes an allocated string. This has been fine until now because
we always leak the allocated strings, and thus we never tried to free
the string constant.

Fix the code to always return an allocated string and free the returned
value at all callsites.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/line-log.c b/line-log.c
index bd3e663c24..67c80b39a0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
 
 static char *output_prefix(struct diff_options *opt)
 {
-	char *prefix = "";
-
 	if (opt->output_prefix) {
 		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
-		prefix = sb->buf;
+		return sb->buf;
+	} else {
+		return xstrdup("");
 	}
-
-	return prefix;
 }
 
 static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
 
 	if (!pair || !diff)
-		return;
+		goto out;
 
 	if (pair->one->oid_valid)
 		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 				   c_context, c_reset, opt->file);
 	}
 
+out:
 	free(p_ends);
 	free(t_ends);
+	free(prefix);
 }
 
 /*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
  */
 static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
 {
-	fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+	char *prefix = output_prefix(&rev->diffopt);
+
+	fprintf(rev->diffopt.file, "%s\n", prefix);
+	free(prefix);
+
 	while (range) {
 		dump_diff_hacky_one(rev, range);
 		range = range->next;
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 10/27] entry: refactor how we remove items for delayed checkouts
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (8 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 09/27] line-log: always allocate the output prefix Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
                     ` (16 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2328 bytes --]

When finalizing a delayed checkout, we sort out several strings from the
passed-in string list by first assigning the empty string to those
filters and then calling `string_list_remove_empty_items()`. Assigning
the empty string will cause compiler warnings though as the string is
a `char *` once we enable `-Wwrite-strings`.

Refactor the code to use a `NULL` pointer with `filter_string_list()`
instead to avoid this warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 entry.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/entry.c b/entry.c
index b8c257f6f9..f291d8eee6 100644
--- a/entry.c
+++ b/entry.c
@@ -167,6 +167,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
 	return !available;
 }
 
+static int string_is_not_null(struct string_list_item *item, void *data UNUSED)
+{
+	return !!item->string;
+}
+
 int finish_delayed_checkout(struct checkout *state, int show_progress)
 {
 	int errs = 0;
@@ -189,7 +194,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 			if (!async_query_available_blobs(filter->string, &available_paths)) {
 				/* Filter reported an error */
 				errs = 1;
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 			if (available_paths.nr <= 0) {
@@ -199,7 +204,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 				 * filter from the list (see
 				 * "string_list_remove_empty_items" call below).
 				 */
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 
@@ -225,7 +230,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					 * Do not ask the filter for available blobs,
 					 * again, as the filter is likely buggy.
 					 */
-					filter->string = "";
+					filter->string = NULL;
 					continue;
 				}
 				ce = index_file_exists(state->istate, path->string,
@@ -239,7 +244,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					errs = 1;
 			}
 		}
-		string_list_remove_empty_items(&dco->filters, 0);
+
+		filter_string_list(&dco->filters, 0, string_is_not_null, NULL);
 	}
 	stop_progress(&progress);
 	string_list_clear(&dco->filters, 0);
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 11/27] ident: add casts for fallback name and GECOS
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (9 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
                     ` (15 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1095 bytes --]

In `xgetpwuid_self()`, we return a fallback identity when it was not
possible to look up the current identity. This fallback identity needs
to be internal and must never be written to by the calles as specified
by getpwuid(3P). As both the `pw_name` and `pw_gecos` fields are marked
as non-constant though, it will cause a warning to assign constant
strings to them once compiling with `-Wwrite-strings`.

Add explicit casts to avoid the warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 ident.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ident.c b/ident.c
index cc7afdbf81..caf41fb2a9 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,9 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
 	pw = getpwuid(getuid());
 	if (!pw) {
 		static struct passwd fallback;
-		fallback.pw_name = "unknown";
+		fallback.pw_name = (char *) "unknown";
 #ifndef NO_GECOS_IN_PWENT
-		fallback.pw_gecos = "Unknown";
+		fallback.pw_gecos = (char *) "Unknown";
 #endif
 		pw = &fallback;
 		if (is_bogus)
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 12/27] object-file: mark cached object buffers as const
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (10 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 17:54     ` Junio C Hamano
  2024-06-06 10:28   ` [PATCH v5 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
                     ` (14 subsequent siblings)
  26 siblings, 1 reply; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1900 bytes --]

The buffers of cached objects are never modified, but are still stored
as a non-constant pointer. This will cause a compiler warning once we
enable the `-Wwrite-strings` compiler warning as we assign an empty
constant string when initializing the static `empty_tree` cached object.

Convert the field to be constant. This requires us to shuffle around
the code a bit because we memcpy(3P) into the allocated buffer in
`pretend_object_file()`. This is easily fixed though by allocating the
buffer into a temporary variable first.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/object-file.c b/object-file.c
index 610b1f465c..6309b24387 100644
--- a/object-file.c
+++ b/object-file.c
@@ -277,7 +277,7 @@ int hash_algo_by_length(int len)
 static struct cached_object {
 	struct object_id oid;
 	enum object_type type;
-	void *buf;
+	const void *buf;
 	unsigned long size;
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
@@ -1778,17 +1778,22 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 			struct object_id *oid)
 {
 	struct cached_object *co;
+	char *co_buf;
+
+	co_buf = xmalloc(len);
+	memcpy(co_buf, buf, len);
 
 	hash_object_file(the_hash_algo, buf, len, type, oid);
 	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
-	    find_cached_object(oid))
+	    find_cached_object(oid)) {
+		free(co_buf);
 		return 0;
+	}
 	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
 	co = &cached_objects[cached_object_nr++];
 	co->size = len;
 	co->type = type;
-	co->buf = xmalloc(len);
-	memcpy(co->buf, buf, len);
+	co->buf = co_buf;
 	oidcpy(&co->oid, oid);
 	return 0;
 }
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 13/27] object-file: make `buf` parameter of `index_mem()` a constant
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (11 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
                     ` (13 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1863 bytes --]

The `buf` parameter of `index_mem()` is a non-constant string. This will
break once we enable `-Wwrite-strings` because we also pass constants
from at least one callsite.

Adapt the parameter to be a constant. As we cannot free the buffer
without casting now, this also requires us to move the lifetime of the
nested buffer around.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/object-file.c b/object-file.c
index 6309b24387..2d5bd3a211 100644
--- a/object-file.c
+++ b/object-file.c
@@ -2487,12 +2487,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
 }
 
 static int index_mem(struct index_state *istate,
-		     struct object_id *oid, void *buf, size_t size,
+		     struct object_id *oid,
+		     const void *buf, size_t size,
 		     enum object_type type,
 		     const char *path, unsigned flags)
 {
+	struct strbuf nbuf = STRBUF_INIT;
 	int ret = 0;
-	int re_allocated = 0;
 	int write_object = flags & HASH_WRITE_OBJECT;
 
 	if (!type)
@@ -2502,11 +2503,10 @@ static int index_mem(struct index_state *istate,
 	 * Convert blobs to git internal format
 	 */
 	if ((type == OBJ_BLOB) && path) {
-		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(istate, path, buf, size, &nbuf,
 				   get_conv_flags(flags))) {
-			buf = strbuf_detach(&nbuf, &size);
-			re_allocated = 1;
+			buf = nbuf.buf;
+			size = nbuf.len;
 		}
 	}
 	if (flags & HASH_FORMAT_CHECK) {
@@ -2523,8 +2523,8 @@ static int index_mem(struct index_state *istate,
 		ret = write_object_file(buf, size, type, oid);
 	else
 		hash_object_file(the_hash_algo, buf, size, type, oid);
-	if (re_allocated)
-		free(buf);
+
+	strbuf_release(&nbuf);
 	return ret;
 }
 
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 14/27] pretty: add casts for decoration option pointers
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (12 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:28   ` [PATCH v5 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
                     ` (12 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 947 bytes --]

The `struct decoration_options` have a prefix and suffix field which are
both non-constant, but we assign a constant pointer to them. This is
safe to do because we pass them to `format_decorations()`, which never
modifies these pointers, and then immediately discard the structure. Add
explicit casts to avoid compilation warnings with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 pretty.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pretty.c b/pretty.c
index ec05db5655..1df9d635fb 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1584,8 +1584,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 	case 'D':
 		{
 			const struct decoration_options opts = {
-				.prefix = "",
-				.suffix = ""
+				.prefix = (char *) "",
+				.suffix = (char *) "",
 			};
 
 			format_decorations(sb, commit, c->auto_color, &opts);
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 15/27] compat/win32: fix const-correctness with string constants
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (13 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
@ 2024-06-06 10:28   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
                     ` (11 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:28 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 3832 bytes --]

Adjust various places in our Win32 compatibility layer where we are not
assigning string constants to `const char *` variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 compat/basename.c | 16 ++++++++++++++--
 compat/mingw.c    | 28 ++++++++++++++++------------
 compat/winansi.c  |  2 +-
 3 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/compat/basename.c b/compat/basename.c
index 96bd9533b4..c33579ef61 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -10,7 +10,13 @@ char *gitbasename (char *path)
 		skip_dos_drive_prefix(&path);
 
 	if (!path || !*path)
-		return ".";
+		/*
+		 * basename(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) ".";
 
 	for (base = path; *path; path++) {
 		if (!is_dir_sep(*path))
@@ -34,7 +40,13 @@ char *gitdirname(char *path)
 	int dos_drive_prefix;
 
 	if (!p)
-		return ".";
+		/*
+		 * dirname(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) ".";
 
 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
 		goto dot;
diff --git a/compat/mingw.c b/compat/mingw.c
index 6b06ea540f..d378cd04cb 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2279,7 +2279,11 @@ struct passwd *getpwuid(int uid)
 	p->pw_name = user_name;
 	p->pw_gecos = get_extended_user_info(NameDisplay);
 	if (!p->pw_gecos)
-		p->pw_gecos = "unknown";
+		/*
+		 * Data returned by getpwuid(3P) is treated as internal and
+		 * must never be written to or freed.
+		 */
+		p->pw_gecos = (char *) "unknown";
 	p->pw_dir = NULL;
 
 	initialized = 1;
@@ -2800,16 +2804,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 			strbuf_addf(report, "'%s' is on a file system that does "
 				    "not record ownership\n", path);
 		} else if (report) {
-			LPSTR str1, str2, str3, str4, to_free1 = NULL,
-			    to_free3 = NULL, to_local_free2 = NULL,
-			    to_local_free4 = NULL;
+			PCSTR str1, str2, str3, str4;
+			LPSTR to_free1 = NULL, to_free3 = NULL,
+			    to_local_free2 = NULL, to_local_free4 = NULL;
 
-			if (user_sid_to_user_name(sid, &str1))
-				to_free1 = str1;
+			if (user_sid_to_user_name(sid, &to_free1))
+				str1 = to_free1;
 			else
 				str1 = "(inconvertible)";
-			if (ConvertSidToStringSidA(sid, &str2))
-				to_local_free2 = str2;
+			if (ConvertSidToStringSidA(sid, &to_local_free2))
+				str2 = to_local_free2;
 			else
 				str2 = "(inconvertible)";
 
@@ -2822,13 +2826,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 				str4 = "(invalid)";
 			} else {
 				if (user_sid_to_user_name(current_user_sid,
-							  &str3))
-					to_free3 = str3;
+							  &to_free3))
+					str3 = to_free3;
 				else
 					str3 = "(inconvertible)";
 				if (ConvertSidToStringSidA(current_user_sid,
-							   &str4))
-					to_local_free4 = str4;
+							   &to_local_free4))
+					str4 = to_local_free4;
 				else
 					str4 = "(inconvertible)";
 			}
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f684..575813bde8 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
 	/* convert utf-8 to utf-16 */
 	int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
 	if (wlen < 0) {
-		wchar_t *err = L"[invalid]";
+		const wchar_t *err = L"[invalid]";
 		WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
 		return;
 	}
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 16/27] http: do not assign string constant to non-const field
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (14 preceding siblings ...)
  2024-06-06 10:28   ` [PATCH v5 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
                     ` (10 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1260 bytes --]

In `write_accept_language()`, we put all acceptable languages into an
array. While all entries in that array are allocated strings, the final
entry in that array is a string constant. This is fine because we
explicitly skip over the last entry when freeing the array, but will
cause warnings once we enable `-Wwrite-strings`.

Adapt the code to also allocate the final entry.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 http.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/http.c b/http.c
index 67cc47d28f..2dea2d03da 100644
--- a/http.c
+++ b/http.c
@@ -1974,7 +1974,7 @@ static void write_accept_language(struct strbuf *buf)
 
 		/* add '*' */
 		REALLOC_ARRAY(language_tags, num_langs + 1);
-		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+		language_tags[num_langs++] = xstrdup("*");
 
 		/* compute decimal_places */
 		for (max_q = 1, decimal_places = 0;
@@ -2004,8 +2004,7 @@ static void write_accept_language(struct strbuf *buf)
 		}
 	}
 
-	/* free language tags -- last one is a static '*' */
-	for (i = 0; i < num_langs - 1; i++)
+	for (i = 0; i < num_langs; i++)
 		free(language_tags[i]);
 	free(language_tags);
 }
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 17/27] parse-options: cast long name for OPTION_ALIAS
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (15 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 18/27] send-pack: always allocate receive status Patrick Steinhardt
                     ` (9 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 792 bytes --]

We assign the long name for OPTION_ALIAS options to a non-constant value
field. We know that the variable will never be written to, but this will
cause warnings once we enable `-Wwrite-strings`.

Cast away the constness to be prepared for this change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 parse-options.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/parse-options.h b/parse-options.h
index bd62e20268..ae15342390 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
 	.type = OPTION_ALIAS, \
 	.short_name = (s), \
 	.long_name = (l), \
-	.value = (source_long_name), \
+	.value = (char *)(source_long_name), \
 }
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 18/27] send-pack: always allocate receive status
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (16 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
                     ` (8 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1586 bytes --]

In `receive_status()`, we record the reason why ref updates have been
rejected by the remote via the `remote_status`. But while we allocate
the assigned string when a reason was given, we assign a string constant
when no reason was given.

This has been working fine so far due to two reasons:

  - We don't ever free the refs in git-send-pack(1)'

  - Remotes always give a reason, at least as implemented by Git proper.

Adapt the code to always allocate the receive status string and free the
refs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/send-pack.c | 2 ++
 send-pack.c         | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaad09..17cae6bbbd 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
+	free_refs(remote_refs);
+	free_refs(local_refs);
 	return ret;
 }
diff --git a/send-pack.c b/send-pack.c
index 37f59d4f66..88e96d000b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -259,7 +259,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
 			if (p)
 				hint->remote_status = xstrdup(p);
 			else
-				hint->remote_status = "failed";
+				hint->remote_status = xstrdup("failed");
 		} else {
 			hint->status = REF_STATUS_OK;
 			hint->remote_status = xstrdup_or_null(p);
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 19/27] remote-curl: avoid assigning string constant to non-const variable
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (17 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 18/27] send-pack: always allocate receive status Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
                     ` (7 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 7752 bytes --]

When processing remote options, we split the option line into two by
searching for a space. If there is one, we replace the space with '\0',
otherwise we implicitly assume that the value is "true" and thus assign
a string constant.

As the return value of strchr(3P) weirdly enough is a `char *` even
though it gets a `const char *` as input, the assigned-to variable also
is a non-constant. This is fine though because the argument is in fact
an allocated string, and thus we are allowed to modify it. But this will
break once we enable `-Wwrite-strings`.

Refactor the code stop splitting the fields with '\0' altogether.
Instead, we can pass the length of the option name to `set_option()` and
then use strncmp(3P) instead of strcmp(3P).

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c | 53 ++++++++++++++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index cae98384da..d0f767df8e 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -58,9 +58,9 @@ struct options {
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
 
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
 {
-	if (!strcmp(name, "verbosity")) {
+	if (!strncmp(name, "verbosity", namelen)) {
 		char *end;
 		int v = strtol(value, &end, 10);
 		if (value == end || *end)
@@ -68,7 +68,7 @@ static int set_option(const char *name, const char *value)
 		options.verbosity = v;
 		return 0;
 	}
-	else if (!strcmp(name, "progress")) {
+	else if (!strncmp(name, "progress", namelen)) {
 		if (!strcmp(value, "true"))
 			options.progress = 1;
 		else if (!strcmp(value, "false"))
@@ -77,7 +77,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "depth")) {
+	else if (!strncmp(name, "depth", namelen)) {
 		char *end;
 		unsigned long v = strtoul(value, &end, 10);
 		if (value == end || *end)
@@ -85,15 +85,15 @@ static int set_option(const char *name, const char *value)
 		options.depth = v;
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-since")) {
+	else if (!strncmp(name, "deepen-since", namelen)) {
 		options.deepen_since = xstrdup(value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-not")) {
+	else if (!strncmp(name, "deepen-not", namelen)) {
 		string_list_append(&options.deepen_not, value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-relative")) {
+	else if (!strncmp(name, "deepen-relative", namelen)) {
 		if (!strcmp(value, "true"))
 			options.deepen_relative = 1;
 		else if (!strcmp(value, "false"))
@@ -102,7 +102,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "followtags")) {
+	else if (!strncmp(name, "followtags", namelen)) {
 		if (!strcmp(value, "true"))
 			options.followtags = 1;
 		else if (!strcmp(value, "false"))
@@ -111,7 +111,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "dry-run")) {
+	else if (!strncmp(name, "dry-run", namelen)) {
 		if (!strcmp(value, "true"))
 			options.dry_run = 1;
 		else if (!strcmp(value, "false"))
@@ -120,7 +120,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "check-connectivity")) {
+	else if (!strncmp(name, "check-connectivity", namelen)) {
 		if (!strcmp(value, "true"))
 			options.check_self_contained_and_connected = 1;
 		else if (!strcmp(value, "false"))
@@ -129,7 +129,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "cas")) {
+	else if (!strncmp(name, "cas", namelen)) {
 		struct strbuf val = STRBUF_INIT;
 		strbuf_addstr(&val, "--force-with-lease=");
 		if (*value != '"')
@@ -139,7 +139,7 @@ static int set_option(const char *name, const char *value)
 		string_list_append(&cas_options, val.buf);
 		strbuf_release(&val);
 		return 0;
-	} else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+	} else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
 		if (!strcmp(value, "true"))
 			options.force_if_includes = 1;
 		else if (!strcmp(value, "false"))
@@ -147,7 +147,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "cloning")) {
+	} else if (!strncmp(name, "cloning", namelen)) {
 		if (!strcmp(value, "true"))
 			options.cloning = 1;
 		else if (!strcmp(value, "false"))
@@ -155,7 +155,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "update-shallow")) {
+	} else if (!strncmp(name, "update-shallow", namelen)) {
 		if (!strcmp(value, "true"))
 			options.update_shallow = 1;
 		else if (!strcmp(value, "false"))
@@ -163,7 +163,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "pushcert")) {
+	} else if (!strncmp(name, "pushcert", namelen)) {
 		if (!strcmp(value, "true"))
 			options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
 		else if (!strcmp(value, "false"))
@@ -173,7 +173,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "atomic")) {
+	} else if (!strncmp(name, "atomic", namelen)) {
 		if (!strcmp(value, "true"))
 			options.atomic = 1;
 		else if (!strcmp(value, "false"))
@@ -181,7 +181,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "push-option")) {
+	} else if (!strncmp(name, "push-option", namelen)) {
 		if (*value != '"')
 			string_list_append(&options.push_options, value);
 		else {
@@ -192,7 +192,7 @@ static int set_option(const char *name, const char *value)
 						 strbuf_detach(&unquoted, NULL));
 		}
 		return 0;
-	} else if (!strcmp(name, "family")) {
+	} else if (!strncmp(name, "family", namelen)) {
 		if (!strcmp(value, "ipv4"))
 			git_curl_ipresolve = CURL_IPRESOLVE_V4;
 		else if (!strcmp(value, "ipv6"))
@@ -202,16 +202,16 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "from-promisor")) {
+	} else if (!strncmp(name, "from-promisor", namelen)) {
 		options.from_promisor = 1;
 		return 0;
-	} else if (!strcmp(name, "refetch")) {
+	} else if (!strncmp(name, "refetch", namelen)) {
 		options.refetch = 1;
 		return 0;
-	} else if (!strcmp(name, "filter")) {
+	} else if (!strncmp(name, "filter", namelen)) {
 		options.filter = xstrdup(value);
 		return 0;
-	} else if (!strcmp(name, "object-format")) {
+	} else if (!strncmp(name, "object-format", namelen)) {
 		options.object_format = 1;
 		if (strcmp(value, "true"))
 			die(_("unknown value for object-format: %s"), value);
@@ -1588,15 +1588,16 @@ int cmd_main(int argc, const char **argv)
 			parse_push(&buf);
 
 		} else if (skip_prefix(buf.buf, "option ", &arg)) {
-			char *value = strchr(arg, ' ');
+			const char *value = strchrnul(arg, ' ');
+			size_t arglen = value - arg;
 			int result;
 
-			if (value)
-				*value++ = '\0';
+			if (*value)
+				value++; /* skip over SP */
 			else
 				value = "true";
 
-			result = set_option(arg, value);
+			result = set_option(arg, arglen, value);
 			if (!result)
 				printf("ok\n");
 			else if (result < 0)
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 20/27] revision: always store allocated strings in output encoding
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (18 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
                     ` (6 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2531 bytes --]

The `git_log_output_encoding` variable can be set via the `--encoding=`
option. When doing so, we conditionally either assign it to the passed
value, or if the value is "none" we assign it the empty string.
Depending on which of the both code paths we pick though, the variable
may end up being assigned either an allocated string or a string
constant.

This is somewhat risky and may easily lead to bugs when a different code
path may want to reassign a new value to it, freeing the previous value.
We already to this when parsing the "i18n.logoutputencoding" config in
`git_default_i18n_config()`. But because the config is typically parsed
before we parse command line options this has been fine so far.

Regardless of that, safeguard the code such that the variable always
contains an allocated string. While at it, also free the old value in
case there was any to plug a potential memory leak.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 revision.c             | 3 ++-
 t/t3900-i18n-commit.sh | 1 +
 t/t3901-i18n-patch.sh  | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 7ddf0f151a..2ee6886078 100644
--- a/revision.c
+++ b/revision.c
@@ -2650,10 +2650,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		free(git_log_output_encoding);
 		if (strcmp(optarg, "none"))
 			git_log_output_encoding = xstrdup(optarg);
 		else
-			git_log_output_encoding = "";
+			git_log_output_encoding = xstrdup("");
 		return argcount;
 	} else if (!strcmp(arg, "--reverse")) {
 		revs->reverse ^= 1;
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09cfd9..db7b403bc1 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
 
 test_description='commit and log output encodings'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78829..5f0b9afc3f 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@ test_description='i18n settings and format-patch | am pipe'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_encoding () {
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 21/27] mailmap: always store allocated strings in mailmap blob
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (19 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
                     ` (5 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Same as with the preceding commit, the `git_mailmap_blob` may sometimes
contain an allocated string and sometimes it may contain a string
constant. This is risky and can easily lead to bugs in case the variable
is getting re-assigned, where the code may then try to free the previous
value to avoid memory leaks.

Safeguard the code by always storing allocated strings in the variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 mailmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mailmap.c b/mailmap.c
index b2efe29b3d..3d1e092fef 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -216,7 +216,7 @@ int read_mailmap(struct string_list *map)
 	map->cmp = namemap_cmp;
 
 	if (!git_mailmap_blob && is_bare_repository())
-		git_mailmap_blob = "HEAD:.mailmap";
+		git_mailmap_blob = xstrdup("HEAD:.mailmap");
 
 	if (!startup_info->have_repository || !is_bare_repository())
 		err |= read_mailmap_file(map, ".mailmap",
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 22/27] imap-send: drop global `imap_server_conf` variable
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (20 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
                     ` (4 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 7200 bytes --]

In "imap-send.c", we have a global `sturct imap_server_conf` variable
that keeps track of the configuration of the IMAP server. This variable
is being populated mostly via the Git configuration.

Refactor the code to allocate the structure on the stack instead of
having it globally. This change allows us to track its lifetime more
closely.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 57 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 8b723b34a5..67a7a6c456 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -82,10 +82,6 @@ struct imap_server_conf {
 	char *auth_method;
 };
 
-static struct imap_server_conf server = {
-	.ssl_verify = 1,
-};
-
 struct imap_socket {
 	int fd[2];
 	SSL *ssl;
@@ -110,6 +106,7 @@ struct imap {
 };
 
 struct imap_store {
+	const struct imap_server_conf *cfg;
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	int uidvalidity;
@@ -194,8 +191,8 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
 
 #ifdef NO_OPENSSL
 static int ssl_socket_connect(struct imap_socket *sock UNUSED,
-			      int use_tls_only UNUSED,
-			      int verify UNUSED)
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only UNUSED)
 {
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
@@ -250,7 +247,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
 		     cname, hostname);
 }
 
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock,
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 	const SSL_METHOD *meth;
@@ -279,7 +278,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	if (use_tls_only)
 		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
-	if (verify)
+	if (cfg->ssl_verify)
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
 
 	if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +305,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	 * OpenSSL does not document this function, but the implementation
 	 * returns 1 on success, 0 on failure after calling SSLerr().
 	 */
-	ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+	ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
 	if (ret != 1)
-		warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+		warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
 #endif
 
 	ret = SSL_connect(sock->ssl);
@@ -317,12 +316,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 		return -1;
 	}
 
-	if (verify) {
+	if (cfg->ssl_verify) {
 		/* make sure the hostname matches that of the certificate */
 		cert = SSL_get_peer_certificate(sock->ssl);
 		if (!cert)
 			return error("unable to get peer certificate.");
-		if (verify_hostname(cert, server.host) < 0)
+		if (verify_hostname(cert, cfg->host) < 0)
 			return -1;
 	}
 
@@ -895,7 +894,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 	int ret;
 	char *response;
 
-	response = cram(prompt, server.user, server.pass);
+	response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
 
 	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
 	if (ret != strlen(response))
@@ -935,6 +934,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 
 	CALLOC_ARRAY(ctx, 1);
 
+	ctx->cfg = srvc;
 	ctx->imap = CALLOC_ARRAY(imap, 1);
 	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
 	imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1035,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		imap->buf.sock.fd[1] = dup(s);
 
 		if (srvc->use_ssl &&
-		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+		    ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
 			close(s);
 			goto bail;
 		}
@@ -1068,8 +1068,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		if (!srvc->use_ssl && CAP(STARTTLS)) {
 			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
 				goto bail;
-			if (ssl_socket_connect(&imap->buf.sock, 1,
-					       srvc->ssl_verify))
+			if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
 				goto bail;
 			/* capabilities may have changed, so get the new capabilities */
 			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1299,23 +1298,24 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 static int git_imap_config(const char *var, const char *val,
 			   const struct config_context *ctx, void *cb)
 {
+	struct imap_server_conf *cfg = cb;
 
 	if (!strcmp("imap.sslverify", var))
-		server.ssl_verify = git_config_bool(var, val);
+		cfg->ssl_verify = git_config_bool(var, val);
 	else if (!strcmp("imap.preformattedhtml", var))
-		server.use_html = git_config_bool(var, val);
+		cfg->use_html = git_config_bool(var, val);
 	else if (!strcmp("imap.folder", var))
-		return git_config_string(&server.folder, var, val);
+		return git_config_string(&cfg->folder, var, val);
 	else if (!strcmp("imap.user", var))
-		return git_config_string(&server.user, var, val);
+		return git_config_string(&cfg->user, var, val);
 	else if (!strcmp("imap.pass", var))
-		return git_config_string(&server.pass, var, val);
+		return git_config_string(&cfg->pass, var, val);
 	else if (!strcmp("imap.tunnel", var))
-		return git_config_string(&server.tunnel, var, val);
+		return git_config_string(&cfg->tunnel, var, val);
 	else if (!strcmp("imap.authmethod", var))
-		return git_config_string(&server.auth_method, var, val);
+		return git_config_string(&cfg->auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val, ctx->kvi);
+		cfg->port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
@@ -1324,11 +1324,11 @@ static int git_imap_config(const char *var, const char *val,
 				val += 5;
 			else if (starts_with(val, "imaps:")) {
 				val += 6;
-				server.use_ssl = 1;
+				cfg->use_ssl = 1;
 			}
 			if (starts_with(val, "//"))
 				val += 2;
-			server.host = xstrdup(val);
+			cfg->host = xstrdup(val);
 		}
 	} else
 		return git_default_config(var, val, ctx, cb);
@@ -1497,12 +1497,15 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
 
 int cmd_main(int argc, const char **argv)
 {
+	struct imap_server_conf server = {
+		.ssl_verify = 1,
+	};
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
 
 	setup_git_directory_gently(&nongit_ok);
-	git_config(git_imap_config, NULL);
+	git_config(git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
 
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 23/27] imap-send: fix leaking memory in `imap_server_conf`
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (21 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
                     ` (3 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 4324 bytes --]

We never free any of the config strings that we populate into the
`struct imap_server_conf`. Fix this by creating a common exit path where
we can free resources.

While at it, drop the unused member `imap_server_conf::name`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 65 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 23 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 67a7a6c456..da3e7ec17e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,7 +69,6 @@ static void imap_warn(const char *, ...);
 static char *next_arg(char **);
 
 struct imap_server_conf {
-	const char *name;
 	char *tunnel;
 	char *host;
 	int port;
@@ -1300,23 +1299,28 @@ static int git_imap_config(const char *var, const char *val,
 {
 	struct imap_server_conf *cfg = cb;
 
-	if (!strcmp("imap.sslverify", var))
+	if (!strcmp("imap.sslverify", var)) {
 		cfg->ssl_verify = git_config_bool(var, val);
-	else if (!strcmp("imap.preformattedhtml", var))
+	} else if (!strcmp("imap.preformattedhtml", var)) {
 		cfg->use_html = git_config_bool(var, val);
-	else if (!strcmp("imap.folder", var))
+	} else if (!strcmp("imap.folder", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->folder, var, val);
-	else if (!strcmp("imap.user", var))
+	} else if (!strcmp("imap.user", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->user, var, val);
-	else if (!strcmp("imap.pass", var))
+	} else if (!strcmp("imap.pass", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->pass, var, val);
-	else if (!strcmp("imap.tunnel", var))
+	} else if (!strcmp("imap.tunnel", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->tunnel, var, val);
-	else if (!strcmp("imap.authmethod", var))
+	} else if (!strcmp("imap.authmethod", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->auth_method, var, val);
-	else if (!strcmp("imap.port", var))
+	} else if (!strcmp("imap.port", var)) {
 		cfg->port = git_config_int(var, val, ctx->kvi);
-	else if (!strcmp("imap.host", var)) {
+	} else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
 		} else {
@@ -1330,8 +1334,9 @@ static int git_imap_config(const char *var, const char *val,
 				val += 2;
 			cfg->host = xstrdup(val);
 		}
-	} else
+	} else {
 		return git_default_config(var, val, ctx, cb);
+	}
 
 	return 0;
 }
@@ -1503,6 +1508,7 @@ int cmd_main(int argc, const char **argv)
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
+	int ret;
 
 	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, &server);
@@ -1529,42 +1535,55 @@ int cmd_main(int argc, const char **argv)
 
 	if (!server.folder) {
 		fprintf(stderr, "no imap store specified\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
 			fprintf(stderr, "no imap host specified\n");
-			return 1;
+			ret = 1;
+			goto out;
 		}
-		server.host = "tunnel";
+		server.host = xstrdup("tunnel");
 	}
 
 	/* read the messages */
 	if (strbuf_read(&all_msgs, 0, 0) < 0) {
 		error_errno(_("could not read from stdin"));
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	if (all_msgs.len == 0) {
 		fprintf(stderr, "nothing to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	total = count_messages(&all_msgs);
 	if (!total) {
 		fprintf(stderr, "no messages to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	/* write it to the imap server */
 
 	if (server.tunnel)
-		return append_msgs_to_imap(&server, &all_msgs, total);
-
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
 #ifdef USE_CURL_FOR_IMAP_SEND
-	if (use_curl)
-		return curl_append_msgs_to_imap(&server, &all_msgs, total);
+	else if (use_curl)
+		ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
 #endif
-
-	return append_msgs_to_imap(&server, &all_msgs, total);
+	else
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
+
+out:
+	free(server.tunnel);
+	free(server.host);
+	free(server.folder);
+	free(server.user);
+	free(server.pass);
+	free(server.auth_method);
+	return ret;
 }
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (22 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
                     ` (2 subsequent siblings)
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2428 bytes --]

The `struct rebase_options::default_backend` field is a non-constant
string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
Fix this by using `xstrdup()` to assign the variable and introduce a new
function `rebase_options_release()` that releases memory held by the
structure, including the newly-allocated variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 14d4f0a5e6..adc990b55e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -135,7 +135,7 @@ struct rebase_options {
 		.type = REBASE_UNSPECIFIED,	  	\
 		.empty = EMPTY_UNSPECIFIED,	  	\
 		.keep_empty = 1,			\
-		.default_backend = "merge",	  	\
+		.default_backend = xstrdup("merge"),  	\
 		.flags = REBASE_NO_QUIET, 		\
 		.git_am_opts = STRVEC_INIT,		\
 		.exec = STRING_LIST_INIT_NODUP,		\
@@ -151,6 +151,19 @@ struct rebase_options {
 		.strategy_opts = STRING_LIST_INIT_NODUP,\
 	}
 
+static void rebase_options_release(struct rebase_options *opts)
+{
+	free(opts->default_backend);
+	free(opts->reflog_action);
+	free(opts->head_name);
+	strvec_clear(&opts->git_am_opts);
+	free(opts->gpg_sign_opt);
+	string_list_clear(&opts->exec, 0);
+	free(opts->strategy);
+	string_list_clear(&opts->strategy_opts, 0);
+	strbuf_release(&opts->git_format_patch_opt);
+}
+
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 {
 	struct replay_opts replay = REPLAY_OPTS_INIT;
@@ -796,6 +809,7 @@ static int rebase_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "rebase.backend")) {
+		FREE_AND_NULL(opts->default_backend);
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
@@ -1833,14 +1847,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 cleanup:
 	strbuf_release(&buf);
 	strbuf_release(&revisions);
-	free(options.reflog_action);
-	free(options.head_name);
-	strvec_clear(&options.git_am_opts);
-	free(options.gpg_sign_opt);
-	string_list_clear(&options.exec, 0);
-	free(options.strategy);
-	string_list_clear(&options.strategy_opts, 0);
-	strbuf_release(&options.git_format_patch_opt);
+	rebase_options_release(&options);
 	free(squash_onto_name);
 	free(keep_base_onto_name);
 	return !!ret;
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 25/27] builtin/rebase: always store allocated string in `options.strategy`
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (23 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2360 bytes --]

The `struct rebase_options::strategy` field is a `char *`, but we do end
up assigning string constants to it in two cases:

  - When being passed a `--strategy=` option via the command line.

  - When being passed a strategy option via `--strategy-option=`, but
    not a strategy.

This will cause warnings once we enable `-Wwrite-strings`.

Ideally, we'd just convert the field to be a `const char *`. But we also
assign to this field via the GIT_TEST_MERGE_ALGORITHM envvar, which we
have to strdup(3P) into it.

Instead, refactor the code to make sure that we only ever assign
allocated strings to this field.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index adc990b55e..4506bae768 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1061,6 +1061,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
 	const char *branch_name;
+	const char *strategy_opt = NULL;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
 	int ok_to_skip_pre_rebase = 0;
@@ -1175,7 +1176,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
-		OPT_STRING('s', "strategy", &options.strategy,
+		OPT_STRING('s', "strategy", &strategy_opt,
 			   N_("strategy"), N_("use the given merge strategy")),
 		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
 				N_("option"),
@@ -1484,13 +1485,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	if (options.strategy_opts.nr && !options.strategy)
-		options.strategy = "ort";
-
-	if (options.strategy) {
-		options.strategy = xstrdup(options.strategy);
+	if (strategy_opt)
+		options.strategy = xstrdup(strategy_opt);
+	else if (options.strategy_opts.nr && !options.strategy)
+		options.strategy = xstrdup("ort");
+	if (options.strategy)
 		imply_merge(&options, "--strategy");
-	}
 
 	if (options.root && !options.onto_name)
 		imply_merge(&options, "--root without --onto");
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 26/27] builtin/merge: always store allocated strings in `pull_twohead`
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (24 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  2024-06-06 10:29   ` [PATCH v5 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2451 bytes --]

The `pull_twohead` configuration may sometimes contain an allocated
string, and sometimes it may contain a string constant. Refactor this to
instead always store an allocated string such that we can release its
resources without risk.

While at it, manage the lifetime of other config strings, as well. Note
that we explicitly don't free `cleanup_arg` here. This is because the
variable may be assigned a string constant via command line options.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/merge.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/builtin/merge.c b/builtin/merge.c
index daed2d4e1e..fb3eb15b89 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -611,17 +611,19 @@ static int git_merge_config(const char *k, const char *v,
 		return 0;
 	}
 
-	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
 		show_diffstat = git_config_bool(k, v);
-	else if (!strcmp(k, "merge.verifysignatures"))
+	} else if (!strcmp(k, "merge.verifysignatures")) {
 		verify_signatures = git_config_bool(k, v);
-	else if (!strcmp(k, "pull.twohead"))
+	} else if (!strcmp(k, "pull.twohead")) {
+		FREE_AND_NULL(pull_twohead);
 		return git_config_string(&pull_twohead, k, v);
-	else if (!strcmp(k, "pull.octopus"))
+	} else if (!strcmp(k, "pull.octopus")) {
+		FREE_AND_NULL(pull_octopus);
 		return git_config_string(&pull_octopus, k, v);
-	else if (!strcmp(k, "commit.cleanup"))
+	} else if (!strcmp(k, "commit.cleanup")) {
 		return git_config_string(&cleanup_arg, k, v);
-	else if (!strcmp(k, "merge.ff")) {
+	} else if (!strcmp(k, "merge.ff")) {
 		int boolval = git_parse_maybe_bool(v);
 		if (0 <= boolval) {
 			fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -1294,7 +1296,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (!pull_twohead) {
 		char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
 		if (default_strategy && !strcmp(default_strategy, "ort"))
-			pull_twohead = "ort";
+			pull_twohead = xstrdup("ort");
 	}
 
 	init_diff_ui_defaults();
@@ -1793,6 +1795,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
+	free(pull_twohead);
+	free(pull_octopus);
 	discard_index(the_repository->index);
 	return ret;
 }
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v5 27/27] config.mak.dev: enable `-Wwrite-strings` warning
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (25 preceding siblings ...)
  2024-06-06 10:29   ` [PATCH v5 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
@ 2024-06-06 10:29   ` Patrick Steinhardt
  26 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:29 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

Writing to string constants is undefined behaviour and must be avoided
in C. Even so, the compiler does not help us with this by default
because those constants are not in fact marked as `const`. This makes it
rather easy to accidentally assign a constant to a non-const variable or
field and then later on try to either free it or write to it.

Enable `-Wwrite-strings` to catch such mistakes. With this warning
enabled, the type of string constants is changed to `const char[]` and
will thus cause compiler warnings when being assigned to non-const
fields and variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.mak.dev | 1 +
 1 file changed, 1 insertion(+)

diff --git a/config.mak.dev b/config.mak.dev
index 981304727c..1ce4c70613 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -37,6 +37,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
 DEVELOPER_CFLAGS += -Wstrict-prototypes
 DEVELOPER_CFLAGS += -Wunused
 DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
 DEVELOPER_CFLAGS += -fno-common
 
 ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
-- 
2.45.2.409.g7b0defb391.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-06-05 10:46             ` Jeff King
  2024-06-05 17:13               ` Junio C Hamano
@ 2024-06-06 10:36               ` Patrick Steinhardt
  1 sibling, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-06 10:36 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git

[-- Attachment #1: Type: text/plain, Size: 1146 bytes --]

On Wed, Jun 05, 2024 at 06:46:46AM -0400, Jeff King wrote:
> On Fri, May 31, 2024 at 08:27:13AM -0700, Junio C Hamano wrote:
> 
> > I wonder if we can do something to separate these two concerns
> > apart, using a trick similar to what we often use with an extra
> > variable "to_free".  Doing so would bloat the refspec_item, but
> > unlike the references themselves, there won't be thousands of them,
> > so it may not be an issue, perhaps?
> 
> I had a similar thought while looking at this spot a while ago, so I dug
> this attempt out of my stash. It's quite ugly, as you need to keep the
> storage pointer and the const pointer in sync. Especially because
> there's a lot of clever pointer indirection via match_name_with_pattern().
> 
> So I don't think it's the best way to refactor this code, but I present
> it here for your amusement/disgust. This is on top of what you have
> queued in ps/no-writable-strings.

Just for the record: I'll keep such a refactoring out of this patch
series as it is already big enough. I do agree though that we should
ideally fix this interface in a subsequent iteration.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 12/27] object-file: mark cached object buffers as const
  2024-06-06  6:10       ` Junio C Hamano
  2024-06-06 10:03         ` Patrick Steinhardt
@ 2024-06-06 16:25         ` Junio C Hamano
  2024-06-07  4:52           ` Patrick Steinhardt
  1 sibling, 1 reply; 205+ messages in thread
From: Junio C Hamano @ 2024-06-06 16:25 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King, Eric Sunshine

Junio C Hamano <gitster@pobox.com> writes:

> Indeed, that seems to be the case.  With the attached at the tip of
> the branch and rebuilding 'seen' seems to pass these 6130, 7010, 8002
> tests with SANTIZE=leak.
>
> From f307bbf7bd317d90db29bd1589b49e84b9e37e88 Mon Sep 17 00:00:00 2001
> From: Junio C Hamano <gitster@pobox.com>
> Date: Wed, 5 Jun 2024 23:03:34 -0700
> Subject: [PATCH] fixup! object-file: mark cached object buffers as const
>
> ---
>  object-file.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/object-file.c b/object-file.c
> index b5b5a59dc6..2d5bd3a211 100644
> --- a/object-file.c
> +++ b/object-file.c
> @@ -1785,8 +1785,10 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
>  
>  	hash_object_file(the_hash_algo, buf, len, type, oid);
>  	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
> -	    find_cached_object(oid))
> +	    find_cached_object(oid)) {
> +		free(co_buf);
>  		return 0;
> +	}
>  	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
>  	co = &cached_objects[cached_object_nr++];
>  	co->size = len;

Wait.  Why do we need to allocate co_buf that early in the first
place?  IOW, shouldn't the fixup be more like this?

 object-file.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git c/object-file.c w/object-file.c
index b5b5a59dc6..0b58751f94 100644
--- c/object-file.c
+++ w/object-file.c
@@ -1780,9 +1780,6 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 	struct cached_object *co;
 	char *co_buf;
 
-	co_buf = xmalloc(len);
-	memcpy(co_buf, buf, len);
-
 	hash_object_file(the_hash_algo, buf, len, type, oid);
 	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
 	    find_cached_object(oid))
@@ -1791,6 +1788,8 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 	co = &cached_objects[cached_object_nr++];
 	co->size = len;
 	co->type = type;
+	co_buf = xmalloc(len);
+	memcpy(co_buf, buf, len);
 	co->buf = co_buf;
 	oidcpy(&co->oid, oid);
 	return 0;

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH v5 12/27] object-file: mark cached object buffers as const
  2024-06-06 10:28   ` [PATCH v5 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
@ 2024-06-06 17:54     ` Junio C Hamano
  0 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-06-06 17:54 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King, Eric Sunshine

Patrick Steinhardt <ps@pks.im> writes:

> @@ -1778,17 +1778,22 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
>  			struct object_id *oid)
>  {
>  	struct cached_object *co;
> +	char *co_buf;
> +
> +	co_buf = xmalloc(len);
> +	memcpy(co_buf, buf, len);

I do not see why we need to do this so early.  The copy is not used
or buf gets modified by the call to hash_object_file(), so ...

>  	hash_object_file(the_hash_algo, buf, len, type, oid);
>  	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
> -	    find_cached_object(oid))
> +	    find_cached_object(oid)) {
> +		free(co_buf);
>  		return 0;
> +	}
>  	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
>  	co = &cached_objects[cached_object_nr++];
>  	co->size = len;
>  	co->type = type;
> -	co->buf = xmalloc(len);
> -	memcpy(co->buf, buf, len);
> +	co->buf = co_buf;

... wouldn't this be a better place to do the "copy to the heap
memory pointed by a writable pointer and then point that piece of
memory with a read-only pointer" pattern?

>  	oidcpy(&co->oid, oid);
>  	return 0;
>  }

^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH v4 12/27] object-file: mark cached object buffers as const
  2024-06-06 16:25         ` Junio C Hamano
@ 2024-06-07  4:52           ` Patrick Steinhardt
  0 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  4:52 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Jeff King, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1552 bytes --]

On Thu, Jun 06, 2024 at 09:25:20AM -0700, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
> > Indeed, that seems to be the case.  With the attached at the tip of
> > the branch and rebuilding 'seen' seems to pass these 6130, 7010, 8002
> > tests with SANTIZE=leak.
> >
> > From f307bbf7bd317d90db29bd1589b49e84b9e37e88 Mon Sep 17 00:00:00 2001
> > From: Junio C Hamano <gitster@pobox.com>
> > Date: Wed, 5 Jun 2024 23:03:34 -0700
> > Subject: [PATCH] fixup! object-file: mark cached object buffers as const
> >
> > ---
> >  object-file.c | 4 +++-
> >  1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/object-file.c b/object-file.c
> > index b5b5a59dc6..2d5bd3a211 100644
> > --- a/object-file.c
> > +++ b/object-file.c
> > @@ -1785,8 +1785,10 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
> >  
> >  	hash_object_file(the_hash_algo, buf, len, type, oid);
> >  	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
> > -	    find_cached_object(oid))
> > +	    find_cached_object(oid)) {
> > +		free(co_buf);
> >  		return 0;
> > +	}
> >  	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
> >  	co = &cached_objects[cached_object_nr++];
> >  	co->size = len;
> 
> Wait.  Why do we need to allocate co_buf that early in the first
> place?  IOW, shouldn't the fixup be more like this?

That is of course the much more elegant solution here. Will adapt.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v6 00/27] Compile with `-Wwrite-strings`
  2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
                   ` (23 preceding siblings ...)
  2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
@ 2024-06-07  6:37 ` Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
                     ` (27 more replies)
  24 siblings, 28 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 8541 bytes --]

Hi,

this is v6 of my patch series that starts to compile our codebase with
`-Wwrite-strings`. This warning turns the type of string constants from
`char []` to `const char []` and should thus help us to detect cases
where we may accidentally write to or free such a string constant.

The only change is in patch 12, where we now allocate a buffer later in
time to avoid a useless allocation and the need for a call to free.

Patrick

Patrick Steinhardt (27):
  global: improve const correctness when assigning string constants
  global: convert intentionally-leaking config strings to consts
  refs/reftable: stop micro-optimizing refname allocations on copy
  reftable: cast away constness when assigning constants to records
  refspec: remove global tag refspec structure
  builtin/remote: cast away constness in `get_head_names()`
  diff: cast string constant in `fill_textconv()`
  line-log: stop assigning string constant to file parent buffer
  line-log: always allocate the output prefix
  entry: refactor how we remove items for delayed checkouts
  ident: add casts for fallback name and GECOS
  object-file: mark cached object buffers as const
  object-file: make `buf` parameter of `index_mem()` a constant
  pretty: add casts for decoration option pointers
  compat/win32: fix const-correctness with string constants
  http: do not assign string constant to non-const field
  parse-options: cast long name for OPTION_ALIAS
  send-pack: always allocate receive status
  remote-curl: avoid assigning string constant to non-const variable
  revision: always store allocated strings in output encoding
  mailmap: always store allocated strings in mailmap blob
  imap-send: drop global `imap_server_conf` variable
  imap-send: fix leaking memory in `imap_server_conf`
  builtin/rebase: do not assign default backend to non-constant field
  builtin/rebase: always store allocated string in `options.strategy`
  builtin/merge: always store allocated strings in `pull_twohead`
  config.mak.dev: enable `-Wwrite-strings` warning

 builtin/bisect.c             |   3 +-
 builtin/blame.c              |   2 +-
 builtin/bugreport.c          |   2 +-
 builtin/check-ignore.c       |   4 +-
 builtin/clone.c              |  14 ++--
 builtin/commit.c             |   6 +-
 builtin/diagnose.c           |   2 +-
 builtin/fetch.c              |  11 ++-
 builtin/log.c                |   2 +-
 builtin/mailsplit.c          |   4 +-
 builtin/merge.c              |  18 +++--
 builtin/pull.c               |  52 +++++++-------
 builtin/rebase.c             |  39 ++++++-----
 builtin/receive-pack.c       |   4 +-
 builtin/remote.c             |  12 ++--
 builtin/revert.c             |   2 +-
 builtin/send-pack.c          |   2 +
 compat/basename.c            |  16 ++++-
 compat/mingw.c               |  28 ++++----
 compat/regex/regcomp.c       |   2 +-
 compat/winansi.c             |   2 +-
 config.mak.dev               |   1 +
 diff.c                       |   6 +-
 diffcore-rename.c            |   6 +-
 entry.c                      |  14 ++--
 fmt-merge-msg.c              |   2 +-
 fsck.c                       |   2 +-
 fsck.h                       |   2 +-
 gpg-interface.c              |   6 +-
 http-backend.c               |   2 +-
 http.c                       |   5 +-
 ident.c                      |   4 +-
 imap-send.c                  | 130 ++++++++++++++++++++---------------
 line-log.c                   |  22 +++---
 mailmap.c                    |   2 +-
 merge-ll.c                   |  11 ++-
 object-file.c                |  22 +++---
 parse-options.h              |   2 +-
 pretty.c                     |   6 +-
 refs.c                       |   2 +-
 refs.h                       |   2 +-
 refs/reftable-backend.c      |  28 ++++----
 refspec.c                    |  13 ----
 refspec.h                    |   1 -
 reftable/basics.c            |  15 ++--
 reftable/basics.h            |   4 +-
 reftable/basics_test.c       |   4 +-
 reftable/block_test.c        |   2 +-
 reftable/merged_test.c       |  44 ++++++------
 reftable/readwrite_test.c    |  32 ++++-----
 reftable/record.c            |   6 +-
 reftable/stack.c             |  10 +--
 reftable/stack_test.c        |  56 +++++++--------
 remote-curl.c                |  53 +++++++-------
 revision.c                   |   3 +-
 run-command.c                |   2 +-
 send-pack.c                  |   2 +-
 t/helper/test-hashmap.c      |   3 +-
 t/helper/test-json-writer.c  |  10 +--
 t/helper/test-regex.c        |   4 +-
 t/helper/test-rot13-filter.c |   5 +-
 t/t3900-i18n-commit.sh       |   1 +
 t/t3901-i18n-patch.sh        |   1 +
 t/unit-tests/t-strbuf.c      |  10 +--
 trailer.c                    |   2 +-
 userdiff.c                   |  10 +--
 userdiff.h                   |  12 ++--
 wt-status.c                  |   2 +-
 68 files changed, 444 insertions(+), 367 deletions(-)

Range-diff against v5:
 1:  e01fde88fe =  1:  ba50d96081 global: improve const correctness when assigning string constants
 2:  92cb0b28c6 =  2:  4769bdb893 global: convert intentionally-leaking config strings to consts
 3:  379145478c =  3:  90613bd582 refs/reftable: stop micro-optimizing refname allocations on copy
 4:  d0a2a2f6c5 =  4:  b126783ba3 reftable: cast away constness when assigning constants to records
 5:  ead27d3d97 =  5:  d10566e0a9 refspec: remove global tag refspec structure
 6:  7cb5df9182 =  6:  e9b8f9eaef builtin/remote: cast away constness in `get_head_names()`
 7:  6e631a9ea4 =  7:  8ea984128d diff: cast string constant in `fill_textconv()`
 8:  ac164651a3 =  8:  777b93a89a line-log: stop assigning string constant to file parent buffer
 9:  b717af02f0 =  9:  527533540e line-log: always allocate the output prefix
10:  b46dd3210d = 10:  4d0b8dcbaf entry: refactor how we remove items for delayed checkouts
11:  030dbd0288 = 11:  0d36b6e5d7 ident: add casts for fallback name and GECOS
12:  5cd014c22c ! 12:  bfd632fea7 object-file: mark cached object buffers as const
    @@ object-file.c: int pretend_object_file(void *buf, unsigned long len, enum object
      {
      	struct cached_object *co;
     +	char *co_buf;
    -+
    -+	co_buf = xmalloc(len);
    -+	memcpy(co_buf, buf, len);
      
      	hash_object_file(the_hash_algo, buf, len, type, oid);
      	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
    --	    find_cached_object(oid))
    -+	    find_cached_object(oid)) {
    -+		free(co_buf);
    - 		return 0;
    -+	}
    - 	ALLOC_GROW(cached_objects, cached_object_nr + 1, cached_object_alloc);
    +@@ object-file.c: int pretend_object_file(void *buf, unsigned long len, enum object_type type,
      	co = &cached_objects[cached_object_nr++];
      	co->size = len;
      	co->type = type;
     -	co->buf = xmalloc(len);
     -	memcpy(co->buf, buf, len);
    ++	co_buf = xmalloc(len);
    ++	memcpy(co_buf, buf, len);
     +	co->buf = co_buf;
      	oidcpy(&co->oid, oid);
      	return 0;
13:  69d904ddce = 13:  4770470a84 object-file: make `buf` parameter of `index_mem()` a constant
14:  ed8f07aa59 = 14:  02e79d030b pretty: add casts for decoration option pointers
15:  5953ae1dac = 15:  57402de20a compat/win32: fix const-correctness with string constants
16:  c80f6eff8c = 16:  71c83468f1 http: do not assign string constant to non-const field
17:  3afd012a88 = 17:  f3e02df0ca parse-options: cast long name for OPTION_ALIAS
18:  527755b648 = 18:  a46708b898 send-pack: always allocate receive status
19:  4598592d2f = 19:  99c88897be remote-curl: avoid assigning string constant to non-const variable
20:  38fcea2845 = 20:  03556233d6 revision: always store allocated strings in output encoding
21:  f990bbeb85 = 21:  2bb96449e2 mailmap: always store allocated strings in mailmap blob
22:  fff2379832 = 22:  8059e013a1 imap-send: drop global `imap_server_conf` variable
23:  9ab84e459a = 23:  04c9cfd34d imap-send: fix leaking memory in `imap_server_conf`
24:  81c69da2e8 = 24:  b2b4a01a2e builtin/rebase: do not assign default backend to non-constant field
25:  6819bf6116 = 25:  09980d211b builtin/rebase: always store allocated string in `options.strategy`
26:  a1d2149429 = 26:  d5dc1453f2 builtin/merge: always store allocated strings in `pull_twohead`
27:  c714b67199 = 27:  2c051d1fe6 config.mak.dev: enable `-Wwrite-strings` warning
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 205+ messages in thread

* [PATCH v6 01/27] global: improve const correctness when assigning string constants
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
@ 2024-06-07  6:37   ` Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
                     ` (26 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 27645 bytes --]

We're about to enable `-Wwrite-strings`, which changes the type of
string constants to `const char[]`. Fix various sites where we assign
such constants to non-const variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/bisect.c             |  3 ++-
 builtin/blame.c              |  2 +-
 builtin/bugreport.c          |  2 +-
 builtin/check-ignore.c       |  4 +--
 builtin/clone.c              |  6 ++---
 builtin/commit.c             |  6 ++---
 builtin/diagnose.c           |  2 +-
 builtin/log.c                |  2 +-
 builtin/mailsplit.c          |  4 +--
 builtin/pull.c               | 52 ++++++++++++++++++------------------
 builtin/receive-pack.c       |  4 +--
 builtin/revert.c             |  2 +-
 compat/regex/regcomp.c       |  2 +-
 diff.c                       |  4 +--
 diffcore-rename.c            |  6 ++---
 fmt-merge-msg.c              |  2 +-
 fsck.c                       |  2 +-
 fsck.h                       |  2 +-
 gpg-interface.c              |  2 +-
 http-backend.c               |  2 +-
 imap-send.c                  |  6 ++---
 pretty.c                     |  2 +-
 refs.c                       |  2 +-
 refs.h                       |  2 +-
 reftable/basics.c            | 15 +++++------
 reftable/basics.h            |  4 +--
 reftable/basics_test.c       |  4 +--
 reftable/record.c            |  6 ++---
 reftable/stack.c             | 10 ++++---
 reftable/stack_test.c        |  8 +++---
 run-command.c                |  2 +-
 t/helper/test-hashmap.c      |  3 ++-
 t/helper/test-json-writer.c  | 10 +++----
 t/helper/test-regex.c        |  4 +--
 t/helper/test-rot13-filter.c |  5 ++--
 t/unit-tests/t-strbuf.c      | 10 ++++---
 trailer.c                    |  2 +-
 wt-status.c                  |  2 +-
 38 files changed, 106 insertions(+), 102 deletions(-)

diff --git a/builtin/bisect.c b/builtin/bisect.c
index a58432b9d9..dabce9b542 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -262,7 +262,8 @@ static int bisect_reset(const char *commit)
 	return bisect_clean_state();
 }
 
-static void log_commit(FILE *fp, char *fmt, const char *state,
+static void log_commit(FILE *fp,
+		       const char *fmt, const char *state,
 		       struct commit *commit)
 {
 	struct pretty_print_context pp = {0};
diff --git a/builtin/blame.c b/builtin/blame.c
index 838cd476be..98c7629b6a 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -134,7 +134,7 @@ static void get_ac_line(const char *inbuf, const char *what,
 {
 	struct ident_split ident;
 	size_t len, maillen, namelen;
-	char *tmp, *endp;
+	const char *tmp, *endp;
 	const char *namebuf, *mailbuf;
 
 	tmp = strstr(inbuf, what);
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 25f860a0d9..b3cc77af53 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -107,7 +107,7 @@ int cmd_bugreport(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode diagnose = DIAGNOSE_NONE;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	const char *user_relative_path = NULL;
 	char *prefixed_filename;
 	size_t output_path_len;
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 6c43430ec4..2bda6a1d46 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -35,8 +35,8 @@ static const struct option check_ignore_options[] = {
 
 static void output_pattern(const char *path, struct path_pattern *pattern)
 {
-	char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
-	char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
+	const char *bang  = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE)  ? "!" : "";
+	const char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
 	if (!nul_term_line) {
 		if (!verbose) {
 			write_name_quoted(path, stdout, '\n');
diff --git a/builtin/clone.c b/builtin/clone.c
index 23993b905b..92ab7d7165 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -71,7 +71,7 @@ static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
 static const char *ref_format;
-static char *option_upload_pack = "git-upload-pack";
+static const char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
 static int option_sparse_checkout;
@@ -177,8 +177,8 @@ static struct option builtin_clone_options[] = {
 
 static const char *get_repo_path_1(struct strbuf *path, int *is_bundle)
 {
-	static char *suffix[] = { "/.git", "", ".git/.git", ".git" };
-	static char *bundle_suffix[] = { ".bundle", "" };
+	static const char *suffix[] = { "/.git", "", ".git/.git", ".git" };
+	static const char *bundle_suffix[] = { ".bundle", "" };
 	size_t baselen = path->len;
 	struct stat st;
 	int i;
diff --git a/builtin/commit.c b/builtin/commit.c
index f53e7e86ff..75c741173e 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -113,7 +113,7 @@ static char *template_file;
  * the commit message and/or authorship.
  */
 static const char *author_message, *author_message_buffer;
-static char *edit_message, *use_message;
+static const char *edit_message, *use_message;
 static char *fixup_message, *fixup_commit, *squash_message;
 static const char *fixup_prefix;
 static int all, also, interactive, patch_interactive, only, amend, signoff;
@@ -121,8 +121,8 @@ static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
 static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit, *pathspec_from_file;
+static const char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
+static const char *sign_commit, *pathspec_from_file;
 static struct strvec trailer_args = STRVEC_INIT;
 
 /*
diff --git a/builtin/diagnose.c b/builtin/diagnose.c
index 4f22eb2b55..4857a4395b 100644
--- a/builtin/diagnose.c
+++ b/builtin/diagnose.c
@@ -18,7 +18,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix)
 	struct tm tm;
 	enum diagnose_mode mode = DIAGNOSE_STATS;
 	char *option_output = NULL;
-	char *option_suffix = "%Y-%m-%d-%H%M";
+	const char *option_suffix = "%Y-%m-%d-%H%M";
 	char *prefixed_filename;
 
 	const struct option diagnose_options[] = {
diff --git a/builtin/log.c b/builtin/log.c
index 78a247d8a9..b8846a9458 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1283,7 +1283,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 	o2->flags = flags2;
 }
 
-static void gen_message_id(struct rev_info *info, char *base)
+static void gen_message_id(struct rev_info *info, const char *base)
 {
 	struct strbuf buf = STRBUF_INIT;
 	strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c
index 3af9ddb8ae..fe6dbc5d05 100644
--- a/builtin/mailsplit.c
+++ b/builtin/mailsplit.c
@@ -113,8 +113,8 @@ static int populate_maildir_list(struct string_list *list, const char *path)
 	DIR *dir;
 	struct dirent *dent;
 	char *name = NULL;
-	char *subs[] = { "cur", "new", NULL };
-	char **sub;
+	const char *subs[] = { "cur", "new", NULL };
+	const char **sub;
 	int ret = -1;
 
 	for (sub = subs; *sub; ++sub) {
diff --git a/builtin/pull.c b/builtin/pull.c
index d622202bce..2d0429f14f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -71,48 +71,48 @@ static const char * const pull_usage[] = {
 
 /* Shared options */
 static int opt_verbosity;
-static char *opt_progress;
+static const char *opt_progress;
 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
 
 /* Options passed to git-merge or git-rebase */
 static enum rebase_type opt_rebase = -1;
-static char *opt_diffstat;
-static char *opt_log;
-static char *opt_signoff;
-static char *opt_squash;
-static char *opt_commit;
-static char *opt_edit;
-static char *cleanup_arg;
-static char *opt_ff;
-static char *opt_verify_signatures;
-static char *opt_verify;
+static const char *opt_diffstat;
+static const char *opt_log;
+static const char *opt_signoff;
+static const char *opt_squash;
+static const char *opt_commit;
+static const char *opt_edit;
+static const char *cleanup_arg;
+static const char *opt_ff;
+static const char *opt_verify_signatures;
+static const char *opt_verify;
 static int opt_autostash = -1;
 static int config_autostash;
 static int check_trust_level = 1;
 static struct strvec opt_strategies = STRVEC_INIT;
 static struct strvec opt_strategy_opts = STRVEC_INIT;
-static char *opt_gpg_sign;
+static const char *opt_gpg_sign;
 static int opt_allow_unrelated_histories;
 
 /* Options passed to git-fetch */
-static char *opt_all;
-static char *opt_append;
-static char *opt_upload_pack;
+static const char *opt_all;
+static const char *opt_append;
+static const char *opt_upload_pack;
 static int opt_force;
-static char *opt_tags;
-static char *opt_prune;
-static char *max_children;
+static const char *opt_tags;
+static const char *opt_prune;
+static const char *max_children;
 static int opt_dry_run;
-static char *opt_keep;
-static char *opt_depth;
-static char *opt_unshallow;
-static char *opt_update_shallow;
-static char *opt_refmap;
-static char *opt_ipv4;
-static char *opt_ipv6;
+static const char *opt_keep;
+static const char *opt_depth;
+static const char *opt_unshallow;
+static const char *opt_update_shallow;
+static const char *opt_refmap;
+static const char *opt_ipv4;
+static const char *opt_ipv6;
 static int opt_show_forced_updates = -1;
-static char *set_upstream;
+static const char *set_upstream;
 static struct strvec opt_fetch = STRVEC_INIT;
 
 static struct option pull_options[] = {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 01c1f04ece..c8d12ee0a7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1249,7 +1249,7 @@ static int run_proc_receive_hook(struct command *commands,
 	return code;
 }
 
-static char *refuse_unconfigured_deny_msg =
+static const char *refuse_unconfigured_deny_msg =
 	N_("By default, updating the current branch in a non-bare repository\n"
 	   "is denied, because it will make the index and work tree inconsistent\n"
 	   "with what you pushed, and will require 'git reset --hard' to match\n"
@@ -1269,7 +1269,7 @@ static void refuse_unconfigured_deny(void)
 	rp_error("%s", _(refuse_unconfigured_deny_msg));
 }
 
-static char *refuse_unconfigured_deny_delete_current_msg =
+static const char *refuse_unconfigured_deny_delete_current_msg =
 	N_("By default, deleting the current branch is denied, because the next\n"
 	   "'git clone' won't result in any file checked out, causing confusion.\n"
 	   "\n"
diff --git a/builtin/revert.c b/builtin/revert.c
index 53935d2c68..7bf2b4e11d 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -179,7 +179,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix,
 
 	/* Check for incompatible command line arguments */
 	if (cmd) {
-		char *this_operation;
+		const char *this_operation;
 		if (cmd == 'q')
 			this_operation = "--quit";
 		else if (cmd == 'c')
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index 2bc0f1187a..6c5d455e92 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -848,7 +848,7 @@ init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
   unsigned int table_size;
 #ifndef _LIBC
-  char *codeset_name;
+  const char *codeset_name;
 #endif
 
   memset (dfa, '\0', sizeof (re_dfa_t));
diff --git a/diff.c b/diff.c
index e70301df76..ffd867ef6c 100644
--- a/diff.c
+++ b/diff.c
@@ -3764,7 +3764,7 @@ static void builtin_diff(const char *name_a,
 	return;
 }
 
-static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+static const char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
 {
 	if (!is_renamed) {
 		if (p->status == DIFF_STATUS_ADDED) {
@@ -4076,7 +4076,7 @@ static int reuse_worktree_file(struct index_state *istate,
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
 	struct strbuf buf = STRBUF_INIT;
-	char *dirty = "";
+	const char *dirty = "";
 
 	/* Are we looking at the work tree? */
 	if (s->dirty_submodule)
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 5a6e2bcac7..0e1adb87df 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -406,7 +406,7 @@ static const char *get_highest_rename_path(struct strintmap *counts)
 	return highest_destination_dir;
 }
 
-static char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
+static const char *UNKNOWN_DIR = "/";  /* placeholder -- short, illegal directory */
 
 static int dir_rename_already_determinable(struct strintmap *counts)
 {
@@ -429,8 +429,8 @@ static int dir_rename_already_determinable(struct strintmap *counts)
 }
 
 static void increment_count(struct dir_rename_info *info,
-			    char *old_dir,
-			    char *new_dir)
+			    const char *old_dir,
+			    const char *new_dir)
 {
 	struct strintmap *counts;
 	struct strmap_entry *e;
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 7d144b803a..5af63ab5ab 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -447,7 +447,7 @@ static void fmt_merge_msg_title(struct strbuf *out,
 				const char *current_branch)
 {
 	int i = 0;
-	char *sep = "";
+	const char *sep = "";
 
 	strbuf_addstr(out, "Merge ");
 	for (i = 0; i < srcs.nr; i++) {
diff --git a/fsck.c b/fsck.c
index 7dff41413e..61cd48aa25 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1231,7 +1231,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 }
 
 int fsck_buffer(const struct object_id *oid, enum object_type type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options)
 {
 	if (type == OBJ_BLOB)
diff --git a/fsck.h b/fsck.h
index 17fa2dda5d..4f0c4e6479 100644
--- a/fsck.h
+++ b/fsck.h
@@ -202,7 +202,7 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
  * struct.
  */
 int fsck_buffer(const struct object_id *oid, enum object_type,
-		void *data, unsigned long size,
+		const void *data, unsigned long size,
 		struct fsck_options *options);
 
 /*
diff --git a/gpg-interface.c b/gpg-interface.c
index 5193223714..71a9382a61 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -727,7 +727,7 @@ static int git_gpg_config(const char *var, const char *value,
 			  void *cb UNUSED)
 {
 	struct gpg_format *fmt = NULL;
-	char *fmtname = NULL;
+	const char *fmtname = NULL;
 	char *trust;
 	int ret;
 
diff --git a/http-backend.c b/http-backend.c
index 5b65287ac9..5b4dca65ed 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -753,7 +753,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c)
 
 int cmd_main(int argc UNUSED, const char **argv UNUSED)
 {
-	char *method = getenv("REQUEST_METHOD");
+	const char *method = getenv("REQUEST_METHOD");
 	const char *proto_header;
 	char *dir;
 	struct service_cmd *cmd = NULL;
diff --git a/imap-send.c b/imap-send.c
index a5d1510180..8b723b34a5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1215,9 +1215,9 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
 static void wrap_in_html(struct strbuf *msg)
 {
 	struct strbuf buf = STRBUF_INIT;
-	static char *content_type = "Content-Type: text/html;\n";
-	static char *pre_open = "<pre>\n";
-	static char *pre_close = "</pre>\n";
+	static const char *content_type = "Content-Type: text/html;\n";
+	static const char *pre_open = "<pre>\n";
+	static const char *pre_close = "</pre>\n";
 	const char *body = strstr(msg->buf, "\n\n");
 
 	if (!body)
diff --git a/pretty.c b/pretty.c
index 22a81506b7..ec05db5655 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1325,7 +1325,7 @@ int format_set_trailers_options(struct process_trailer_options *opts,
 static size_t parse_describe_args(const char *start, struct strvec *args)
 {
 	struct {
-		char *name;
+		const char *name;
 		enum {
 			DESCRIBE_ARG_BOOL,
 			DESCRIBE_ARG_INTEGER,
diff --git a/refs.c b/refs.c
index 8260c27cde..292e8d947e 100644
--- a/refs.c
+++ b/refs.c
@@ -159,7 +159,7 @@ void update_ref_namespace(enum ref_namespace namespace, char *ref)
 {
 	struct ref_namespace_info *info = &ref_namespace[namespace];
 	if (info->ref_updated)
-		free(info->ref);
+		free((char *)info->ref);
 	info->ref = ref;
 	info->ref_updated = 1;
 }
diff --git a/refs.h b/refs.h
index 34568ee1fb..923f751d18 100644
--- a/refs.h
+++ b/refs.h
@@ -975,7 +975,7 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt);
  */
 
 struct ref_namespace_info {
-	char *ref;
+	const char *ref;
 	enum decoration_type decoration;
 
 	/*
diff --git a/reftable/basics.c b/reftable/basics.c
index fea711db7e..0058619ca6 100644
--- a/reftable/basics.c
+++ b/reftable/basics.c
@@ -67,9 +67,9 @@ void free_names(char **a)
 	reftable_free(a);
 }
 
-size_t names_length(char **names)
+size_t names_length(const char **names)
 {
-	char **p = names;
+	const char **p = names;
 	while (*p)
 		p++;
 	return p - names;
@@ -102,15 +102,12 @@ void parse_names(char *buf, int size, char ***namesp)
 	*namesp = names;
 }
 
-int names_equal(char **a, char **b)
+int names_equal(const char **a, const char **b)
 {
-	int i = 0;
-	for (; a[i] && b[i]; i++) {
-		if (strcmp(a[i], b[i])) {
+	size_t i = 0;
+	for (; a[i] && b[i]; i++)
+		if (strcmp(a[i], b[i]))
 			return 0;
-		}
-	}
-
 	return a[i] == b[i];
 }
 
diff --git a/reftable/basics.h b/reftable/basics.h
index 523ecd5307..c8fec68d4e 100644
--- a/reftable/basics.h
+++ b/reftable/basics.h
@@ -42,10 +42,10 @@ void free_names(char **a);
 void parse_names(char *buf, int size, char ***namesp);
 
 /* compares two NULL-terminated arrays of strings. */
-int names_equal(char **a, char **b);
+int names_equal(const char **a, const char **b);
 
 /* returns the array size of a NULL-terminated array of strings. */
-size_t names_length(char **names);
+size_t names_length(const char **names);
 
 /* Allocation routines; they invoke the functions set through
  * reftable_set_alloc() */
diff --git a/reftable/basics_test.c b/reftable/basics_test.c
index 997c4d9e01..13bc761817 100644
--- a/reftable/basics_test.c
+++ b/reftable/basics_test.c
@@ -58,8 +58,8 @@ static void test_binsearch(void)
 
 static void test_names_length(void)
 {
-	char *a[] = { "a", "b", NULL };
-	EXPECT(names_length(a) == 2);
+	const char *names[] = { "a", "b", NULL };
+	EXPECT(names_length(names) == 2);
 }
 
 static void test_parse_names_normal(void)
diff --git a/reftable/record.c b/reftable/record.c
index 5506f3e913..a2cba5ef74 100644
--- a/reftable/record.c
+++ b/reftable/record.c
@@ -116,7 +116,7 @@ static int decode_string(struct strbuf *dest, struct string_view in)
 	return start_len - in.len;
 }
 
-static int encode_string(char *str, struct string_view s)
+static int encode_string(const char *str, struct string_view s)
 {
 	struct string_view start = s;
 	int l = strlen(str);
@@ -969,9 +969,9 @@ static int reftable_log_record_decode(void *rec, struct strbuf key,
 	return REFTABLE_FORMAT_ERROR;
 }
 
-static int null_streq(char *a, char *b)
+static int null_streq(const char *a, const char *b)
 {
-	char *empty = "";
+	const char *empty = "";
 	if (!a)
 		a = empty;
 
diff --git a/reftable/stack.c b/reftable/stack.c
index a59ebe038d..09549c51c9 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -204,7 +204,8 @@ static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
 	return cur;
 }
 
-static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
+static int reftable_stack_reload_once(struct reftable_stack *st,
+				      const char **names,
 				      int reuse_open)
 {
 	size_t cur_len = !st->merged ? 0 : st->merged->stack_len;
@@ -222,7 +223,7 @@ static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
 
 	while (*names) {
 		struct reftable_reader *rd = NULL;
-		char *name = *names++;
+		const char *name = *names++;
 
 		/* this is linear; we assume compaction keeps the number of
 		   tables under control so this is not quadratic. */
@@ -354,7 +355,7 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 				goto out;
 		}
 
-		err = reftable_stack_reload_once(st, names, reuse_open);
+		err = reftable_stack_reload_once(st, (const char **) names, reuse_open);
 		if (!err)
 			break;
 		if (err != REFTABLE_NOT_EXIST_ERROR)
@@ -368,7 +369,8 @@ static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 		err = read_lines(st->list_file, &names_after);
 		if (err < 0)
 			goto out;
-		if (names_equal(names_after, names)) {
+		if (names_equal((const char **) names_after,
+				(const char **) names)) {
 			err = REFTABLE_NOT_EXIST_ERROR;
 			goto out;
 		}
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 7889f818d1..07d89b45da 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -83,7 +83,7 @@ static void test_read_file(void)
 	char out[1024] = "line1\n\nline2\nline3";
 	int n, err;
 	char **names = NULL;
-	char *want[] = { "line1", "line2", "line3" };
+	const char *want[] = { "line1", "line2", "line3" };
 	int i = 0;
 
 	EXPECT(fd > 0);
@@ -116,9 +116,9 @@ static void test_parse_names(void)
 
 static void test_names_equal(void)
 {
-	char *a[] = { "a", "b", "c", NULL };
-	char *b[] = { "a", "b", "d", NULL };
-	char *c[] = { "a", "b", NULL };
+	const char *a[] = { "a", "b", "c", NULL };
+	const char *b[] = { "a", "b", "d", NULL };
+	const char *c[] = { "a", "b", NULL };
 
 	EXPECT(names_equal(a, a));
 	EXPECT(!names_equal(a, b));
diff --git a/run-command.c b/run-command.c
index 1b821042b4..7600531fb6 100644
--- a/run-command.c
+++ b/run-command.c
@@ -663,7 +663,7 @@ int start_command(struct child_process *cmd)
 	int need_in, need_out, need_err;
 	int fdin[2], fdout[2], fderr[2];
 	int failed_errno;
-	char *str;
+	const char *str;
 
 	/*
 	 * In case of errors we must keep the promise to close FDs
diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c
index 0eb0b3d49c..2912899558 100644
--- a/t/helper/test-hashmap.c
+++ b/t/helper/test-hashmap.c
@@ -36,7 +36,8 @@ static int test_entry_cmp(const void *cmp_data,
 }
 
 static struct test_entry *alloc_test_entry(unsigned int hash,
-					   char *key, char *value)
+					   const char *key,
+					   const char *value)
 {
 	size_t klen = strlen(key);
 	size_t vlen = strlen(value);
diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c
index afe393f597..ed52eb76bf 100644
--- a/t/helper/test-json-writer.c
+++ b/t/helper/test-json-writer.c
@@ -174,7 +174,7 @@ static void make_arr4(int pretty)
 	jw_end(&arr4);
 }
 
-static char *expect_nest1 =
+static const char *expect_nest1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
 static struct json_writer nest1 = JSON_WRITER_INIT;
@@ -195,10 +195,10 @@ static void make_nest1(int pretty)
 	jw_release(&arr1);
 }
 
-static char *expect_inline1 =
+static const char *expect_inline1 =
 	"{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 
-static char *pretty_inline1 =
+static const char *pretty_inline1 =
 	("{\n"
 	 "  \"obj1\": {\n"
 	 "    \"a\": \"abc\",\n"
@@ -236,10 +236,10 @@ static void make_inline1(int pretty)
 	jw_end(&inline1);
 }
 
-static char *expect_inline2 =
+static const char *expect_inline2 =
 	"[[1,2],[3,4],{\"a\":\"abc\"}]";
 
-static char *pretty_inline2 =
+static const char *pretty_inline2 =
 	("[\n"
 	 "  [\n"
 	 "    1,\n"
diff --git a/t/helper/test-regex.c b/t/helper/test-regex.c
index 80042eafc2..366bd70976 100644
--- a/t/helper/test-regex.c
+++ b/t/helper/test-regex.c
@@ -20,8 +20,8 @@ static struct reg_flag reg_flags[] = {
 
 static int test_regex_bug(void)
 {
-	char *pat = "[^={} \t]+";
-	char *str = "={}\nfred";
+	const char *pat = "[^={} \t]+";
+	const char *str = "={}\nfred";
 	regex_t r;
 	regmatch_t m[1];
 
diff --git a/t/helper/test-rot13-filter.c b/t/helper/test-rot13-filter.c
index f8d564c622..7e1d9e0ee4 100644
--- a/t/helper/test-rot13-filter.c
+++ b/t/helper/test-rot13-filter.c
@@ -136,7 +136,7 @@ static void free_delay_entries(void)
 	strmap_clear(&delay, 0);
 }
 
-static void add_delay_entry(char *pathname, int count, int requested)
+static void add_delay_entry(const char *pathname, int count, int requested)
 {
 	struct delay_entry *entry = xcalloc(1, sizeof(*entry));
 	entry->count = count;
@@ -189,7 +189,8 @@ static void reply_list_available_blobs_cmd(void)
 static void command_loop(void)
 {
 	for (;;) {
-		char *buf, *output;
+		char *buf;
+		const char *output;
 		char *pathname;
 		struct delay_entry *entry;
 		struct strbuf input = STRBUF_INIT;
diff --git a/t/unit-tests/t-strbuf.c b/t/unit-tests/t-strbuf.c
index de434a4441..6027dafef7 100644
--- a/t/unit-tests/t-strbuf.c
+++ b/t/unit-tests/t-strbuf.c
@@ -2,7 +2,8 @@
 #include "strbuf.h"
 
 /* wrapper that supplies tests with an empty, initialized strbuf */
-static void setup(void (*f)(struct strbuf*, void*), void *data)
+static void setup(void (*f)(struct strbuf*, const void*),
+		  const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -13,7 +14,8 @@ static void setup(void (*f)(struct strbuf*, void*), void *data)
 }
 
 /* wrapper that supplies tests with a populated, initialized strbuf */
-static void setup_populated(void (*f)(struct strbuf*, void*), char *init_str, void *data)
+static void setup_populated(void (*f)(struct strbuf*, const void*),
+			    const char *init_str, const void *data)
 {
 	struct strbuf buf = STRBUF_INIT;
 
@@ -64,7 +66,7 @@ static void t_dynamic_init(void)
 	strbuf_release(&buf);
 }
 
-static void t_addch(struct strbuf *buf, void *data)
+static void t_addch(struct strbuf *buf, const void *data)
 {
 	const char *p_ch = data;
 	const char ch = *p_ch;
@@ -83,7 +85,7 @@ static void t_addch(struct strbuf *buf, void *data)
 	check_char(buf->buf[buf->len], ==, '\0');
 }
 
-static void t_addstr(struct strbuf *buf, void *data)
+static void t_addstr(struct strbuf *buf, const void *data)
 {
 	const char *text = data;
 	size_t len = strlen(text);
diff --git a/trailer.c b/trailer.c
index 2bcb9ba8f7..72e5136c73 100644
--- a/trailer.c
+++ b/trailer.c
@@ -63,7 +63,7 @@ struct arg_item {
 
 static LIST_HEAD(conf_head);
 
-static char *separators = ":";
+static const char *separators = ":";
 
 static int configured;
 
diff --git a/wt-status.c b/wt-status.c
index ff4be071ca..7912545e4e 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2408,7 +2408,7 @@ static void wt_porcelain_v2_print_unmerged_entry(
 		int mode;
 		struct object_id oid;
 	} stages[3];
-	char *key;
+	const char *key;
 	char submodule_token[5];
 	char unmerged_prefix = 'u';
 	char eol_char = s->null_termination ? '\0' : '\n';
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 02/27] global: convert intentionally-leaking config strings to consts
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
@ 2024-06-07  6:37   ` Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
                     ` (25 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 5084 bytes --]

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 the struct memers are never
    written to or free'd.

  - `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 written to or 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 written to or free'd.

All of these are intentionally kept alive and are never written to.
Furthermore, all of these are being assigned both string constants in
some places, and allocated strings in other places. This will cause
warnings once we enable `-Wwrite-strings`, so 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(-)

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;
 };
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 03/27] refs/reftable: stop micro-optimizing refname allocations on copy
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
@ 2024-06-07  6:37   ` Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
                     ` (24 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 4918 bytes --]

When copying refs, we execute `write_copy_table()` to write the new
table. As the names are given to us via `arg->newname` and
`arg->oldname`, respectively, we optimize away some allocations by
assigning those fields to the reftable records we are about to write
directly, without duplicating them. This requires us to cast the input
to `char *` pointers as they are in fact constant strings. Later on, we
then unset the refname for all of the records before calling
`reftable_log_record_release()` on them.

We also do this when assigning the "HEAD" constant, but here we do not
cast because its type is `char[]` by default. It's about to be turned
into `const char *` though once we enable `-Wwrite-strings` and will
thus cause another warning.

It's quite dubious whether this micro-optimization really helps. We're
about to write to disk anyway, which is going to be way slower than a
small handful of allocations. Let's drop the optimization altogther and
instead copy arguments to simplify the code and avoid the future warning
with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 refs/reftable-backend.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 1af86bbdec..e77faa2b9d 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1340,10 +1340,10 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	 * old reference.
 	 */
 	refs[0] = old_ref;
-	refs[0].refname = (char *)arg->newname;
+	refs[0].refname = xstrdup(arg->newname);
 	refs[0].update_index = creation_ts;
 	if (arg->delete_old) {
-		refs[1].refname = (char *)arg->oldname;
+		refs[1].refname = xstrdup(arg->oldname);
 		refs[1].value_type = REFTABLE_REF_DELETION;
 		refs[1].update_index = deletion_ts;
 	}
@@ -1366,7 +1366,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 		fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs[logs_nr].update_index = deletion_ts;
 		logs[logs_nr].value.update.message =
 			xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1387,7 +1387,13 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (append_head_reflog) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			logs[logs_nr] = logs[logs_nr - 1];
-			logs[logs_nr].refname = "HEAD";
+			logs[logs_nr].refname = xstrdup("HEAD");
+			logs[logs_nr].value.update.name =
+				xstrdup(logs[logs_nr].value.update.name);
+			logs[logs_nr].value.update.email =
+				xstrdup(logs[logs_nr].value.update.email);
+			logs[logs_nr].value.update.message =
+				xstrdup(logs[logs_nr].value.update.message);
 			logs_nr++;
 		}
 	}
@@ -1398,7 +1404,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 	memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
 	fill_reftable_log_record(&logs[logs_nr], &committer_ident);
-	logs[logs_nr].refname = (char *)arg->newname;
+	logs[logs_nr].refname = xstrdup(arg->newname);
 	logs[logs_nr].update_index = creation_ts;
 	logs[logs_nr].value.update.message =
 		xstrndup(arg->logmsg, arg->refs->write_options.block_size / 2);
@@ -1430,7 +1436,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		 */
 		ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 		logs[logs_nr] = old_log;
-		logs[logs_nr].refname = (char *)arg->newname;
+		logs[logs_nr].refname = xstrdup(arg->newname);
 		logs_nr++;
 
 		/*
@@ -1439,7 +1445,7 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 		if (arg->delete_old) {
 			ALLOC_GROW(logs, logs_nr + 1, logs_alloc);
 			memset(&logs[logs_nr], 0, sizeof(logs[logs_nr]));
-			logs[logs_nr].refname = (char *)arg->oldname;
+			logs[logs_nr].refname = xstrdup(arg->oldname);
 			logs[logs_nr].value_type = REFTABLE_LOG_DELETION;
 			logs[logs_nr].update_index = old_log.update_index;
 			logs_nr++;
@@ -1462,13 +1468,11 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
 	reftable_iterator_destroy(&it);
 	string_list_clear(&skip, 0);
 	strbuf_release(&errbuf);
-	for (i = 0; i < logs_nr; i++) {
-		if (!strcmp(logs[i].refname, "HEAD"))
-			continue;
-		logs[i].refname = NULL;
+	for (i = 0; i < logs_nr; i++)
 		reftable_log_record_release(&logs[i]);
-	}
 	free(logs);
+	for (i = 0; i < ARRAY_SIZE(refs); i++)
+		reftable_ref_record_release(&refs[i]);
 	reftable_ref_record_release(&old_ref);
 	reftable_log_record_release(&old_log);
 	return ret;
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 04/27] reftable: cast away constness when assigning constants to records
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (2 preceding siblings ...)
  2024-06-07  6:37   ` [PATCH v6 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
@ 2024-06-07  6:37   ` Patrick Steinhardt
  2024-06-07  6:37   ` [PATCH v6 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
                     ` (23 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 14245 bytes --]

The reftable records are used in multiple ways throughout the reftable
library. In many of those cases they merely act as input to a function
without getting modified by it at all. Most importantly, this happens
when writing records and when querying for records.

We rely on this in our tests and thus assign string constants to those
fields, which is about to generate warnings as those fields are of type
`char *`. While we could go through the process and instead allocate
those strings in all of our tests, this feels quite unnecessary.

Instead, add casts to `char *` for all of those strings. As this is part
of our tests, this also nicely serves as a demonstration that nothing
writes or frees those string constants, which would otherwise lead to
segfaults.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 reftable/block_test.c     |  2 +-
 reftable/merged_test.c    | 44 +++++++++++++++++------------------
 reftable/readwrite_test.c | 32 +++++++++++++-------------
 reftable/stack_test.c     | 48 +++++++++++++++++++--------------------
 4 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/reftable/block_test.c b/reftable/block_test.c
index 26a9cfbc83..90aecd5a7c 100644
--- a/reftable/block_test.c
+++ b/reftable/block_test.c
@@ -42,7 +42,7 @@ static void test_block_read_write(void)
 	block_writer_init(&bw, BLOCK_TYPE_REF, block.data, block_size,
 			  header_off, hash_size(GIT_SHA1_FORMAT_ID));
 
-	rec.u.ref.refname = "";
+	rec.u.ref.refname = (char *) "";
 	rec.u.ref.value_type = REFTABLE_REF_DELETION;
 	n = block_writer_add(&bw, &rec);
 	EXPECT(n == REFTABLE_API_ERROR);
diff --git a/reftable/merged_test.c b/reftable/merged_test.c
index 530fc82d1c..6d1159d12d 100644
--- a/reftable/merged_test.c
+++ b/reftable/merged_test.c
@@ -124,13 +124,13 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 static void test_merged_between(void)
 {
 	struct reftable_ref_record r1[] = { {
-		.refname = "b",
+		.refname = (char *) "b",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = { 1, 2, 3, 0 },
 	} };
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
@@ -165,38 +165,38 @@ static void test_merged(void)
 {
 	struct reftable_ref_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		},
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
 		}
 	};
 	struct reftable_ref_record r2[] = { {
-		.refname = "a",
+		.refname = (char *) "a",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_DELETION,
 	} };
 	struct reftable_ref_record r3[] = {
 		{
-			.refname = "c",
+			.refname = (char *) "c",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 2 },
 		},
 		{
-			.refname = "d",
+			.refname = (char *) "d",
 			.update_index = 3,
 			.value_type = REFTABLE_REF_VAL1,
 			.value.val1 = { 1 },
@@ -291,46 +291,46 @@ static void test_merged_logs(void)
 {
 	struct reftable_log_record r1[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 2 },
 				/* deletion */
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message2",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message2",
 			}
 		},
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message1",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message1",
 			}
 		},
 	};
 	struct reftable_log_record r2[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 3,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value.update = {
 				.new_hash = { 3 },
-				.name = "jane doe",
-				.email = "jane@invalid",
-				.message = "message3",
+				.name = (char *) "jane doe",
+				.email = (char *) "jane@invalid",
+				.message = (char *) "message3",
 			}
 		},
 	};
 	struct reftable_log_record r3[] = {
 		{
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 2,
 			.value_type = REFTABLE_LOG_DELETION,
 		},
@@ -406,7 +406,7 @@ static void test_default_write_opts(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 
 	struct reftable_ref_record rec = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.update_index = 1,
 	};
 	int err;
diff --git a/reftable/readwrite_test.c b/reftable/readwrite_test.c
index a6dbd214c5..c55019232b 100644
--- a/reftable/readwrite_test.c
+++ b/reftable/readwrite_test.c
@@ -86,7 +86,7 @@ static void write_table(char ***names, struct strbuf *buf, int N,
 		log.update_index = update_index;
 		log.value_type = REFTABLE_LOG_UPDATE;
 		set_test_hash(log.value.update.new_hash, i);
-		log.value.update.message = "message";
+		log.value.update.message = (char *) "message";
 
 		n = reftable_writer_add_log(w, &log);
 		EXPECT(n == 0);
@@ -118,15 +118,15 @@ static void test_log_buffer_size(void)
 	int err;
 	int i;
 	struct reftable_log_record
-		log = { .refname = "refs/heads/master",
+		log = { .refname = (char *) "refs/heads/master",
 			.update_index = 0xa,
 			.value_type = REFTABLE_LOG_UPDATE,
 			.value = { .update = {
-					   .name = "Han-Wen Nienhuys",
-					   .email = "hanwen@google.com",
+					   .name = (char *) "Han-Wen Nienhuys",
+					   .email = (char *) "hanwen@google.com",
 					   .tz_offset = 100,
 					   .time = 0x5e430672,
-					   .message = "commit: 9\n",
+					   .message = (char *) "commit: 9\n",
 				   } } };
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
@@ -156,15 +156,15 @@ static void test_log_overflow(void)
 	};
 	int err;
 	struct reftable_log_record log = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 0xa,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.old_hash = { 1 },
 				.new_hash = { 2 },
-				.name = "Han-Wen Nienhuys",
-				.email = "hanwen@google.com",
+				.name = (char *) "Han-Wen Nienhuys",
+				.email = (char *) "hanwen@google.com",
 				.tz_offset = 100,
 				.time = 0x5e430672,
 				.message = msg,
@@ -293,14 +293,14 @@ static void test_log_zlib_corruption(void)
 	char message[100] = { 0 };
 	int err, i, n;
 	struct reftable_log_record log = {
-		.refname = "refname",
+		.refname = (char *) "refname",
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
 			.update = {
 				.new_hash = { 1 },
 				.old_hash = { 2 },
-				.name = "My Name",
-				.email = "myname@invalid",
+				.name = (char *) "My Name",
+				.email = (char *) "myname@invalid",
 				.message = message,
 			},
 		},
@@ -728,7 +728,7 @@ static void test_write_empty_key(void)
 	struct reftable_writer *w =
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record ref = {
-		.refname = "",
+		.refname = (char *) "",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_DELETION,
 	};
@@ -752,18 +752,18 @@ static void test_write_key_order(void)
 		reftable_new_writer(&strbuf_add_void, &noop_flush, &buf, &opts);
 	struct reftable_ref_record refs[2] = {
 		{
-			.refname = "b",
+			.refname = (char *) "b",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}, {
-			.refname = "a",
+			.refname = (char *) "a",
 			.update_index = 1,
 			.value_type = REFTABLE_REF_SYMREF,
 			.value = {
-				.symref = "target",
+				.symref = (char *) "target",
 			},
 		}
 	};
diff --git a/reftable/stack_test.c b/reftable/stack_test.c
index 07d89b45da..4abf92636d 100644
--- a/reftable/stack_test.c
+++ b/reftable/stack_test.c
@@ -156,10 +156,10 @@ static void test_reftable_stack_add_one(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 	struct stat stat_result = { 0 };
@@ -216,16 +216,16 @@ static void test_reftable_stack_uptodate(void)
 
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "branch2",
+		.refname = (char *) "branch2",
 		.update_index = 2,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 
@@ -264,10 +264,10 @@ static void test_reftable_stack_transaction_api(void)
 	struct reftable_addition *add = NULL;
 
 	struct reftable_ref_record ref = {
-		.refname = "HEAD",
+		.refname = (char *) "HEAD",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record dest = { NULL };
 
@@ -313,7 +313,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		char name[100];
 
@@ -356,7 +356,7 @@ static void test_reftable_stack_transaction_api_performs_auto_compaction(void)
 static void test_reftable_stack_auto_compaction_fails_gracefully(void)
 {
 	struct reftable_ref_record ref = {
-		.refname = "refs/heads/master",
+		.refname = (char *) "refs/heads/master",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_VAL1,
 		.value.val1 = {0x01},
@@ -409,16 +409,16 @@ static void test_reftable_stack_update_index_check(void)
 	struct reftable_stack *st = NULL;
 	int err;
 	struct reftable_ref_record ref1 = {
-		.refname = "name1",
+		.refname = (char *) "name1",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 	struct reftable_ref_record ref2 = {
-		.refname = "name2",
+		.refname = (char *) "name2",
 		.update_index = 1,
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "master",
+		.value.symref = (char *) "master",
 	};
 
 	err = reftable_new_stack(&st, dir, cfg);
@@ -561,7 +561,7 @@ static void test_reftable_stack_log_normalize(void)
 	struct reftable_stack *st = NULL;
 	char *dir = get_tmp_dir(__LINE__);
 	struct reftable_log_record input = {
-		.refname = "branch",
+		.refname = (char *) "branch",
 		.update_index = 1,
 		.value_type = REFTABLE_LOG_UPDATE,
 		.value = {
@@ -582,11 +582,11 @@ static void test_reftable_stack_log_normalize(void)
 	err = reftable_new_stack(&st, dir, cfg);
 	EXPECT_ERR(err);
 
-	input.value.update.message = "one\ntwo";
+	input.value.update.message = (char *) "one\ntwo";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT(err == REFTABLE_API_ERROR);
 
-	input.value.update.message = "one";
+	input.value.update.message = (char *) "one";
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
 
@@ -594,7 +594,7 @@ static void test_reftable_stack_log_normalize(void)
 	EXPECT_ERR(err);
 	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
 
-	input.value.update.message = "two\n";
+	input.value.update.message = (char *) "two\n";
 	arg.update_index = 2;
 	err = reftable_stack_add(st, &write_test_log, &arg);
 	EXPECT_ERR(err);
@@ -697,9 +697,9 @@ static void test_reftable_stack_hash_id(void)
 	int err;
 
 	struct reftable_ref_record ref = {
-		.refname = "master",
+		.refname = (char *) "master",
 		.value_type = REFTABLE_REF_SYMREF,
-		.value.symref = "target",
+		.value.symref = (char *) "target",
 		.update_index = 1,
 	};
 	struct reftable_write_options cfg32 = { .hash_id = GIT_SHA256_FORMAT_ID };
@@ -879,7 +879,7 @@ static void test_reftable_stack_auto_compaction(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -913,7 +913,7 @@ static void test_reftable_stack_add_performs_auto_compaction(void)
 		struct reftable_ref_record ref = {
 			.update_index = reftable_stack_next_update_index(st),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 
 		/*
@@ -964,7 +964,7 @@ static void test_reftable_stack_compaction_concurrent(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
@@ -1014,7 +1014,7 @@ static void test_reftable_stack_compaction_concurrent_clean(void)
 			.refname = name,
 			.update_index = reftable_stack_next_update_index(st1),
 			.value_type = REFTABLE_REF_SYMREF,
-			.value.symref = "master",
+			.value.symref = (char *) "master",
 		};
 		snprintf(name, sizeof(name), "branch%04d", i);
 
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 05/27] refspec: remove global tag refspec structure
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (3 preceding siblings ...)
  2024-06-07  6:37   ` [PATCH v6 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
@ 2024-06-07  6:37   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
                     ` (22 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:37 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 3608 bytes --]

We have a global tag refspec structure that is used by both git-clone(1)
and git-fetch(1). Initialization of the structure will break once we
enable `-Wwrite-strings`, even though the breakage is harmless. While we
could just add casts, the structure isn't really required in the first
place as we can simply initialize the structures at the respective
callsites.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/clone.c |  8 ++++++--
 builtin/fetch.c | 11 ++++++++---
 refspec.c       | 13 -------------
 refspec.h       |  1 -
 4 files changed, 14 insertions(+), 19 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 92ab7d7165..bde1d284a2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -523,6 +523,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD"));
 	struct ref *local_refs = head;
 	struct ref **tail = head ? &head->next : &local_refs;
+	struct refspec_item tag_refspec;
+
+	refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
 
 	if (option_single_branch) {
 		struct ref *remote_head = NULL;
@@ -545,7 +548,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 					      &tail, 0);
 
 			/* if --branch=tag, pull the requested tag explicitly */
-			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+			get_fetch_map(remote_head, &tag_refspec, &tail, 0);
 		}
 		free_refs(remote_head);
 	} else {
@@ -555,8 +558,9 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 	}
 
 	if (!option_mirror && !option_single_branch && !option_no_tags)
-		get_fetch_map(refs, tag_refspec, &tail, 0);
+		get_fetch_map(refs, &tag_refspec, &tail, 0);
 
+	refspec_item_clear(&tag_refspec);
 	return local_refs;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 75255dc600..06b60867f5 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -582,11 +582,16 @@ static struct ref *get_ref_map(struct remote *remote,
 		}
 	}
 
-	if (tags == TAGS_SET)
+	if (tags == TAGS_SET) {
+		struct refspec_item tag_refspec;
+
 		/* also fetch all tags */
-		get_fetch_map(remote_refs, tag_refspec, &tail, 0);
-	else if (tags == TAGS_DEFAULT && *autotags)
+		refspec_item_init(&tag_refspec, TAG_REFSPEC, 0);
+		get_fetch_map(remote_refs, &tag_refspec, &tail, 0);
+		refspec_item_clear(&tag_refspec);
+	} else if (tags == TAGS_DEFAULT && *autotags) {
 		find_non_local_tags(remote_refs, NULL, &ref_map, &tail);
+	}
 
 	/* Now append any refs to be updated opportunistically: */
 	*tail = orefs;
diff --git a/refspec.c b/refspec.c
index d60932f4de..1df5de6c2f 100644
--- a/refspec.c
+++ b/refspec.c
@@ -7,19 +7,6 @@
 #include "refspec.h"
 #include "strbuf.h"
 
-static struct refspec_item s_tag_refspec = {
-	.force = 0,
-	.pattern = 1,
-	.matching = 0,
-	.exact_sha1 = 0,
-	.negative = 0,
-	.src = "refs/tags/*",
-	.dst = "refs/tags/*",
-};
-
-/* See TAG_REFSPEC for the string version */
-const struct refspec_item *tag_refspec = &s_tag_refspec;
-
 /*
  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
  * Returns 1 if successful and 0 if the refspec is invalid.
diff --git a/refspec.h b/refspec.h
index 8c0c446993..754be45cee 100644
--- a/refspec.h
+++ b/refspec.h
@@ -2,7 +2,6 @@
 #define REFSPEC_H
 
 #define TAG_REFSPEC "refs/tags/*:refs/tags/*"
-extern const struct refspec_item *tag_refspec;
 
 /**
  * A struct refspec_item holds the parsed interpretation of a refspec.  If it
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 06/27] builtin/remote: cast away constness in `get_head_names()`
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (4 preceding siblings ...)
  2024-06-07  6:37   ` [PATCH v6 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
                     ` (21 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2509 bytes --]

In `get_head_names()`, we assign the "refs/heads/*" string constant to
`struct refspec_item::{src,dst}`, which are both non-constant pointers.
Ideally, we'd refactor the code such that both of these fields were
constant. But `struct refspec_item` is used for two different usecases
with conflicting requirements:

  - To query for a source or destination based on the given refspec. The
    caller either sets `src` or `dst` as the branch that we want to
    search for, and the respective other field gets populated. The
    fields should be constant when being used as a query parameter,
    which is owned by the caller, and non-constant when being used as an
    out parameter, which is owned by the refspec item. This is is
    contradictory in itself already.

  - To store refspec items with their respective source and destination
    branches, in which case both fields should be owned by the struct.

Ideally, we'd split up this interface to clearly separate between
querying and storing, which would enable us to clarify lifetimes of the
strings. This would be a much bigger undertaking though.

Instead, accept the status quo for now and cast away the constness of
the source and destination patterns. We know that those are not being
written to or freed, so while this is ugly it certainly is fine for now.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/remote.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/builtin/remote.c b/builtin/remote.c
index d52b1c0e10..b44f580b8c 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -493,12 +493,13 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 {
 	struct ref *ref, *matches;
 	struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;
-	struct refspec_item refspec;
+	struct refspec_item refspec = {
+		.force = 0,
+		.pattern = 1,
+		.src = (char *) "refs/heads/*",
+		.dst = (char *) "refs/heads/*",
+	};
 
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.force = 0;
-	refspec.pattern = 1;
-	refspec.src = refspec.dst = "refs/heads/*";
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
 	matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
 				    fetch_map, 1);
@@ -507,7 +508,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 
 	free_refs(fetch_map);
 	free_refs(matches);
-
 	return 0;
 }
 
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 07/27] diff: cast string constant in `fill_textconv()`
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (5 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
                     ` (20 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1597 bytes --]

The `fill_textconv()` function is responsible for converting an input
file with a textconv driver, which is then passed to the caller. Weirdly
though, the function also handles the case where there is no textconv
driver at all. In that case, it will return either the contents of the
populated filespec, or an empty string if the filespec is invalid.

These two cases have differing memory ownership semantics. When there is
a textconv driver, then the result is an allocated string. Otherwise,
the result is either a string constant or owned by the filespec struct.
All callers are in fact aware of this weirdness and only end up freeing
the output buffer when they had a textconv driver.

Ideally, we'd split up this interface to only perform the conversion via
the textconv driver, and BUG in case the caller didn't provide one. This
would make memory ownership semantics much more straight forward. For
now though, let's simply cast the empty string constant to `char *` to
avoid a warning with `-Wwrite-strings`. This is equivalent to the same
cast that we already have in `fill_mmfile()`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 diff.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/diff.c b/diff.c
index ffd867ef6c..cecda216cf 100644
--- a/diff.c
+++ b/diff.c
@@ -7235,7 +7235,7 @@ size_t fill_textconv(struct repository *r,
 
 	if (!driver) {
 		if (!DIFF_FILE_VALID(df)) {
-			*outbuf = "";
+			*outbuf = (char *) "";
 			return 0;
 		}
 		if (diff_populate_filespec(r, df, NULL))
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 08/27] line-log: stop assigning string constant to file parent buffer
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (6 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 09/27] line-log: always allocate the output prefix Patrick Steinhardt
                     ` (19 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1198 bytes --]

Stop assigning a string constant to the file parent buffer and instead
assign an allocated string. While the code is fine in practice, it will
break once we compile with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/line-log.c b/line-log.c
index 8ff6ccb772..bd3e663c24 100644
--- a/line-log.c
+++ b/line-log.c
@@ -1032,6 +1032,7 @@ static int process_diff_filepair(struct rev_info *rev,
 	struct range_set tmp;
 	struct diff_ranges diff;
 	mmfile_t file_parent, file_target;
+	char *parent_data_to_free = NULL;
 
 	assert(pair->two->path);
 	while (rg) {
@@ -1056,7 +1057,7 @@ static int process_diff_filepair(struct rev_info *rev,
 		file_parent.ptr = pair->one->data;
 		file_parent.size = pair->one->size;
 	} else {
-		file_parent.ptr = "";
+		file_parent.ptr = parent_data_to_free = xstrdup("");
 		file_parent.size = 0;
 	}
 
@@ -1075,6 +1076,7 @@ static int process_diff_filepair(struct rev_info *rev,
 
 	diff_ranges_release(&diff);
 
+	free(parent_data_to_free);
 	return ((*diff_out)->parent.nr > 0);
 }
 
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 09/27] line-log: always allocate the output prefix
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (7 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
                     ` (18 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2111 bytes --]

The returned string by `output_prefix()` is sometimes a string constant
and sometimes an allocated string. This has been fine until now because
we always leak the allocated strings, and thus we never tried to free
the string constant.

Fix the code to always return an allocated string and free the returned
value at all callsites.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 line-log.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/line-log.c b/line-log.c
index bd3e663c24..67c80b39a0 100644
--- a/line-log.c
+++ b/line-log.c
@@ -899,14 +899,12 @@ static void print_line(const char *prefix, char first,
 
 static char *output_prefix(struct diff_options *opt)
 {
-	char *prefix = "";
-
 	if (opt->output_prefix) {
 		struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
-		prefix = sb->buf;
+		return sb->buf;
+	} else {
+		return xstrdup("");
 	}
-
-	return prefix;
 }
 
 static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
@@ -927,7 +925,7 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
 
 	if (!pair || !diff)
-		return;
+		goto out;
 
 	if (pair->one->oid_valid)
 		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
@@ -1002,8 +1000,10 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
 				   c_context, c_reset, opt->file);
 	}
 
+out:
 	free(p_ends);
 	free(t_ends);
+	free(prefix);
 }
 
 /*
@@ -1012,7 +1012,11 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
  */
 static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
 {
-	fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt));
+	char *prefix = output_prefix(&rev->diffopt);
+
+	fprintf(rev->diffopt.file, "%s\n", prefix);
+	free(prefix);
+
 	while (range) {
 		dump_diff_hacky_one(rev, range);
 		range = range->next;
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 10/27] entry: refactor how we remove items for delayed checkouts
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (8 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 09/27] line-log: always allocate the output prefix Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
                     ` (17 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2328 bytes --]

When finalizing a delayed checkout, we sort out several strings from the
passed-in string list by first assigning the empty string to those
filters and then calling `string_list_remove_empty_items()`. Assigning
the empty string will cause compiler warnings though as the string is
a `char *` once we enable `-Wwrite-strings`.

Refactor the code to use a `NULL` pointer with `filter_string_list()`
instead to avoid this warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 entry.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/entry.c b/entry.c
index b8c257f6f9..f291d8eee6 100644
--- a/entry.c
+++ b/entry.c
@@ -167,6 +167,11 @@ static int remove_available_paths(struct string_list_item *item, void *cb_data)
 	return !available;
 }
 
+static int string_is_not_null(struct string_list_item *item, void *data UNUSED)
+{
+	return !!item->string;
+}
+
 int finish_delayed_checkout(struct checkout *state, int show_progress)
 {
 	int errs = 0;
@@ -189,7 +194,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 			if (!async_query_available_blobs(filter->string, &available_paths)) {
 				/* Filter reported an error */
 				errs = 1;
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 			if (available_paths.nr <= 0) {
@@ -199,7 +204,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 				 * filter from the list (see
 				 * "string_list_remove_empty_items" call below).
 				 */
-				filter->string = "";
+				filter->string = NULL;
 				continue;
 			}
 
@@ -225,7 +230,7 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					 * Do not ask the filter for available blobs,
 					 * again, as the filter is likely buggy.
 					 */
-					filter->string = "";
+					filter->string = NULL;
 					continue;
 				}
 				ce = index_file_exists(state->istate, path->string,
@@ -239,7 +244,8 @@ int finish_delayed_checkout(struct checkout *state, int show_progress)
 					errs = 1;
 			}
 		}
-		string_list_remove_empty_items(&dco->filters, 0);
+
+		filter_string_list(&dco->filters, 0, string_is_not_null, NULL);
 	}
 	stop_progress(&progress);
 	string_list_clear(&dco->filters, 0);
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 11/27] ident: add casts for fallback name and GECOS
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (9 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
                     ` (16 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1095 bytes --]

In `xgetpwuid_self()`, we return a fallback identity when it was not
possible to look up the current identity. This fallback identity needs
to be internal and must never be written to by the calles as specified
by getpwuid(3P). As both the `pw_name` and `pw_gecos` fields are marked
as non-constant though, it will cause a warning to assign constant
strings to them once compiling with `-Wwrite-strings`.

Add explicit casts to avoid the warning.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 ident.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ident.c b/ident.c
index cc7afdbf81..caf41fb2a9 100644
--- a/ident.c
+++ b/ident.c
@@ -46,9 +46,9 @@ static struct passwd *xgetpwuid_self(int *is_bogus)
 	pw = getpwuid(getuid());
 	if (!pw) {
 		static struct passwd fallback;
-		fallback.pw_name = "unknown";
+		fallback.pw_name = (char *) "unknown";
 #ifndef NO_GECOS_IN_PWENT
-		fallback.pw_gecos = "Unknown";
+		fallback.pw_gecos = (char *) "Unknown";
 #endif
 		pw = &fallback;
 		if (is_bogus)
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 12/27] object-file: mark cached object buffers as const
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (10 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
                     ` (15 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1812 bytes --]

The buffers of cached objects are never modified, but are still stored
as a non-constant pointer. This will cause a compiler warning once we
enable the `-Wwrite-strings` compiler warning as we assign an empty
constant string when initializing the static `empty_tree` cached object.

Convert the field to be constant. This requires us to shuffle around
the code a bit because we memcpy(3P) into the allocated buffer in
`pretend_object_file()`. This is easily fixed though by allocating the
buffer into a temporary variable first.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/object-file.c b/object-file.c
index 610b1f465c..08c00dcc02 100644
--- a/object-file.c
+++ b/object-file.c
@@ -277,7 +277,7 @@ int hash_algo_by_length(int len)
 static struct cached_object {
 	struct object_id oid;
 	enum object_type type;
-	void *buf;
+	const void *buf;
 	unsigned long size;
 } *cached_objects;
 static int cached_object_nr, cached_object_alloc;
@@ -1778,6 +1778,7 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 			struct object_id *oid)
 {
 	struct cached_object *co;
+	char *co_buf;
 
 	hash_object_file(the_hash_algo, buf, len, type, oid);
 	if (repo_has_object_file_with_flags(the_repository, oid, OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) ||
@@ -1787,8 +1788,9 @@ int pretend_object_file(void *buf, unsigned long len, enum object_type type,
 	co = &cached_objects[cached_object_nr++];
 	co->size = len;
 	co->type = type;
-	co->buf = xmalloc(len);
-	memcpy(co->buf, buf, len);
+	co_buf = xmalloc(len);
+	memcpy(co_buf, buf, len);
+	co->buf = co_buf;
 	oidcpy(&co->oid, oid);
 	return 0;
 }
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 13/27] object-file: make `buf` parameter of `index_mem()` a constant
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (11 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
                     ` (14 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1863 bytes --]

The `buf` parameter of `index_mem()` is a non-constant string. This will
break once we enable `-Wwrite-strings` because we also pass constants
from at least one callsite.

Adapt the parameter to be a constant. As we cannot free the buffer
without casting now, this also requires us to move the lifetime of the
nested buffer around.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 object-file.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/object-file.c b/object-file.c
index 08c00dcc02..0b58751f94 100644
--- a/object-file.c
+++ b/object-file.c
@@ -2484,12 +2484,13 @@ static int hash_format_check_report(struct fsck_options *opts UNUSED,
 }
 
 static int index_mem(struct index_state *istate,
-		     struct object_id *oid, void *buf, size_t size,
+		     struct object_id *oid,
+		     const void *buf, size_t size,
 		     enum object_type type,
 		     const char *path, unsigned flags)
 {
+	struct strbuf nbuf = STRBUF_INIT;
 	int ret = 0;
-	int re_allocated = 0;
 	int write_object = flags & HASH_WRITE_OBJECT;
 
 	if (!type)
@@ -2499,11 +2500,10 @@ static int index_mem(struct index_state *istate,
 	 * Convert blobs to git internal format
 	 */
 	if ((type == OBJ_BLOB) && path) {
-		struct strbuf nbuf = STRBUF_INIT;
 		if (convert_to_git(istate, path, buf, size, &nbuf,
 				   get_conv_flags(flags))) {
-			buf = strbuf_detach(&nbuf, &size);
-			re_allocated = 1;
+			buf = nbuf.buf;
+			size = nbuf.len;
 		}
 	}
 	if (flags & HASH_FORMAT_CHECK) {
@@ -2520,8 +2520,8 @@ static int index_mem(struct index_state *istate,
 		ret = write_object_file(buf, size, type, oid);
 	else
 		hash_object_file(the_hash_algo, buf, size, type, oid);
-	if (re_allocated)
-		free(buf);
+
+	strbuf_release(&nbuf);
 	return ret;
 }
 
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 14/27] pretty: add casts for decoration option pointers
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (12 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
                     ` (13 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 947 bytes --]

The `struct decoration_options` have a prefix and suffix field which are
both non-constant, but we assign a constant pointer to them. This is
safe to do because we pass them to `format_decorations()`, which never
modifies these pointers, and then immediately discard the structure. Add
explicit casts to avoid compilation warnings with `-Wwrite-strings`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 pretty.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pretty.c b/pretty.c
index ec05db5655..1df9d635fb 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1584,8 +1584,8 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 	case 'D':
 		{
 			const struct decoration_options opts = {
-				.prefix = "",
-				.suffix = ""
+				.prefix = (char *) "",
+				.suffix = (char *) "",
 			};
 
 			format_decorations(sb, commit, c->auto_color, &opts);
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 15/27] compat/win32: fix const-correctness with string constants
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (13 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
                     ` (12 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 3832 bytes --]

Adjust various places in our Win32 compatibility layer where we are not
assigning string constants to `const char *` variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 compat/basename.c | 16 ++++++++++++++--
 compat/mingw.c    | 28 ++++++++++++++++------------
 compat/winansi.c  |  2 +-
 3 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/compat/basename.c b/compat/basename.c
index 96bd9533b4..c33579ef61 100644
--- a/compat/basename.c
+++ b/compat/basename.c
@@ -10,7 +10,13 @@ char *gitbasename (char *path)
 		skip_dos_drive_prefix(&path);
 
 	if (!path || !*path)
-		return ".";
+		/*
+		 * basename(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) ".";
 
 	for (base = path; *path; path++) {
 		if (!is_dir_sep(*path))
@@ -34,7 +40,13 @@ char *gitdirname(char *path)
 	int dos_drive_prefix;
 
 	if (!p)
-		return ".";
+		/*
+		 * dirname(3P) is mis-specified because it returns a
+		 * non-constant pointer even though it is specified to return a
+		 * pointer to internal memory at times. The cast is a result of
+		 * that.
+		 */
+		return (char *) ".";
 
 	if ((dos_drive_prefix = skip_dos_drive_prefix(&p)) && !*p)
 		goto dot;
diff --git a/compat/mingw.c b/compat/mingw.c
index 6b06ea540f..d378cd04cb 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2279,7 +2279,11 @@ struct passwd *getpwuid(int uid)
 	p->pw_name = user_name;
 	p->pw_gecos = get_extended_user_info(NameDisplay);
 	if (!p->pw_gecos)
-		p->pw_gecos = "unknown";
+		/*
+		 * Data returned by getpwuid(3P) is treated as internal and
+		 * must never be written to or freed.
+		 */
+		p->pw_gecos = (char *) "unknown";
 	p->pw_dir = NULL;
 
 	initialized = 1;
@@ -2800,16 +2804,16 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 			strbuf_addf(report, "'%s' is on a file system that does "
 				    "not record ownership\n", path);
 		} else if (report) {
-			LPSTR str1, str2, str3, str4, to_free1 = NULL,
-			    to_free3 = NULL, to_local_free2 = NULL,
-			    to_local_free4 = NULL;
+			PCSTR str1, str2, str3, str4;
+			LPSTR to_free1 = NULL, to_free3 = NULL,
+			    to_local_free2 = NULL, to_local_free4 = NULL;
 
-			if (user_sid_to_user_name(sid, &str1))
-				to_free1 = str1;
+			if (user_sid_to_user_name(sid, &to_free1))
+				str1 = to_free1;
 			else
 				str1 = "(inconvertible)";
-			if (ConvertSidToStringSidA(sid, &str2))
-				to_local_free2 = str2;
+			if (ConvertSidToStringSidA(sid, &to_local_free2))
+				str2 = to_local_free2;
 			else
 				str2 = "(inconvertible)";
 
@@ -2822,13 +2826,13 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
 				str4 = "(invalid)";
 			} else {
 				if (user_sid_to_user_name(current_user_sid,
-							  &str3))
-					to_free3 = str3;
+							  &to_free3))
+					str3 = to_free3;
 				else
 					str3 = "(inconvertible)";
 				if (ConvertSidToStringSidA(current_user_sid,
-							   &str4))
-					to_local_free4 = str4;
+							   &to_local_free4))
+					str4 = to_local_free4;
 				else
 					str4 = "(inconvertible)";
 			}
diff --git a/compat/winansi.c b/compat/winansi.c
index f83610f684..575813bde8 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -139,7 +139,7 @@ static void write_console(unsigned char *str, size_t len)
 	/* convert utf-8 to utf-16 */
 	int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
 	if (wlen < 0) {
-		wchar_t *err = L"[invalid]";
+		const wchar_t *err = L"[invalid]";
 		WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
 		return;
 	}
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 16/27] http: do not assign string constant to non-const field
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (14 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
                     ` (11 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1260 bytes --]

In `write_accept_language()`, we put all acceptable languages into an
array. While all entries in that array are allocated strings, the final
entry in that array is a string constant. This is fine because we
explicitly skip over the last entry when freeing the array, but will
cause warnings once we enable `-Wwrite-strings`.

Adapt the code to also allocate the final entry.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 http.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/http.c b/http.c
index 67cc47d28f..2dea2d03da 100644
--- a/http.c
+++ b/http.c
@@ -1974,7 +1974,7 @@ static void write_accept_language(struct strbuf *buf)
 
 		/* add '*' */
 		REALLOC_ARRAY(language_tags, num_langs + 1);
-		language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+		language_tags[num_langs++] = xstrdup("*");
 
 		/* compute decimal_places */
 		for (max_q = 1, decimal_places = 0;
@@ -2004,8 +2004,7 @@ static void write_accept_language(struct strbuf *buf)
 		}
 	}
 
-	/* free language tags -- last one is a static '*' */
-	for (i = 0; i < num_langs - 1; i++)
+	for (i = 0; i < num_langs; i++)
 		free(language_tags[i]);
 	free(language_tags);
 }
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 17/27] parse-options: cast long name for OPTION_ALIAS
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (15 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:38   ` [PATCH v6 18/27] send-pack: always allocate receive status Patrick Steinhardt
                     ` (10 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 792 bytes --]

We assign the long name for OPTION_ALIAS options to a non-constant value
field. We know that the variable will never be written to, but this will
cause warnings once we enable `-Wwrite-strings`.

Cast away the constness to be prepared for this change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 parse-options.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/parse-options.h b/parse-options.h
index bd62e20268..ae15342390 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -355,7 +355,7 @@ struct option {
 	.type = OPTION_ALIAS, \
 	.short_name = (s), \
 	.long_name = (l), \
-	.value = (source_long_name), \
+	.value = (char *)(source_long_name), \
 }
 
 #define OPT_SUBCOMMAND_F(l, v, fn, f) { \
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 18/27] send-pack: always allocate receive status
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (16 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
@ 2024-06-07  6:38   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
                     ` (9 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:38 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1586 bytes --]

In `receive_status()`, we record the reason why ref updates have been
rejected by the remote via the `remote_status`. But while we allocate
the assigned string when a reason was given, we assign a string constant
when no reason was given.

This has been working fine so far due to two reasons:

  - We don't ever free the refs in git-send-pack(1)'

  - Remotes always give a reason, at least as implemented by Git proper.

Adapt the code to always allocate the receive status string and free the
refs.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/send-pack.c | 2 ++
 send-pack.c         | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 3df9eaad09..17cae6bbbd 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -336,5 +336,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 		/* stable plumbing output; do not modify or localize */
 		fprintf(stderr, "Everything up-to-date\n");
 
+	free_refs(remote_refs);
+	free_refs(local_refs);
 	return ret;
 }
diff --git a/send-pack.c b/send-pack.c
index 37f59d4f66..88e96d000b 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -259,7 +259,7 @@ static int receive_status(struct packet_reader *reader, struct ref *refs)
 			if (p)
 				hint->remote_status = xstrdup(p);
 			else
-				hint->remote_status = "failed";
+				hint->remote_status = xstrdup("failed");
 		} else {
 			hint->status = REF_STATUS_OK;
 			hint->remote_status = xstrdup_or_null(p);
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 19/27] remote-curl: avoid assigning string constant to non-const variable
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (17 preceding siblings ...)
  2024-06-07  6:38   ` [PATCH v6 18/27] send-pack: always allocate receive status Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
                     ` (8 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 7752 bytes --]

When processing remote options, we split the option line into two by
searching for a space. If there is one, we replace the space with '\0',
otherwise we implicitly assume that the value is "true" and thus assign
a string constant.

As the return value of strchr(3P) weirdly enough is a `char *` even
though it gets a `const char *` as input, the assigned-to variable also
is a non-constant. This is fine though because the argument is in fact
an allocated string, and thus we are allowed to modify it. But this will
break once we enable `-Wwrite-strings`.

Refactor the code stop splitting the fields with '\0' altogether.
Instead, we can pass the length of the option name to `set_option()` and
then use strncmp(3P) instead of strcmp(3P).

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 remote-curl.c | 53 ++++++++++++++++++++++++++-------------------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index cae98384da..d0f767df8e 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -58,9 +58,9 @@ struct options {
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
 
-static int set_option(const char *name, const char *value)
+static int set_option(const char *name, size_t namelen, const char *value)
 {
-	if (!strcmp(name, "verbosity")) {
+	if (!strncmp(name, "verbosity", namelen)) {
 		char *end;
 		int v = strtol(value, &end, 10);
 		if (value == end || *end)
@@ -68,7 +68,7 @@ static int set_option(const char *name, const char *value)
 		options.verbosity = v;
 		return 0;
 	}
-	else if (!strcmp(name, "progress")) {
+	else if (!strncmp(name, "progress", namelen)) {
 		if (!strcmp(value, "true"))
 			options.progress = 1;
 		else if (!strcmp(value, "false"))
@@ -77,7 +77,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "depth")) {
+	else if (!strncmp(name, "depth", namelen)) {
 		char *end;
 		unsigned long v = strtoul(value, &end, 10);
 		if (value == end || *end)
@@ -85,15 +85,15 @@ static int set_option(const char *name, const char *value)
 		options.depth = v;
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-since")) {
+	else if (!strncmp(name, "deepen-since", namelen)) {
 		options.deepen_since = xstrdup(value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-not")) {
+	else if (!strncmp(name, "deepen-not", namelen)) {
 		string_list_append(&options.deepen_not, value);
 		return 0;
 	}
-	else if (!strcmp(name, "deepen-relative")) {
+	else if (!strncmp(name, "deepen-relative", namelen)) {
 		if (!strcmp(value, "true"))
 			options.deepen_relative = 1;
 		else if (!strcmp(value, "false"))
@@ -102,7 +102,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "followtags")) {
+	else if (!strncmp(name, "followtags", namelen)) {
 		if (!strcmp(value, "true"))
 			options.followtags = 1;
 		else if (!strcmp(value, "false"))
@@ -111,7 +111,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "dry-run")) {
+	else if (!strncmp(name, "dry-run", namelen)) {
 		if (!strcmp(value, "true"))
 			options.dry_run = 1;
 		else if (!strcmp(value, "false"))
@@ -120,7 +120,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "check-connectivity")) {
+	else if (!strncmp(name, "check-connectivity", namelen)) {
 		if (!strcmp(value, "true"))
 			options.check_self_contained_and_connected = 1;
 		else if (!strcmp(value, "false"))
@@ -129,7 +129,7 @@ static int set_option(const char *name, const char *value)
 			return -1;
 		return 0;
 	}
-	else if (!strcmp(name, "cas")) {
+	else if (!strncmp(name, "cas", namelen)) {
 		struct strbuf val = STRBUF_INIT;
 		strbuf_addstr(&val, "--force-with-lease=");
 		if (*value != '"')
@@ -139,7 +139,7 @@ static int set_option(const char *name, const char *value)
 		string_list_append(&cas_options, val.buf);
 		strbuf_release(&val);
 		return 0;
-	} else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+	} else if (!strncmp(name, TRANS_OPT_FORCE_IF_INCLUDES, namelen)) {
 		if (!strcmp(value, "true"))
 			options.force_if_includes = 1;
 		else if (!strcmp(value, "false"))
@@ -147,7 +147,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "cloning")) {
+	} else if (!strncmp(name, "cloning", namelen)) {
 		if (!strcmp(value, "true"))
 			options.cloning = 1;
 		else if (!strcmp(value, "false"))
@@ -155,7 +155,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "update-shallow")) {
+	} else if (!strncmp(name, "update-shallow", namelen)) {
 		if (!strcmp(value, "true"))
 			options.update_shallow = 1;
 		else if (!strcmp(value, "false"))
@@ -163,7 +163,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "pushcert")) {
+	} else if (!strncmp(name, "pushcert", namelen)) {
 		if (!strcmp(value, "true"))
 			options.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
 		else if (!strcmp(value, "false"))
@@ -173,7 +173,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "atomic")) {
+	} else if (!strncmp(name, "atomic", namelen)) {
 		if (!strcmp(value, "true"))
 			options.atomic = 1;
 		else if (!strcmp(value, "false"))
@@ -181,7 +181,7 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "push-option")) {
+	} else if (!strncmp(name, "push-option", namelen)) {
 		if (*value != '"')
 			string_list_append(&options.push_options, value);
 		else {
@@ -192,7 +192,7 @@ static int set_option(const char *name, const char *value)
 						 strbuf_detach(&unquoted, NULL));
 		}
 		return 0;
-	} else if (!strcmp(name, "family")) {
+	} else if (!strncmp(name, "family", namelen)) {
 		if (!strcmp(value, "ipv4"))
 			git_curl_ipresolve = CURL_IPRESOLVE_V4;
 		else if (!strcmp(value, "ipv6"))
@@ -202,16 +202,16 @@ static int set_option(const char *name, const char *value)
 		else
 			return -1;
 		return 0;
-	} else if (!strcmp(name, "from-promisor")) {
+	} else if (!strncmp(name, "from-promisor", namelen)) {
 		options.from_promisor = 1;
 		return 0;
-	} else if (!strcmp(name, "refetch")) {
+	} else if (!strncmp(name, "refetch", namelen)) {
 		options.refetch = 1;
 		return 0;
-	} else if (!strcmp(name, "filter")) {
+	} else if (!strncmp(name, "filter", namelen)) {
 		options.filter = xstrdup(value);
 		return 0;
-	} else if (!strcmp(name, "object-format")) {
+	} else if (!strncmp(name, "object-format", namelen)) {
 		options.object_format = 1;
 		if (strcmp(value, "true"))
 			die(_("unknown value for object-format: %s"), value);
@@ -1588,15 +1588,16 @@ int cmd_main(int argc, const char **argv)
 			parse_push(&buf);
 
 		} else if (skip_prefix(buf.buf, "option ", &arg)) {
-			char *value = strchr(arg, ' ');
+			const char *value = strchrnul(arg, ' ');
+			size_t arglen = value - arg;
 			int result;
 
-			if (value)
-				*value++ = '\0';
+			if (*value)
+				value++; /* skip over SP */
 			else
 				value = "true";
 
-			result = set_option(arg, value);
+			result = set_option(arg, arglen, value);
 			if (!result)
 				printf("ok\n");
 			else if (result < 0)
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 20/27] revision: always store allocated strings in output encoding
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (18 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
                     ` (7 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2531 bytes --]

The `git_log_output_encoding` variable can be set via the `--encoding=`
option. When doing so, we conditionally either assign it to the passed
value, or if the value is "none" we assign it the empty string.
Depending on which of the both code paths we pick though, the variable
may end up being assigned either an allocated string or a string
constant.

This is somewhat risky and may easily lead to bugs when a different code
path may want to reassign a new value to it, freeing the previous value.
We already to this when parsing the "i18n.logoutputencoding" config in
`git_default_i18n_config()`. But because the config is typically parsed
before we parse command line options this has been fine so far.

Regardless of that, safeguard the code such that the variable always
contains an allocated string. While at it, also free the old value in
case there was any to plug a potential memory leak.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 revision.c             | 3 ++-
 t/t3900-i18n-commit.sh | 1 +
 t/t3901-i18n-patch.sh  | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 7ddf0f151a..2ee6886078 100644
--- a/revision.c
+++ b/revision.c
@@ -2650,10 +2650,11 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if (!strcmp(arg, "--invert-grep")) {
 		revs->grep_filter.no_body_match = 1;
 	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		free(git_log_output_encoding);
 		if (strcmp(optarg, "none"))
 			git_log_output_encoding = xstrdup(optarg);
 		else
-			git_log_output_encoding = "";
+			git_log_output_encoding = xstrdup("");
 		return argcount;
 	} else if (!strcmp(arg, "--reverse")) {
 		revs->reverse ^= 1;
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index f27d09cfd9..db7b403bc1 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -5,6 +5,7 @@
 
 test_description='commit and log output encodings'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 compare_with () {
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 4b37f78829..5f0b9afc3f 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -8,6 +8,7 @@ test_description='i18n settings and format-patch | am pipe'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_encoding () {
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 21/27] mailmap: always store allocated strings in mailmap blob
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (19 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
                     ` (6 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1005 bytes --]

Same as with the preceding commit, the `git_mailmap_blob` may sometimes
contain an allocated string and sometimes it may contain a string
constant. This is risky and can easily lead to bugs in case the variable
is getting re-assigned, where the code may then try to free the previous
value to avoid memory leaks.

Safeguard the code by always storing allocated strings in the variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 mailmap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mailmap.c b/mailmap.c
index b2efe29b3d..3d1e092fef 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -216,7 +216,7 @@ int read_mailmap(struct string_list *map)
 	map->cmp = namemap_cmp;
 
 	if (!git_mailmap_blob && is_bare_repository())
-		git_mailmap_blob = "HEAD:.mailmap";
+		git_mailmap_blob = xstrdup("HEAD:.mailmap");
 
 	if (!startup_info->have_repository || !is_bare_repository())
 		err |= read_mailmap_file(map, ".mailmap",
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 22/27] imap-send: drop global `imap_server_conf` variable
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (20 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
                     ` (5 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 7200 bytes --]

In "imap-send.c", we have a global `sturct imap_server_conf` variable
that keeps track of the configuration of the IMAP server. This variable
is being populated mostly via the Git configuration.

Refactor the code to allocate the structure on the stack instead of
having it globally. This change allows us to track its lifetime more
closely.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 57 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 27 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 8b723b34a5..67a7a6c456 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -82,10 +82,6 @@ struct imap_server_conf {
 	char *auth_method;
 };
 
-static struct imap_server_conf server = {
-	.ssl_verify = 1,
-};
-
 struct imap_socket {
 	int fd[2];
 	SSL *ssl;
@@ -110,6 +106,7 @@ struct imap {
 };
 
 struct imap_store {
+	const struct imap_server_conf *cfg;
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	int uidvalidity;
@@ -194,8 +191,8 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
 
 #ifdef NO_OPENSSL
 static int ssl_socket_connect(struct imap_socket *sock UNUSED,
-			      int use_tls_only UNUSED,
-			      int verify UNUSED)
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only UNUSED)
 {
 	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
 	return -1;
@@ -250,7 +247,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
 		     cname, hostname);
 }
 
-static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock,
+			      const struct imap_server_conf *cfg,
+			      int use_tls_only)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 	const SSL_METHOD *meth;
@@ -279,7 +278,7 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	if (use_tls_only)
 		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
 
-	if (verify)
+	if (cfg->ssl_verify)
 		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
 
 	if (!SSL_CTX_set_default_verify_paths(ctx)) {
@@ -306,9 +305,9 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 	 * OpenSSL does not document this function, but the implementation
 	 * returns 1 on success, 0 on failure after calling SSLerr().
 	 */
-	ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
+	ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
 	if (ret != 1)
-		warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
+		warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
 #endif
 
 	ret = SSL_connect(sock->ssl);
@@ -317,12 +316,12 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
 		return -1;
 	}
 
-	if (verify) {
+	if (cfg->ssl_verify) {
 		/* make sure the hostname matches that of the certificate */
 		cert = SSL_get_peer_certificate(sock->ssl);
 		if (!cert)
 			return error("unable to get peer certificate.");
-		if (verify_hostname(cert, server.host) < 0)
+		if (verify_hostname(cert, cfg->host) < 0)
 			return -1;
 	}
 
@@ -895,7 +894,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
 	int ret;
 	char *response;
 
-	response = cram(prompt, server.user, server.pass);
+	response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
 
 	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
 	if (ret != strlen(response))
@@ -935,6 +934,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 
 	CALLOC_ARRAY(ctx, 1);
 
+	ctx->cfg = srvc;
 	ctx->imap = CALLOC_ARRAY(imap, 1);
 	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
 	imap->in_progress_append = &imap->in_progress;
@@ -1035,7 +1035,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		imap->buf.sock.fd[1] = dup(s);
 
 		if (srvc->use_ssl &&
-		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+		    ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
 			close(s);
 			goto bail;
 		}
@@ -1068,8 +1068,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
 		if (!srvc->use_ssl && CAP(STARTTLS)) {
 			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
 				goto bail;
-			if (ssl_socket_connect(&imap->buf.sock, 1,
-					       srvc->ssl_verify))
+			if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
 				goto bail;
 			/* capabilities may have changed, so get the new capabilities */
 			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
@@ -1299,23 +1298,24 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
 static int git_imap_config(const char *var, const char *val,
 			   const struct config_context *ctx, void *cb)
 {
+	struct imap_server_conf *cfg = cb;
 
 	if (!strcmp("imap.sslverify", var))
-		server.ssl_verify = git_config_bool(var, val);
+		cfg->ssl_verify = git_config_bool(var, val);
 	else if (!strcmp("imap.preformattedhtml", var))
-		server.use_html = git_config_bool(var, val);
+		cfg->use_html = git_config_bool(var, val);
 	else if (!strcmp("imap.folder", var))
-		return git_config_string(&server.folder, var, val);
+		return git_config_string(&cfg->folder, var, val);
 	else if (!strcmp("imap.user", var))
-		return git_config_string(&server.user, var, val);
+		return git_config_string(&cfg->user, var, val);
 	else if (!strcmp("imap.pass", var))
-		return git_config_string(&server.pass, var, val);
+		return git_config_string(&cfg->pass, var, val);
 	else if (!strcmp("imap.tunnel", var))
-		return git_config_string(&server.tunnel, var, val);
+		return git_config_string(&cfg->tunnel, var, val);
 	else if (!strcmp("imap.authmethod", var))
-		return git_config_string(&server.auth_method, var, val);
+		return git_config_string(&cfg->auth_method, var, val);
 	else if (!strcmp("imap.port", var))
-		server.port = git_config_int(var, val, ctx->kvi);
+		cfg->port = git_config_int(var, val, ctx->kvi);
 	else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
@@ -1324,11 +1324,11 @@ static int git_imap_config(const char *var, const char *val,
 				val += 5;
 			else if (starts_with(val, "imaps:")) {
 				val += 6;
-				server.use_ssl = 1;
+				cfg->use_ssl = 1;
 			}
 			if (starts_with(val, "//"))
 				val += 2;
-			server.host = xstrdup(val);
+			cfg->host = xstrdup(val);
 		}
 	} else
 		return git_default_config(var, val, ctx, cb);
@@ -1497,12 +1497,15 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
 
 int cmd_main(int argc, const char **argv)
 {
+	struct imap_server_conf server = {
+		.ssl_verify = 1,
+	};
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
 
 	setup_git_directory_gently(&nongit_ok);
-	git_config(git_imap_config, NULL);
+	git_config(git_imap_config, &server);
 
 	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
 
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 23/27] imap-send: fix leaking memory in `imap_server_conf`
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (21 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
                     ` (4 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 4324 bytes --]

We never free any of the config strings that we populate into the
`struct imap_server_conf`. Fix this by creating a common exit path where
we can free resources.

While at it, drop the unused member `imap_server_conf::name`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 imap-send.c | 65 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 23 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 67a7a6c456..da3e7ec17e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -69,7 +69,6 @@ static void imap_warn(const char *, ...);
 static char *next_arg(char **);
 
 struct imap_server_conf {
-	const char *name;
 	char *tunnel;
 	char *host;
 	int port;
@@ -1300,23 +1299,28 @@ static int git_imap_config(const char *var, const char *val,
 {
 	struct imap_server_conf *cfg = cb;
 
-	if (!strcmp("imap.sslverify", var))
+	if (!strcmp("imap.sslverify", var)) {
 		cfg->ssl_verify = git_config_bool(var, val);
-	else if (!strcmp("imap.preformattedhtml", var))
+	} else if (!strcmp("imap.preformattedhtml", var)) {
 		cfg->use_html = git_config_bool(var, val);
-	else if (!strcmp("imap.folder", var))
+	} else if (!strcmp("imap.folder", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->folder, var, val);
-	else if (!strcmp("imap.user", var))
+	} else if (!strcmp("imap.user", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->user, var, val);
-	else if (!strcmp("imap.pass", var))
+	} else if (!strcmp("imap.pass", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->pass, var, val);
-	else if (!strcmp("imap.tunnel", var))
+	} else if (!strcmp("imap.tunnel", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->tunnel, var, val);
-	else if (!strcmp("imap.authmethod", var))
+	} else if (!strcmp("imap.authmethod", var)) {
+		FREE_AND_NULL(cfg->folder);
 		return git_config_string(&cfg->auth_method, var, val);
-	else if (!strcmp("imap.port", var))
+	} else if (!strcmp("imap.port", var)) {
 		cfg->port = git_config_int(var, val, ctx->kvi);
-	else if (!strcmp("imap.host", var)) {
+	} else if (!strcmp("imap.host", var)) {
 		if (!val) {
 			return config_error_nonbool(var);
 		} else {
@@ -1330,8 +1334,9 @@ static int git_imap_config(const char *var, const char *val,
 				val += 2;
 			cfg->host = xstrdup(val);
 		}
-	} else
+	} else {
 		return git_default_config(var, val, ctx, cb);
+	}
 
 	return 0;
 }
@@ -1503,6 +1508,7 @@ int cmd_main(int argc, const char **argv)
 	struct strbuf all_msgs = STRBUF_INIT;
 	int total;
 	int nongit_ok;
+	int ret;
 
 	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, &server);
@@ -1529,42 +1535,55 @@ int cmd_main(int argc, const char **argv)
 
 	if (!server.folder) {
 		fprintf(stderr, "no imap store specified\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
 			fprintf(stderr, "no imap host specified\n");
-			return 1;
+			ret = 1;
+			goto out;
 		}
-		server.host = "tunnel";
+		server.host = xstrdup("tunnel");
 	}
 
 	/* read the messages */
 	if (strbuf_read(&all_msgs, 0, 0) < 0) {
 		error_errno(_("could not read from stdin"));
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	if (all_msgs.len == 0) {
 		fprintf(stderr, "nothing to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	total = count_messages(&all_msgs);
 	if (!total) {
 		fprintf(stderr, "no messages to send\n");
-		return 1;
+		ret = 1;
+		goto out;
 	}
 
 	/* write it to the imap server */
 
 	if (server.tunnel)
-		return append_msgs_to_imap(&server, &all_msgs, total);
-
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
 #ifdef USE_CURL_FOR_IMAP_SEND
-	if (use_curl)
-		return curl_append_msgs_to_imap(&server, &all_msgs, total);
+	else if (use_curl)
+		ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
 #endif
-
-	return append_msgs_to_imap(&server, &all_msgs, total);
+	else
+		ret = append_msgs_to_imap(&server, &all_msgs, total);
+
+out:
+	free(server.tunnel);
+	free(server.host);
+	free(server.folder);
+	free(server.user);
+	free(server.pass);
+	free(server.auth_method);
+	return ret;
 }
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 24/27] builtin/rebase: do not assign default backend to non-constant field
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (22 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
                     ` (3 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2428 bytes --]

The `struct rebase_options::default_backend` field is a non-constant
string, but is being assigned a constant via `REBASE_OPTIONS_INIT`.
Fix this by using `xstrdup()` to assign the variable and introduce a new
function `rebase_options_release()` that releases memory held by the
structure, including the newly-allocated variable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 14d4f0a5e6..adc990b55e 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -135,7 +135,7 @@ struct rebase_options {
 		.type = REBASE_UNSPECIFIED,	  	\
 		.empty = EMPTY_UNSPECIFIED,	  	\
 		.keep_empty = 1,			\
-		.default_backend = "merge",	  	\
+		.default_backend = xstrdup("merge"),  	\
 		.flags = REBASE_NO_QUIET, 		\
 		.git_am_opts = STRVEC_INIT,		\
 		.exec = STRING_LIST_INIT_NODUP,		\
@@ -151,6 +151,19 @@ struct rebase_options {
 		.strategy_opts = STRING_LIST_INIT_NODUP,\
 	}
 
+static void rebase_options_release(struct rebase_options *opts)
+{
+	free(opts->default_backend);
+	free(opts->reflog_action);
+	free(opts->head_name);
+	strvec_clear(&opts->git_am_opts);
+	free(opts->gpg_sign_opt);
+	string_list_clear(&opts->exec, 0);
+	free(opts->strategy);
+	string_list_clear(&opts->strategy_opts, 0);
+	strbuf_release(&opts->git_format_patch_opt);
+}
+
 static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 {
 	struct replay_opts replay = REPLAY_OPTS_INIT;
@@ -796,6 +809,7 @@ static int rebase_config(const char *var, const char *value,
 	}
 
 	if (!strcmp(var, "rebase.backend")) {
+		FREE_AND_NULL(opts->default_backend);
 		return git_config_string(&opts->default_backend, var, value);
 	}
 
@@ -1833,14 +1847,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 cleanup:
 	strbuf_release(&buf);
 	strbuf_release(&revisions);
-	free(options.reflog_action);
-	free(options.head_name);
-	strvec_clear(&options.git_am_opts);
-	free(options.gpg_sign_opt);
-	string_list_clear(&options.exec, 0);
-	free(options.strategy);
-	string_list_clear(&options.strategy_opts, 0);
-	strbuf_release(&options.git_format_patch_opt);
+	rebase_options_release(&options);
 	free(squash_onto_name);
 	free(keep_base_onto_name);
 	return !!ret;
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 25/27] builtin/rebase: always store allocated string in `options.strategy`
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (23 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
                     ` (2 subsequent siblings)
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2360 bytes --]

The `struct rebase_options::strategy` field is a `char *`, but we do end
up assigning string constants to it in two cases:

  - When being passed a `--strategy=` option via the command line.

  - When being passed a strategy option via `--strategy-option=`, but
    not a strategy.

This will cause warnings once we enable `-Wwrite-strings`.

Ideally, we'd just convert the field to be a `const char *`. But we also
assign to this field via the GIT_TEST_MERGE_ALGORITHM envvar, which we
have to strdup(3P) into it.

Instead, refactor the code to make sure that we only ever assign
allocated strings to this field.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/rebase.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index adc990b55e..4506bae768 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1061,6 +1061,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
 	const char *branch_name;
+	const char *strategy_opt = NULL;
 	int ret, flags, total_argc, in_progress = 0;
 	int keep_base = 0;
 	int ok_to_skip_pre_rebase = 0;
@@ -1175,7 +1176,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 			PARSE_OPT_OPTARG, parse_opt_rebase_merges),
 		OPT_BOOL(0, "fork-point", &options.fork_point,
 			 N_("use 'merge-base --fork-point' to refine upstream")),
-		OPT_STRING('s', "strategy", &options.strategy,
+		OPT_STRING('s', "strategy", &strategy_opt,
 			   N_("strategy"), N_("use the given merge strategy")),
 		OPT_STRING_LIST('X', "strategy-option", &options.strategy_opts,
 				N_("option"),
@@ -1484,13 +1485,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		}
 	}
 
-	if (options.strategy_opts.nr && !options.strategy)
-		options.strategy = "ort";
-
-	if (options.strategy) {
-		options.strategy = xstrdup(options.strategy);
+	if (strategy_opt)
+		options.strategy = xstrdup(strategy_opt);
+	else if (options.strategy_opts.nr && !options.strategy)
+		options.strategy = xstrdup("ort");
+	if (options.strategy)
 		imply_merge(&options, "--strategy");
-	}
 
 	if (options.root && !options.onto_name)
 		imply_merge(&options, "--root without --onto");
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 26/27] builtin/merge: always store allocated strings in `pull_twohead`
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (24 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07  6:39   ` [PATCH v6 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
  2024-06-07 17:34   ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Junio C Hamano
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 2451 bytes --]

The `pull_twohead` configuration may sometimes contain an allocated
string, and sometimes it may contain a string constant. Refactor this to
instead always store an allocated string such that we can release its
resources without risk.

While at it, manage the lifetime of other config strings, as well. Note
that we explicitly don't free `cleanup_arg` here. This is because the
variable may be assigned a string constant via command line options.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 builtin/merge.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/builtin/merge.c b/builtin/merge.c
index daed2d4e1e..fb3eb15b89 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -611,17 +611,19 @@ static int git_merge_config(const char *k, const char *v,
 		return 0;
 	}
 
-	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
+	if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) {
 		show_diffstat = git_config_bool(k, v);
-	else if (!strcmp(k, "merge.verifysignatures"))
+	} else if (!strcmp(k, "merge.verifysignatures")) {
 		verify_signatures = git_config_bool(k, v);
-	else if (!strcmp(k, "pull.twohead"))
+	} else if (!strcmp(k, "pull.twohead")) {
+		FREE_AND_NULL(pull_twohead);
 		return git_config_string(&pull_twohead, k, v);
-	else if (!strcmp(k, "pull.octopus"))
+	} else if (!strcmp(k, "pull.octopus")) {
+		FREE_AND_NULL(pull_octopus);
 		return git_config_string(&pull_octopus, k, v);
-	else if (!strcmp(k, "commit.cleanup"))
+	} else if (!strcmp(k, "commit.cleanup")) {
 		return git_config_string(&cleanup_arg, k, v);
-	else if (!strcmp(k, "merge.ff")) {
+	} else if (!strcmp(k, "merge.ff")) {
 		int boolval = git_parse_maybe_bool(v);
 		if (0 <= boolval) {
 			fast_forward = boolval ? FF_ALLOW : FF_NO;
@@ -1294,7 +1296,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (!pull_twohead) {
 		char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
 		if (default_strategy && !strcmp(default_strategy, "ort"))
-			pull_twohead = "ort";
+			pull_twohead = xstrdup("ort");
 	}
 
 	init_diff_ui_defaults();
@@ -1793,6 +1795,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 	strbuf_release(&buf);
 	free(branch_to_free);
+	free(pull_twohead);
+	free(pull_octopus);
 	discard_index(the_repository->index);
 	return ret;
 }
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* [PATCH v6 27/27] config.mak.dev: enable `-Wwrite-strings` warning
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (25 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
@ 2024-06-07  6:39   ` Patrick Steinhardt
  2024-06-07 17:34   ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Junio C Hamano
  27 siblings, 0 replies; 205+ messages in thread
From: Patrick Steinhardt @ 2024-06-07  6:39 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Eric Sunshine

[-- Attachment #1: Type: text/plain, Size: 1139 bytes --]

Writing to string constants is undefined behaviour and must be avoided
in C. Even so, the compiler does not help us with this by default
because those constants are not in fact marked as `const`. This makes it
rather easy to accidentally assign a constant to a non-const variable or
field and then later on try to either free it or write to it.

Enable `-Wwrite-strings` to catch such mistakes. With this warning
enabled, the type of string constants is changed to `const char[]` and
will thus cause compiler warnings when being assigned to non-const
fields and variables.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 config.mak.dev | 1 +
 1 file changed, 1 insertion(+)

diff --git a/config.mak.dev b/config.mak.dev
index 981304727c..1ce4c70613 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -37,6 +37,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
 DEVELOPER_CFLAGS += -Wstrict-prototypes
 DEVELOPER_CFLAGS += -Wunused
 DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -Wwrite-strings
 DEVELOPER_CFLAGS += -fno-common
 
 ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
-- 
2.45.2.436.gcd77e87115.dirty


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply related	[flat|nested] 205+ messages in thread

* Re: [PATCH v6 00/27] Compile with `-Wwrite-strings`
  2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
                     ` (26 preceding siblings ...)
  2024-06-07  6:39   ` [PATCH v6 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
@ 2024-06-07 17:34   ` Junio C Hamano
  27 siblings, 0 replies; 205+ messages in thread
From: Junio C Hamano @ 2024-06-07 17:34 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git, Jeff King, Eric Sunshine

Patrick Steinhardt <ps@pks.im> writes:

> The only change is in patch 12, where we now allocate a buffer later in
> time to avoid a useless allocation and the need for a call to free.

That makes sense (and is closer to the original anyway).

Will queue.  Thanks.


^ permalink raw reply	[flat|nested] 205+ messages in thread

* Re: [PATCH 02/19] global: assign non-const strings as required
  2024-06-05 17:13               ` Junio C Hamano
@ 2024-06-08 10:59                 ` Jeff King
  0 siblings, 0 replies; 205+ messages in thread
From: Jeff King @ 2024-06-08 10:59 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Patrick Steinhardt, git

On Wed, Jun 05, 2024 at 10:13:28AM -0700, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > On Fri, May 31, 2024 at 08:27:13AM -0700, Junio C Hamano wrote:
> >
> >> I wonder if we can do something to separate these two concerns
> >> apart, using a trick similar to what we often use with an extra
> >> variable "to_free".  Doing so would bloat the refspec_item, but
> >> unlike the references themselves, there won't be thousands of them,
> >> so it may not be an issue, perhaps?
> >
> > I had a similar thought while looking at this spot a while ago, so I dug
> > this attempt out of my stash. It's quite ugly, as you need to keep the
> > storage pointer and the const pointer in sync. Especially because
> > there's a lot of clever pointer indirection via match_name_with_pattern().
> 
> Ah, true.  The patch itself does not look _too_ bad, but that may
> simply be because the original is bad enough ;-)

A lot of it is not how bad the patch itself looks, but how hard it was
to write. Each site which touches "src" needs to consider whether it
should be "src_storage", and there was a lot of running the test suite
with ASan, followed by head-scratching. I _think_ I got all of the right
spots, but who knows. ;)

Anyway, I think Patrick is quite sensible in not pursuing it further for
this series. But just for posterity, here's a slightly less invasive
version of the patch I showed earlier. I had added an extra arguments to
match_name_with_pattern() to auto-assign the "const" version, but it
turned out that only one caller cared about it (and had to manually
assign the const version in its fallback code path anyway!).

So this version has a little less noise in it.

---
 branch.c         |  6 +++---
 builtin/fetch.c  |  8 ++++----
 builtin/remote.c |  6 +++---
 checkout.c       | 13 +++++++------
 refspec.c        |  6 ++++--
 refspec.h        |  6 ++++--
 remote.c         | 12 +++++++-----
 transport.c      |  2 +-
 8 files changed, 33 insertions(+), 26 deletions(-)

diff --git a/branch.c b/branch.c
index df5d24fec6..e1585b10c2 100644
--- a/branch.c
+++ b/branch.c
@@ -38,7 +38,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 	if (!remote_find_tracking(remote, &tracking->spec)) {
 		switch (++tracking->matches) {
 		case 1:
-			string_list_append_nodup(tracking->srcs, tracking->spec.src);
+			string_list_append_nodup(tracking->srcs, tracking->spec.src_storage);
 			tracking->remote = remote->name;
 			break;
 		case 2:
@@ -47,7 +47,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)
 			/* fall through */
 		default:
 			string_list_append(&ftb->ambiguous_remotes, remote->name);
-			free(tracking->spec.src);
+			free(tracking->spec.src_storage);
 			string_list_clear(tracking->srcs, 0);
 		break;
 		}
@@ -489,7 +489,7 @@ static int check_tracking_branch(struct remote *remote, void *cb_data)
 	memset(&query, 0, sizeof(struct refspec_item));
 	query.dst = tracking_branch;
 	res = !remote_find_tracking(remote, &query);
-	free(query.src);
+	free(query.src_storage);
 	return res;
 }
 
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 06b60867f5..00a67c99ef 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -443,7 +443,7 @@ static void filter_prefetch_refspec(struct refspec *rs)
 
 	for (i = 0; i < rs->nr; i++) {
 		struct strbuf new_dst = STRBUF_INIT;
-		char *old_dst;
+		const char *old_dst;
 		const char *sub = NULL;
 
 		if (rs->items[i].negative)
@@ -454,8 +454,8 @@ static void filter_prefetch_refspec(struct refspec *rs)
 				 ref_namespace[NAMESPACE_TAGS].ref))) {
 			int j;
 
-			free(rs->items[i].src);
-			free(rs->items[i].dst);
+			free(rs->items[i].src_storage);
+			free(rs->items[i].dst_storage);
 
 			for (j = i + 1; j < rs->nr; j++) {
 				rs->items[j - 1] = rs->items[j];
@@ -481,7 +481,7 @@ static void filter_prefetch_refspec(struct refspec *rs)
 		rs->items[i].dst = strbuf_detach(&new_dst, NULL);
 		rs->items[i].force = 1;
 
-		free(old_dst);
+		free(rs->items[i].dst_storage);
 	}
 }
 
diff --git a/builtin/remote.c b/builtin/remote.c
index b44f580b8c..87b97f5ef1 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -496,8 +496,8 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
 	struct refspec_item refspec = {
 		.force = 0,
 		.pattern = 1,
-		.src = (char *) "refs/heads/*",
-		.dst = (char *) "refs/heads/*",
+		.src = "refs/heads/*",
+		.dst = "refs/heads/*",
 	};
 
 	get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
@@ -981,7 +981,7 @@ static int append_ref_to_tracked_list(const char *refname,
 		return 0;
 
 	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
+	refspec.dst = refname;
 	if (!remote_find_tracking(states->remote, &refspec))
 		string_list_append(&states->tracked, abbrev_branch(refspec.src));
 
diff --git a/checkout.c b/checkout.c
index cfaea4bd10..327ab5f6e3 100644
--- a/checkout.c
+++ b/checkout.c
@@ -8,7 +8,7 @@
 #include "strbuf.h"
 
 struct tracking_name_data {
-	/* const */ char *src_ref;
+	const char *src_ref;
 	char *dst_ref;
 	struct object_id *dst_oid;
 	int num_matches;
@@ -27,7 +27,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
 	query.src = cb->src_ref;
 	if (remote_find_tracking(remote, &query) ||
 	    repo_get_oid(the_repository, query.dst, cb->dst_oid)) {
-		free(query.dst);
+		free(query.dst_storage);
 		return 0;
 	}
 	cb->num_matches++;
@@ -38,10 +38,10 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
 		cb->default_dst_oid = dst;
 	}
 	if (cb->dst_ref) {
-		free(query.dst);
+		free(query.dst_storage);
 		return 0;
 	}
-	cb->dst_ref = query.dst;
+	cb->dst_ref = query.dst_storage;
 	return 0;
 }
 
@@ -50,14 +50,15 @@ char *unique_tracking_name(const char *name, struct object_id *oid,
 {
 	struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
 	const char *default_remote = NULL;
+	char *src_ref;
 	if (!git_config_get_string_tmp("checkout.defaultremote", &default_remote))
 		cb_data.default_remote = default_remote;
-	cb_data.src_ref = xstrfmt("refs/heads/%s", name);
+	cb_data.src_ref = src_ref = xstrfmt("refs/heads/%s", name);
 	cb_data.dst_oid = oid;
 	for_each_remote(check_tracking_name, &cb_data);
 	if (dwim_remotes_matched)
 		*dwim_remotes_matched = cb_data.num_matches;
-	free(cb_data.src_ref);
+	free(src_ref);
 	if (cb_data.num_matches == 1) {
 		free(cb_data.default_dst_ref);
 		free(cb_data.default_dst_oid);
diff --git a/refspec.c b/refspec.c
index 1df5de6c2f..a7473c9628 100644
--- a/refspec.c
+++ b/refspec.c
@@ -163,8 +163,10 @@ void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
 
 void refspec_item_clear(struct refspec_item *item)
 {
-	FREE_AND_NULL(item->src);
-	FREE_AND_NULL(item->dst);
+	FREE_AND_NULL(item->src_storage);
+	item->src = NULL;
+	FREE_AND_NULL(item->dst_storage);
+	item->dst = NULL;
 	item->force = 0;
 	item->pattern = 0;
 	item->matching = 0;
diff --git a/refspec.h b/refspec.h
index 754be45cee..fbc4352397 100644
--- a/refspec.h
+++ b/refspec.h
@@ -24,8 +24,10 @@ struct refspec_item {
 	unsigned exact_sha1 : 1;
 	unsigned negative : 1;
 
-	char *src;
-	char *dst;
+	const char *src;
+	const char *dst;
+	char *src_storage;
+	char *dst_storage;
 };
 
 #define REFSPEC_FETCH 1
diff --git a/remote.c b/remote.c
index d319f28757..d1511ecf83 100644
--- a/remote.c
+++ b/remote.c
@@ -967,7 +967,7 @@ static void query_refspecs_multiple(struct refspec *rs,
 		const char *key = find_src ? refspec->dst : refspec->src;
 		const char *value = find_src ? refspec->src : refspec->dst;
 		const char *needle = find_src ? query->dst : query->src;
-		char **result = find_src ? &query->src : &query->dst;
+		char **result = find_src ? &query->src_storage : &query->dst_storage;
 
 		if (!refspec->dst || refspec->negative)
 			continue;
@@ -985,7 +985,8 @@ int query_refspecs(struct refspec *rs, struct refspec_item *query)
 	int i;
 	int find_src = !query->src;
 	const char *needle = find_src ? query->dst : query->src;
-	char **result = find_src ? &query->src : &query->dst;
+	char **result = find_src ? &query->src_storage : &query->dst_storage;
+	const char **result_const = find_src ? &query->src : &query->dst;
 
 	if (find_src && !query->dst)
 		BUG("query_refspecs: need either src or dst");
@@ -1002,11 +1003,12 @@ int query_refspecs(struct refspec *rs, struct refspec_item *query)
 			continue;
 		if (refspec->pattern) {
 			if (match_name_with_pattern(key, needle, value, result)) {
+				*result_const = *result;
 				query->force = refspec->force;
 				return 0;
 			}
 		} else if (!strcmp(needle, key)) {
-			*result = xstrdup(value);
+			*result_const = *result = xstrdup(value);
 			query->force = refspec->force;
 			return 0;
 		}
@@ -1019,12 +1021,12 @@ char *apply_refspecs(struct refspec *rs, const char *name)
 	struct refspec_item query;
 
 	memset(&query, 0, sizeof(struct refspec_item));
-	query.src = (char *)name;
+	query.src = name;
 
 	if (query_refspecs(rs, &query))
 		return NULL;
 
-	return query.dst;
+	return query.dst_storage;
 }
 
 int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)
diff --git a/transport.c b/transport.c
index 83ddea8fbc..0cedda425c 100644
--- a/transport.c
+++ b/transport.c
@@ -550,7 +550,7 @@ static void update_one_tracking_ref(struct remote *remote, char *refname,
 			refs_update_ref(get_main_ref_store(the_repository),
 					"update by push", rs.dst, new_oid,
 					NULL, 0, 0);
-		free(rs.dst);
+		free(rs.dst_storage);
 	}
 }
 

^ permalink raw reply related	[flat|nested] 205+ messages in thread

end of thread, other threads:[~2024-06-08 10:59 UTC | newest]

Thread overview: 205+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-29 12:44 [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
2024-05-29 12:44 ` [PATCH 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
2024-05-29 16:58   ` Junio C Hamano
2024-05-30 11:29     ` Patrick Steinhardt
2024-05-29 12:44 ` [PATCH 02/19] global: assign non-const strings as required Patrick Steinhardt
2024-05-29 17:25   ` Junio C Hamano
2024-05-30 11:29     ` Patrick Steinhardt
2024-05-30 19:38       ` Junio C Hamano
2024-05-31 13:00         ` Patrick Steinhardt
2024-05-31 13:33           ` Patrick Steinhardt
2024-05-31 15:27             ` Junio C Hamano
2024-05-31 15:27           ` Junio C Hamano
2024-06-05 10:46             ` Jeff King
2024-06-05 17:13               ` Junio C Hamano
2024-06-08 10:59                 ` Jeff King
2024-06-06 10:36               ` Patrick Steinhardt
2024-05-29 12:44 ` [PATCH 03/19] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
2024-05-29 17:28   ` Junio C Hamano
2024-05-30 11:30     ` Patrick Steinhardt
2024-05-30 16:00       ` Junio C Hamano
2024-05-29 12:44 ` [PATCH 04/19] compat/win32: fix const-correctness with string constants Patrick Steinhardt
2024-05-29 12:44 ` [PATCH 05/19] reftable: improve const correctness when assigning " Patrick Steinhardt
2024-05-29 17:43   ` Junio C Hamano
2024-05-30 11:30     ` Patrick Steinhardt
2024-05-30 16:07       ` Junio C Hamano
2024-05-29 12:44 ` [PATCH 06/19] refspec: remove global tag refspec structure Patrick Steinhardt
2024-05-29 17:47   ` Junio C Hamano
2024-05-29 12:44 ` [PATCH 07/19] http: do not assign string constant to non-const field Patrick Steinhardt
2024-05-29 19:39   ` Junio C Hamano
2024-05-29 12:44 ` [PATCH 08/19] line-log: always allocate the output prefix Patrick Steinhardt
2024-05-29 19:51   ` Junio C Hamano
2024-05-29 12:44 ` [PATCH 09/19] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
2024-05-29 20:01   ` Junio C Hamano
2024-05-29 12:44 ` [PATCH 10/19] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
2024-05-29 12:44 ` [PATCH 11/19] send-pack: always allocate receive status Patrick Steinhardt
2024-05-29 12:44 ` [PATCH 12/19] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
2024-05-29 20:21   ` Junio C Hamano
2024-05-30 11:30     ` Patrick Steinhardt
2024-05-29 12:45 ` [PATCH 13/19] revision: always store allocated strings in output encoding Patrick Steinhardt
2024-05-29 20:23   ` Junio C Hamano
2024-05-29 12:45 ` [PATCH 14/19] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
2024-05-29 12:45 ` [PATCH 15/19] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
2024-05-29 12:45 ` [PATCH 16/19] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
2024-05-29 20:55   ` Junio C Hamano
2024-05-30 11:31     ` Patrick Steinhardt
2024-05-30 16:30       ` Junio C Hamano
2024-05-29 12:45 ` [PATCH 17/19] builtin/rebase: adapt code to not assign string constants to non-const Patrick Steinhardt
2024-05-29 21:01   ` Junio C Hamano
2024-05-30 11:31     ` Patrick Steinhardt
2024-05-29 12:45 ` [PATCH 18/19] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
2024-05-29 12:45 ` [PATCH 19/19] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
2024-05-29 12:52 ` [PATCH 00/19] Compile with `-Wwrite-strings` Patrick Steinhardt
2024-05-30 12:50 ` [PATCH v2 " Patrick Steinhardt
2024-05-30 12:50   ` [PATCH v2 01/19] global: improve const correctness when assigning string constants Patrick Steinhardt
2024-05-30 12:50   ` [PATCH v2 02/19] global: assign non-const strings as required Patrick Steinhardt
2024-05-30 12:50   ` [PATCH v2 03/19] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
2024-05-30 12:50   ` [PATCH v2 04/19] compat/win32: fix const-correctness with string constants Patrick Steinhardt
2024-05-30 12:50   ` [PATCH v2 05/19] refspec: remove global tag refspec structure Patrick Steinhardt
2024-05-30 12:50   ` [PATCH v2 06/19] http: do not assign string constant to non-const field Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 07/19] line-log: always allocate the output prefix Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 08/19] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 09/19] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 10/19] send-pack: always allocate receive status Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 11/19] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 12/19] revision: always store allocated strings in output encoding Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 13/19] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 14/19] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 15/19] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 16/19] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 17/19] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
2024-05-30 12:51   ` [PATCH v2 18/19] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
2024-05-30 12:52   ` [PATCH v2 19/19] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
2024-05-31  9:13   ` [PATCH v2 00/19] Compile with `-Wwrite-strings` Junio C Hamano
2024-05-31 12:10     ` Patrick Steinhardt
2024-06-03  9:38 ` [PATCH v3 00/27] " Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
2024-06-03 18:08     ` Junio C Hamano
2024-06-03  9:39   ` [PATCH v3 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
2024-06-03 18:11     ` Junio C Hamano
2024-06-03  9:39   ` [PATCH v3 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 09/27] line-log: always allocate the output prefix Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
2024-06-03  9:39   ` [PATCH v3 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
2024-06-03 16:57     ` Eric Sunshine
2024-06-03 19:04       ` Junio C Hamano
2024-06-04  6:42         ` Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 18/27] send-pack: always allocate receive status Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
2024-06-03  9:40   ` [PATCH v3 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
2024-06-03  9:41   ` [PATCH v3 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
2024-06-03  9:41   ` [PATCH v3 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
2024-06-03 16:59   ` [PATCH v3 00/27] Compile with `-Wwrite-strings` Junio C Hamano
2024-06-04 12:36 ` [PATCH v4 " Patrick Steinhardt
2024-06-04 12:36   ` [PATCH v4 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 09/27] line-log: always allocate the output prefix Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
2024-06-06  6:02     ` Junio C Hamano
2024-06-06  6:10       ` Junio C Hamano
2024-06-06 10:03         ` Patrick Steinhardt
2024-06-06 16:25         ` Junio C Hamano
2024-06-07  4:52           ` Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
2024-06-04 12:37   ` [PATCH v4 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 18/27] send-pack: always allocate receive status Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
2024-06-04 12:38   ` [PATCH v4 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
2024-06-04 14:06     ` Phillip Wood
2024-06-05  5:40       ` Patrick Steinhardt
2024-06-05 13:06         ` Phillip Wood
2024-06-06  9:50           ` Patrick Steinhardt
2024-06-05 16:11         ` Junio C Hamano
2024-06-04 12:38   ` [PATCH v4 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
2024-06-04 14:10     ` Phillip Wood
2024-06-04 12:38   ` [PATCH v4 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
2024-06-04 12:39   ` [PATCH v4 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
2024-06-06 10:27 ` [PATCH v5 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
2024-06-06 10:27   ` [PATCH v5 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
2024-06-06 10:27   ` [PATCH v5 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
2024-06-06 10:27   ` [PATCH v5 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 09/27] line-log: always allocate the output prefix Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
2024-06-06 17:54     ` Junio C Hamano
2024-06-06 10:28   ` [PATCH v5 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
2024-06-06 10:28   ` [PATCH v5 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 18/27] send-pack: always allocate receive status Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
2024-06-06 10:29   ` [PATCH v5 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
2024-06-07  6:37 ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Patrick Steinhardt
2024-06-07  6:37   ` [PATCH v6 01/27] global: improve const correctness when assigning string constants Patrick Steinhardt
2024-06-07  6:37   ` [PATCH v6 02/27] global: convert intentionally-leaking config strings to consts Patrick Steinhardt
2024-06-07  6:37   ` [PATCH v6 03/27] refs/reftable: stop micro-optimizing refname allocations on copy Patrick Steinhardt
2024-06-07  6:37   ` [PATCH v6 04/27] reftable: cast away constness when assigning constants to records Patrick Steinhardt
2024-06-07  6:37   ` [PATCH v6 05/27] refspec: remove global tag refspec structure Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 06/27] builtin/remote: cast away constness in `get_head_names()` Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 07/27] diff: cast string constant in `fill_textconv()` Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 08/27] line-log: stop assigning string constant to file parent buffer Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 09/27] line-log: always allocate the output prefix Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 10/27] entry: refactor how we remove items for delayed checkouts Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 11/27] ident: add casts for fallback name and GECOS Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 12/27] object-file: mark cached object buffers as const Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 13/27] object-file: make `buf` parameter of `index_mem()` a constant Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 14/27] pretty: add casts for decoration option pointers Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 15/27] compat/win32: fix const-correctness with string constants Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 16/27] http: do not assign string constant to non-const field Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 17/27] parse-options: cast long name for OPTION_ALIAS Patrick Steinhardt
2024-06-07  6:38   ` [PATCH v6 18/27] send-pack: always allocate receive status Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 19/27] remote-curl: avoid assigning string constant to non-const variable Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 20/27] revision: always store allocated strings in output encoding Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 21/27] mailmap: always store allocated strings in mailmap blob Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 22/27] imap-send: drop global `imap_server_conf` variable Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 23/27] imap-send: fix leaking memory in `imap_server_conf` Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 24/27] builtin/rebase: do not assign default backend to non-constant field Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 25/27] builtin/rebase: always store allocated string in `options.strategy` Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 26/27] builtin/merge: always store allocated strings in `pull_twohead` Patrick Steinhardt
2024-06-07  6:39   ` [PATCH v6 27/27] config.mak.dev: enable `-Wwrite-strings` warning Patrick Steinhardt
2024-06-07 17:34   ` [PATCH v6 00/27] Compile with `-Wwrite-strings` Junio C Hamano

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).