git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] revert: convert resolve_ref() to read_ref_full()
@ 2011-12-12 11:20 Nguyễn Thái Ngọc Duy
  2011-12-12 11:20 ` [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function Nguyễn Thái Ngọc Duy
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2011-12-12 11:20 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jonathan Nieder,
	Nguyễn Thái Ngọc Duy

This is the follow up of c689332 (Convert many resolve_ref() calls to
read_ref*() and ref_exists() - 2011-11-13). See the said commit for
rationale.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/revert.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/builtin/revert.c b/builtin/revert.c
index 1ea525c..0c52a83 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -901,7 +901,7 @@ static int rollback_single_pick(void)
 	if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
 	    !file_exists(git_path("REVERT_HEAD")))
 		return error(_("no cherry-pick or revert in progress"));
-	if (!resolve_ref("HEAD", head_sha1, 0, NULL))
+	if (read_ref_full("HEAD", head_sha1, 0, NULL))
 		return error(_("cannot resolve HEAD"));
 	if (is_null_sha1(head_sha1))
 		return error(_("cannot abort from a branch yet to be born"));
-- 
1.7.8.36.g69ee2

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

* [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function
  2011-12-12 11:20 [PATCH 1/4] revert: convert resolve_ref() to read_ref_full() Nguyễn Thái Ngọc Duy
@ 2011-12-12 11:20 ` Nguyễn Thái Ngọc Duy
  2011-12-12 18:07   ` Junio C Hamano
  2011-12-12 11:20 ` [PATCH 3/4] Guard memory overwriting in resolve_ref's static buffer Nguyễn Thái Ngọc Duy
  2011-12-12 11:20 ` [PATCH 4/4] Rename resolve_ref() to resolve_ref_unsafe() Nguyễn Thái Ngọc Duy
  2 siblings, 1 reply; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2011-12-12 11:20 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jonathan Nieder,
	Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/branch.c        |   11 ++++-------
 builtin/checkout.c      |   15 +++++++--------
 builtin/fmt-merge-msg.c |    6 +++---
 builtin/for-each-ref.c  |    7 ++-----
 builtin/merge.c         |   12 +++++-------
 builtin/notes.c         |    6 +++---
 builtin/receive-pack.c  |    8 +++-----
 builtin/show-branch.c   |    4 +---
 cache.h                 |    1 +
 reflog-walk.c           |   13 ++++++-------
 refs.c                  |    6 ++++++
 wt-status.c             |    4 +---
 12 files changed, 42 insertions(+), 51 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index e1e486e..72c4c31 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -103,7 +103,7 @@ static int branch_merged(int kind, const char *name,
 	 * safely to HEAD (or the other branch).
 	 */
 	struct commit *reference_rev = NULL;
-	const char *reference_name = NULL;
+	char *reference_name = NULL;
 	int merged;
 
 	if (kind == REF_LOCAL_BRANCH) {
@@ -115,10 +115,8 @@ static int branch_merged(int kind, const char *name,
 		    branch->merge[0] &&
 		    branch->merge[0]->dst &&
 		    (reference_name =
-		     resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL) {
-			reference_name = xstrdup(reference_name);
+		     resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
 			reference_rev = lookup_commit_reference(sha1);
-		}
 	}
 	if (!reference_rev)
 		reference_rev = head_rev;
@@ -143,7 +141,7 @@ static int branch_merged(int kind, const char *name,
 				"         '%s', even though it is merged to HEAD."),
 				name, reference_name);
 	}
-	free((char *)reference_name);
+	free(reference_name);
 	return merged;
 }
 
@@ -731,10 +729,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	track = git_branch_track;
 
-	head = resolve_ref("HEAD", head_sha1, 0, NULL);
+	head = resolve_refdup("HEAD", head_sha1, 0, NULL);
 	if (!head)
 		die(_("Failed to resolve HEAD as a valid ref."));
-	head = xstrdup(head);
 	if (!strcmp(head, "HEAD")) {
 		detached = 1;
 	} else {
diff --git a/builtin/checkout.c b/builtin/checkout.c
index b7c6302..a66d3eb 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -696,17 +696,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 {
 	int ret = 0;
 	struct branch_info old;
+	char *path;
 	unsigned char rev[20];
 	int flag;
 	memset(&old, 0, sizeof(old));
-	old.path = resolve_ref("HEAD", rev, 0, &flag);
-	if (old.path)
-		old.path = xstrdup(old.path);
+	old.path = path = resolve_refdup("HEAD", rev, 0, &flag);
 	old.commit = lookup_commit_reference_gently(rev, 1);
-	if (!(flag & REF_ISSYMREF)) {
-		free((char *)old.path);
+	if (!(flag & REF_ISSYMREF))
 		old.path = NULL;
-	}
 
 	if (old.path && !prefixcmp(old.path, "refs/heads/"))
 		old.name = old.path + strlen("refs/heads/");
@@ -720,8 +717,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 	}
 
 	ret = merge_working_tree(opts, &old, new);
-	if (ret)
+	if (ret) {
+		free(path);
 		return ret;
+	}
 
 	if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
 		orphaned_commit_warning(old.commit);
@@ -729,7 +728,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 	update_refs_for_switch(opts, &old, new);
 
 	ret = post_checkout_hook(old.commit, new->commit, 1);
-	free((char *)old.path);
+	free(path);
 	return ret || opts->writeout_error;
 }
 
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index bdfa0ea..a27efcd 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -372,14 +372,14 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 	int i = 0, pos = 0;
 	unsigned char head_sha1[20];
 	const char *current_branch;
+	char *ref;
 
 	/* get current branch */
-	current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+	current_branch = ref = resolve_refdup("HEAD", head_sha1, 1, NULL);
 	if (!current_branch)
 		die("No current branch");
 	if (!prefixcmp(current_branch, "refs/heads/"))
 		current_branch += 11;
-	current_branch = xstrdup(current_branch);
 
 	/* get a line */
 	while (pos < in->len) {
@@ -421,7 +421,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 	}
 
 	strbuf_complete_line(out);
-	free((char *)current_branch);
+	free(ref);
 	return 0;
 }
 
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index d90e5d2..b01d76a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -628,11 +628,8 @@ static void populate_value(struct refinfo *ref)
 
 	if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
 		unsigned char unused1[20];
-		const char *symref;
-		symref = resolve_ref(ref->refname, unused1, 1, NULL);
-		if (symref)
-			ref->symref = xstrdup(symref);
-		else
+		ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+		if (!ref->symref)
 			ref->symref = "";
 	}
 
diff --git a/builtin/merge.c b/builtin/merge.c
index a1c8534..6437db2 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1096,6 +1096,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	struct commit_list *common = NULL;
 	const char *best_strategy = NULL, *wt_strategy = NULL;
 	struct commit_list **remotes = &remoteheads;
+	char *branch_ref;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1104,12 +1105,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
 	 * current branch.
 	 */
-	branch = resolve_ref("HEAD", head_sha1, 0, &flag);
-	if (branch) {
-		if (!prefixcmp(branch, "refs/heads/"))
-			branch += 11;
-		branch = xstrdup(branch);
-	}
+	branch = branch_ref = resolve_refdup("HEAD", head_sha1, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
 	if (!branch || is_null_sha1(head_sha1))
 		head_commit = NULL;
 	else
@@ -1520,6 +1518,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		ret = suggest_conflicts(option_renormalize);
 
 done:
-	free((char *)branch);
+	free(branch_ref);
 	return ret;
 }
diff --git a/builtin/notes.c b/builtin/notes.c
index 10b8bc7..816c998 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -804,6 +804,7 @@ static int merge_commit(struct notes_merge_options *o)
 	struct notes_tree *t;
 	struct commit *partial;
 	struct pretty_print_context pretty_ctx;
+	char *ref;
 	int ret;
 
 	/*
@@ -826,10 +827,9 @@ static int merge_commit(struct notes_merge_options *o)
 	t = xcalloc(1, sizeof(struct notes_tree));
 	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
-	o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL);
+	o->local_ref = ref = resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
 	if (!o->local_ref)
 		die("Failed to resolve NOTES_MERGE_REF");
-	o->local_ref = xstrdup(o->local_ref);
 
 	if (notes_merge_commit(o, t, partial, sha1))
 		die("Failed to finalize notes merge");
@@ -846,7 +846,7 @@ static int merge_commit(struct notes_merge_options *o)
 	free_notes(t);
 	strbuf_release(&msg);
 	ret = merge_abort(o);
-	free((char *)o->local_ref);
+	free(ref);
 	return ret;
 }
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b6d957c..5cd6fc7 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -36,7 +36,7 @@ static int use_sideband;
 static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
-static const char *head_name;
+static char *head_name;
 static int sent_capabilities;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -695,10 +695,8 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
 
 	check_aliased_updates(commands);
 
-	free((char *)head_name);
-	head_name = resolve_ref("HEAD", sha1, 0, NULL);
-	if (head_name)
-		head_name = xstrdup(head_name);
+	free(head_name);
+	head_name = resolve_refdup("HEAD", sha1, 0, NULL);
 
 	for (cmd = commands; cmd; cmd = cmd->next)
 		if (!cmd->skip_update)
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 4b480d7..a1f148e 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -726,10 +726,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 
 		if (ac == 0) {
 			static const char *fake_av[2];
-			const char *refname;
 
-			refname = resolve_ref("HEAD", sha1, 1, NULL);
-			fake_av[0] = xstrdup(refname);
+			fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
 			fake_av[1] = NULL;
 			av = fake_av;
 			ac = 1;
diff --git a/cache.h b/cache.h
index 8c98d05..4887a3e 100644
--- a/cache.h
+++ b/cache.h
@@ -866,6 +866,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
  * errno is sometimes set on errors, but not always.
  */
 extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
 
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/reflog-walk.c b/reflog-walk.c
index da71a85..5572b42 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -50,11 +50,10 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 	for_each_reflog_ent(ref, read_one_reflog, reflogs);
 	if (reflogs->nr == 0) {
 		unsigned char sha1[20];
-		const char *name = resolve_ref(ref, sha1, 1, NULL);
+		char *name = resolve_refdup(ref, sha1, 1, NULL);
 		if (name) {
-			name = xstrdup(name);
 			for_each_reflog_ent(name, read_one_reflog, reflogs);
-			free((char *)name);
+			free(name);
 		}
 	}
 	if (reflogs->nr == 0) {
@@ -171,11 +170,11 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
 	else {
 		if (*branch == '\0') {
 			unsigned char sha1[20];
-			const char *head = resolve_ref("HEAD", sha1, 0, NULL);
-			if (!head)
-				die ("No current branch");
 			free(branch);
-			branch = xstrdup(head);
+			branch = resolve_refdup("HEAD", sha1, 0, NULL);
+			if (!branch)
+				die ("No current branch");
+
 		}
 		reflogs = read_complete_reflog(branch);
 		if (!reflogs || reflogs->nr == 0) {
diff --git a/refs.c b/refs.c
index f5cb297..8ffb32f 100644
--- a/refs.c
+++ b/refs.c
@@ -605,6 +605,12 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
 	return ref;
 }
 
+char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+{
+	const char *ret = resolve_ref(ref, sha1, reading, flag);
+	return ret ? xstrdup(ret) : NULL;
+}
+
 /* The argument to filter_refs */
 struct ref_filter {
 	const char *pattern;
diff --git a/wt-status.c b/wt-status.c
index 70fdb76..9ffc535 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -111,7 +111,6 @@ void status_printf_more(struct wt_status *s, const char *color,
 void wt_status_prepare(struct wt_status *s)
 {
 	unsigned char sha1[20];
-	const char *head;
 
 	memset(s, 0, sizeof(*s));
 	memcpy(s->color_palette, default_wt_status_colors,
@@ -119,8 +118,7 @@ void wt_status_prepare(struct wt_status *s)
 	s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 	s->use_color = -1;
 	s->relative_paths = 1;
-	head = resolve_ref("HEAD", sha1, 0, NULL);
-	s->branch = head ? xstrdup(head) : NULL;
+	s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
 	s->reference = "HEAD";
 	s->fp = stdout;
 	s->index_file = get_index_file();
-- 
1.7.8.36.g69ee2

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

* [PATCH 3/4] Guard memory overwriting in resolve_ref's static buffer
  2011-12-12 11:20 [PATCH 1/4] revert: convert resolve_ref() to read_ref_full() Nguyễn Thái Ngọc Duy
  2011-12-12 11:20 ` [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function Nguyễn Thái Ngọc Duy
@ 2011-12-12 11:20 ` Nguyễn Thái Ngọc Duy
  2011-12-13  0:37   ` Junio C Hamano
  2011-12-12 11:20 ` [PATCH 4/4] Rename resolve_ref() to resolve_ref_unsafe() Nguyễn Thái Ngọc Duy
  2 siblings, 1 reply; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2011-12-12 11:20 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jonathan Nieder,
	Nguyễn Thái Ngọc Duy

There is a potential problem with resolve_ref() and some other
functions in git. The return value returned by resolve_ref() may
be changed when the function is called again. Callers must make sure the
next call won't happen as long as the value is still being used.

It's usually hard to track down this kind of problem.  Michael Haggerty
has an idea [1] that, instead of passing the same static buffer to
caller every time the function is called, we free the old buffer and
allocate the new one. This way access to the old (now invalid) buffer
may be caught.

This patch applies the same principle for resolve_ref() with a
few modifications:

 - This behavior is enabled when GIT_DEBUG_MEMCHECK is set. The ability
   is always available. We may be able to ask users to rerun with this
   flag on in suspicious cases.

 - Rely on mmap/mprotect to catch illegal access. We need valgrind or
   some other memory tracking tool to reliably catch this in Michael's
   approach.

 - Because mprotect is used instead of munmap, we definitely leak
   memory. Hopefully callers will not put resolve_ref() in a
   loop that runs 1 million times.

 - Save caller location in the allocated buffer so we know who made this
   call in the core dump.

Also introduce a new target, "make memcheck", that runs tests with this
flag on.

[1] http://comments.gmane.org/gmane.comp.version-control.git/182209

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Changes include:

  - __FUNCTION__ to __FILE__ for compiler compatibility
  - x{malloc,free}_mmap() now put call site information in a struct,
    it's clearer this way and hopefully will avoid platform issues
  - update t0071 to follow '&&' convention
  - add notes where to get caller site info in git-compat-util.h

 .gitignore          |    1 +
 Makefile            |    4 ++++
 cache.h             |    3 ++-
 git-compat-util.h   |   20 ++++++++++++++++++++
 refs.c              |   13 +++++++++++--
 t/t0071-memcheck.sh |   11 +++++++++++
 test-resolve-ref.c  |   18 ++++++++++++++++++
 wrapper.c           |   27 +++++++++++++++++++++++++++
 8 files changed, 94 insertions(+), 3 deletions(-)
 create mode 100755 t/t0071-memcheck.sh
 create mode 100644 test-resolve-ref.c

diff --git a/.gitignore b/.gitignore
index 8572c8c..470e452 100644
--- a/.gitignore
+++ b/.gitignore
@@ -179,6 +179,7 @@
 /test-obj-pool
 /test-parse-options
 /test-path-utils
+/test-resolve-ref
 /test-run-command
 /test-sha1
 /test-sigchain
diff --git a/Makefile b/Makefile
index ed82320..d71cf04 100644
--- a/Makefile
+++ b/Makefile
@@ -444,6 +444,7 @@ TEST_PROGRAMS_NEED_X += test-obj-pool
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
 TEST_PROGRAMS_NEED_X += test-run-command
+TEST_PROGRAMS_NEED_X += test-resolve-ref
 TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-string-pool
@@ -2241,6 +2242,9 @@ export NO_SVN_TESTS
 test: all
 	$(MAKE) -C t/ all
 
+memcheck: all
+	GIT_DEBUG_MEMCHECK=1 $(MAKE) -C t/ all
+
 test-ctype$X: ctype.o
 
 test-date$X: date.o ctype.o
diff --git a/cache.h b/cache.h
index 4887a3e..ba5e911 100644
--- a/cache.h
+++ b/cache.h
@@ -865,7 +865,8 @@ extern int read_ref(const char *filename, unsigned char *sha1);
  *
  * errno is sometimes set on errors, but not always.
  */
-extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+#define resolve_ref(ref, sha1, reading, flag) resolve_ref_real(ref, sha1, reading, flag, __FILE__, __LINE__)
+extern const char *resolve_ref_real(const char *ref, unsigned char *sha1, int reading, int *flag, const char *file, int line);
 extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
 
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/git-compat-util.h b/git-compat-util.h
index 77062ed..0cb6e34 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -433,6 +433,26 @@ extern char *xstrndup(const char *str, size_t len);
 extern void *xrealloc(void *ptr, size_t size);
 extern void *xcalloc(size_t nmemb, size_t size);
 extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+
+/*
+ * These functions are used to allocate new memory blocks and catch
+ * invalid use after they are released (though the memory is never
+ * returned to system, so do not allocate too much this way).
+ *
+ * mprotect() is used to remove all access to memory when xfree_mmap()
+ * is called. Invalid access will cause sigsegv. The memory block is
+ * preceded by struct alloc_header, describing where it is
+ * allocated. This information can be found in the core dump.
+ */
+struct alloc_header {
+	const char *file;
+	int line;
+	int size;
+	char buf[FLEX_ARRAY];
+};
+extern void *xmalloc_mmap(size_t, const char *file, int line);
+extern void xfree_mmap(void *);
+
 extern ssize_t xread(int fd, void *buf, size_t len);
 extern ssize_t xwrite(int fd, const void *buf, size_t len);
 extern int xdup(int fd);
diff --git a/refs.c b/refs.c
index 8ffb32f..cf8dfcc 100644
--- a/refs.c
+++ b/refs.c
@@ -497,12 +497,21 @@ static int get_packed_ref(const char *ref, unsigned char *sha1)
 	return -1;
 }
 
-const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
+const char *resolve_ref_real(const char *ref, unsigned char *sha1,
+			     int reading, int *flag, const char *file, int line)
 {
 	int depth = MAXDEPTH;
 	ssize_t len;
 	char buffer[256];
-	static char ref_buffer[256];
+	static char real_ref_buffer[256];
+	static char *ref_buffer;
+
+	if (!ref_buffer && !getenv("GIT_DEBUG_MEMCHECK"))
+		ref_buffer = real_ref_buffer;
+	if (ref_buffer != real_ref_buffer) {
+		xfree_mmap(ref_buffer);
+		ref_buffer = xmalloc_mmap(256, file, line);
+	}
 
 	if (flag)
 		*flag = 0;
diff --git a/t/t0071-memcheck.sh b/t/t0071-memcheck.sh
new file mode 100755
index 0000000..8904369
--- /dev/null
+++ b/t/t0071-memcheck.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+test_description='test that GIT_DEBUG_MEMCHECK works correctly'
+. ./test-lib.sh
+
+test_expect_success 'test-resolve-ref must crash' '
+	test-resolve-ref &&
+	GIT_DEBUG_MEMCHECK=1 test_expect_code 139 test-resolve-ref
+'
+
+test_done
diff --git a/test-resolve-ref.c b/test-resolve-ref.c
new file mode 100644
index 0000000..b772038
--- /dev/null
+++ b/test-resolve-ref.c
@@ -0,0 +1,18 @@
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+	unsigned char sha1[20];
+	const char *ref1, *ref2;
+	setup_git_directory();
+
+	/*
+	 * This is an invalid use of resolve_ref_unsafe(). This
+	 * function returns a shared buffer, so by the time the second
+	 * call is made, ref1 must _not_ be accessed any more.
+	 */
+	ref1 = resolve_ref("HEAD", sha1, 0, NULL);
+	ref2 = resolve_ref("HEAD", sha1, 0, NULL);
+	printf("ref1 = %s\nref2 = %s\n", ref1, ref2);
+	return 0;
+}
diff --git a/wrapper.c b/wrapper.c
index 85f09df..02b6c81 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -60,6 +60,33 @@ void *xmallocz(size_t size)
 	return ret;
 }
 
+void *xmalloc_mmap(size_t size, const char *file, int line)
+{
+	struct alloc_header *block;
+	size += offsetof(struct alloc_header,buf);
+	block = mmap(NULL, size, PROT_READ | PROT_WRITE,
+		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	if (block == (struct alloc_header*)-1)
+		die_errno("unable to mmap %lu bytes anonymously",
+			  (unsigned long)size);
+
+	block->file = file;
+	block->line = line;
+	block->size = size;
+	return block->buf;
+}
+
+void xfree_mmap(void *p)
+{
+	struct alloc_header *block;
+
+	if (!p)
+		return;
+	block = (struct alloc_header *)((char*)p - offsetof(struct alloc_header,buf));
+	if (mprotect(block, block->size, PROT_NONE) == -1)
+		die_errno("unable to remove memory access");
+}
+
 /*
  * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
  * "data" to the allocated memory, zero terminates the allocated memory,
-- 
1.7.8.36.g69ee2

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

* [PATCH 4/4] Rename resolve_ref() to resolve_ref_unsafe()
  2011-12-12 11:20 [PATCH 1/4] revert: convert resolve_ref() to read_ref_full() Nguyễn Thái Ngọc Duy
  2011-12-12 11:20 ` [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function Nguyễn Thái Ngọc Duy
  2011-12-12 11:20 ` [PATCH 3/4] Guard memory overwriting in resolve_ref's static buffer Nguyễn Thái Ngọc Duy
@ 2011-12-12 11:20 ` Nguyễn Thái Ngọc Duy
  2 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2011-12-12 11:20 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jonathan Nieder,
	Nguyễn Thái Ngọc Duy

resolve_ref() may return a pointer to a shared buffer and can be
overwritten by the next resolve_ref() calls. Callers need to
pay attention, not to keep the pointer when the next call happens.

Rename with "_unsafe" suffix to warn developers (or reviewers) before
introducing new call sites.

This patch is generated using the following command

git grep -l 'resolve_ref(' -- '*.[ch]'|xargs sed -i 's/resolve_ref(/resolve_ref_unsafe(/g'

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 branch.c               |    2 +-
 builtin/branch.c       |    2 +-
 builtin/commit.c       |    2 +-
 builtin/fsck.c         |    2 +-
 builtin/log.c          |    4 ++--
 builtin/receive-pack.c |    2 +-
 builtin/remote.c       |    2 +-
 builtin/show-branch.c  |    2 +-
 builtin/symbolic-ref.c |    2 +-
 cache.h                |    2 +-
 refs.c                 |   20 ++++++++++----------
 remote.c               |    6 +++---
 test-resolve-ref.c     |    4 ++--
 transport.c            |    2 +-
 14 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/branch.c b/branch.c
index d91a099..772a4c2 100644
--- a/branch.c
+++ b/branch.c
@@ -182,7 +182,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
 		const char *head;
 		unsigned char sha1[20];
 
-		head = resolve_ref("HEAD", sha1, 0, NULL);
+		head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
 		if (!is_bare_repository() && head && !strcmp(head, ref->buf))
 			die("Cannot force update the current branch.");
 	}
diff --git a/builtin/branch.c b/builtin/branch.c
index 72c4c31..641bee1 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -251,7 +251,7 @@ static char *resolve_symref(const char *src, const char *prefix)
 	int flag;
 	const char *dst, *cp;
 
-	dst = resolve_ref(src, sha1, 0, &flag);
+	dst = resolve_ref_unsafe(src, sha1, 0, &flag);
 	if (!(dst && (flag & REF_ISSYMREF)))
 		return NULL;
 	if (prefix && (cp = skip_prefix(dst, prefix)))
diff --git a/builtin/commit.c b/builtin/commit.c
index e36e9ad..4d39d25 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1304,7 +1304,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
 	rev.diffopt.break_opt = 0;
 	diff_setup_done(&rev.diffopt);
 
-	head = resolve_ref("HEAD", junk_sha1, 0, NULL);
+	head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
 	printf("[%s%s ",
 		!prefixcmp(head, "refs/heads/") ?
 			head + 11 :
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 30d0dc8..8c479a7 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -563,7 +563,7 @@ static int fsck_head_link(void)
 	if (verbose)
 		fprintf(stderr, "Checking HEAD link\n");
 
-	head_points_at = resolve_ref("HEAD", head_sha1, 0, &flag);
+	head_points_at = resolve_ref_unsafe("HEAD", head_sha1, 0, &flag);
 	if (!head_points_at)
 		return error("Invalid HEAD");
 	if (!strcmp(head_points_at, "HEAD"))
diff --git a/builtin/log.c b/builtin/log.c
index 4395f3e..89d0cc0 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1040,7 +1040,7 @@ static char *find_branch_name(struct rev_info *rev)
 	if (positive < 0)
 		return NULL;
 	strbuf_addf(&buf, "refs/heads/%s", rev->cmdline.rev[positive].name);
-	branch = resolve_ref(buf.buf, branch_sha1, 1, NULL);
+	branch = resolve_ref_unsafe(buf.buf, branch_sha1, 1, NULL);
 	if (!branch ||
 	    prefixcmp(branch, "refs/heads/") ||
 	    hashcmp(rev->cmdline.rev[positive].item->sha1, branch_sha1))
@@ -1268,7 +1268,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
 			rev.pending.objects[0].item->flags |= UNINTERESTING;
 			add_head_to_pending(&rev);
-			ref = resolve_ref("HEAD", sha1, 1, NULL);
+			ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
 			if (ref && !prefixcmp(ref, "refs/heads/"))
 				branch_name = xstrdup(ref + strlen("refs/heads/"));
 			else
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 5cd6fc7..a1a4b09 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -571,7 +571,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
 	int flag;
 
 	strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
-	dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+	dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag);
 	strbuf_release(&buf);
 
 	if (!(flag & REF_ISSYMREF))
diff --git a/builtin/remote.c b/builtin/remote.c
index 407abfb..583eec9 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -573,7 +573,7 @@ static int read_remote_branches(const char *refname,
 	strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
 	if (!prefixcmp(refname, buf.buf)) {
 		item = string_list_append(rename->remote_branches, xstrdup(refname));
-		symref = resolve_ref(refname, orig_sha1, 1, &flag);
+		symref = resolve_ref_unsafe(refname, orig_sha1, 1, &flag);
 		if (flag & REF_ISSYMREF)
 			item->util = xstrdup(symref);
 		else
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index a1f148e..a59e088 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -789,7 +789,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 		}
 	}
 
-	head_p = resolve_ref("HEAD", head_sha1, 1, NULL);
+	head_p = resolve_ref_unsafe("HEAD", head_sha1, 1, NULL);
 	if (head_p) {
 		head_len = strlen(head_p);
 		memcpy(head, head_p, head_len + 1);
diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c
index dea849c..2ef5962 100644
--- a/builtin/symbolic-ref.c
+++ b/builtin/symbolic-ref.c
@@ -12,7 +12,7 @@ static void check_symref(const char *HEAD, int quiet)
 {
 	unsigned char sha1[20];
 	int flag;
-	const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);
+	const char *refs_heads_master = resolve_ref_unsafe(HEAD, sha1, 0, &flag);
 
 	if (!refs_heads_master)
 		die("No such ref: %s", HEAD);
diff --git a/cache.h b/cache.h
index ba5e911..051e7ee 100644
--- a/cache.h
+++ b/cache.h
@@ -865,7 +865,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
  *
  * errno is sometimes set on errors, but not always.
  */
-#define resolve_ref(ref, sha1, reading, flag) resolve_ref_real(ref, sha1, reading, flag, __FILE__, __LINE__)
+#define resolve_ref_unsafe(ref, sha1, reading, flag) resolve_ref_real(ref, sha1, reading, flag, __FILE__, __LINE__)
 extern const char *resolve_ref_real(const char *ref, unsigned char *sha1, int reading, int *flag, const char *file, int line);
 extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
 
diff --git a/refs.c b/refs.c
index cf8dfcc..010bb72 100644
--- a/refs.c
+++ b/refs.c
@@ -361,7 +361,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
 	if (!(flags & REF_ISSYMREF))
 		return 0;
 
-	resolves_to = resolve_ref(refname, junk, 0, NULL);
+	resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
 	if (!resolves_to || strcmp(resolves_to, d->refname))
 		return 0;
 
@@ -616,7 +616,7 @@ const char *resolve_ref_real(const char *ref, unsigned char *sha1,
 
 char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
-	const char *ret = resolve_ref(ref, sha1, reading, flag);
+	const char *ret = resolve_ref_unsafe(ref, sha1, reading, flag);
 	return ret ? xstrdup(ret) : NULL;
 }
 
@@ -629,7 +629,7 @@ struct ref_filter {
 
 int read_ref_full(const char *ref, unsigned char *sha1, int reading, int *flags)
 {
-	if (resolve_ref(ref, sha1, reading, flags))
+	if (resolve_ref_unsafe(ref, sha1, reading, flags))
 		return 0;
 	return -1;
 }
@@ -1126,7 +1126,7 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 
 		this_result = refs_found ? sha1_from_ref : sha1;
 		mksnpath(fullref, sizeof(fullref), *p, len, str);
-		r = resolve_ref(fullref, this_result, 1, &flag);
+		r = resolve_ref_unsafe(fullref, this_result, 1, &flag);
 		if (r) {
 			if (!refs_found++)
 				*ref = xstrdup(r);
@@ -1156,7 +1156,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
 		const char *ref, *it;
 
 		mksnpath(path, sizeof(path), *p, len, str);
-		ref = resolve_ref(path, hash, 1, NULL);
+		ref = resolve_ref_unsafe(path, hash, 1, NULL);
 		if (!ref)
 			continue;
 		if (!stat(git_path("logs/%s", path), &st) &&
@@ -1192,7 +1192,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
 	lock = xcalloc(1, sizeof(struct ref_lock));
 	lock->lock_fd = -1;
 
-	ref = resolve_ref(ref, lock->old_sha1, mustexist, &type);
+	ref = resolve_ref_unsafe(ref, lock->old_sha1, mustexist, &type);
 	if (!ref && errno == EISDIR) {
 		/* we are trying to lock foo but we used to
 		 * have foo/bar which now does not exist;
@@ -1205,7 +1205,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
 			error("there are still refs under '%s'", orig_ref);
 			goto error_return;
 		}
-		ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, &type);
+		ref = resolve_ref_unsafe(orig_ref, lock->old_sha1, mustexist, &type);
 	}
 	if (type_p)
 	    *type_p = type;
@@ -1368,7 +1368,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
 	if (log && S_ISLNK(loginfo.st_mode))
 		return error("reflog for %s is a symlink", oldref);
 
-	symref = resolve_ref(oldref, orig_sha1, 1, &flag);
+	symref = resolve_ref_unsafe(oldref, orig_sha1, 1, &flag);
 	if (flag & REF_ISSYMREF)
 		return error("refname %s is a symbolic ref, renaming it is not supported",
 			oldref);
@@ -1657,7 +1657,7 @@ int write_ref_sha1(struct ref_lock *lock,
 		unsigned char head_sha1[20];
 		int head_flag;
 		const char *head_ref;
-		head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
+		head_ref = resolve_ref_unsafe("HEAD", head_sha1, 1, &head_flag);
 		if (head_ref && (head_flag & REF_ISSYMREF) &&
 		    !strcmp(head_ref, lock->ref_name))
 			log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
@@ -1994,7 +1994,7 @@ int update_ref(const char *action, const char *refname,
 int ref_exists(const char *refname)
 {
 	unsigned char sha1[20];
-	return !!resolve_ref(refname, sha1, 1, NULL);
+	return !!resolve_ref_unsafe(refname, sha1, 1, NULL);
 }
 
 struct ref *find_ref_by_name(const struct ref *list, const char *name)
diff --git a/remote.c b/remote.c
index 6655bb0..73a3809 100644
--- a/remote.c
+++ b/remote.c
@@ -482,7 +482,7 @@ static void read_config(void)
 		return;
 	default_remote_name = xstrdup("origin");
 	current_branch = NULL;
-	head_ref = resolve_ref("HEAD", sha1, 0, &flag);
+	head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
 	if (head_ref && (flag & REF_ISSYMREF) &&
 	    !prefixcmp(head_ref, "refs/heads/")) {
 		current_branch =
@@ -1007,7 +1007,7 @@ static char *guess_ref(const char *name, struct ref *peer)
 	struct strbuf buf = STRBUF_INIT;
 	unsigned char sha1[20];
 
-	const char *r = resolve_ref(peer->name, sha1, 1, NULL);
+	const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
 	if (!r)
 		return NULL;
 
@@ -1058,7 +1058,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
 		unsigned char sha1[20];
 		int flag;
 
-		dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
+		dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
 		if (!dst_value ||
 		    ((flag & REF_ISSYMREF) &&
 		     prefixcmp(dst_value, "refs/heads/")))
diff --git a/test-resolve-ref.c b/test-resolve-ref.c
index b772038..59d04e0 100644
--- a/test-resolve-ref.c
+++ b/test-resolve-ref.c
@@ -11,8 +11,8 @@ int main(int argc, char **argv)
 	 * function returns a shared buffer, so by the time the second
 	 * call is made, ref1 must _not_ be accessed any more.
 	 */
-	ref1 = resolve_ref("HEAD", sha1, 0, NULL);
-	ref2 = resolve_ref("HEAD", sha1, 0, NULL);
+	ref1 = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
+	ref2 = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
 	printf("ref1 = %s\nref2 = %s\n", ref1, ref2);
 	return 0;
 }
diff --git a/transport.c b/transport.c
index 51814b5..e9797c0 100644
--- a/transport.c
+++ b/transport.c
@@ -163,7 +163,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
 		/* Follow symbolic refs (mainly for HEAD). */
 		localname = ref->peer_ref->name;
 		remotename = ref->name;
-		tmp = resolve_ref(localname, sha, 1, &flag);
+		tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
 		if (tmp && flag & REF_ISSYMREF &&
 			!prefixcmp(tmp, "refs/heads/"))
 			localname = tmp;
-- 
1.7.8.36.g69ee2

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

* Re: [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function
  2011-12-12 11:20 ` [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function Nguyễn Thái Ngọc Duy
@ 2011-12-12 18:07   ` Junio C Hamano
  2011-12-13 12:31     ` [PATCH] " Nguyễn Thái Ngọc Duy
  2011-12-13 14:09     ` [PATCH 2/4] " Michael Haggerty
  0 siblings, 2 replies; 9+ messages in thread
From: Junio C Hamano @ 2011-12-12 18:07 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jonathan Nieder

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:

> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>

Thanks.

The patch looks correct but I have a slight maintainability concern and a
suggestion for possible improvement.

> ...
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index b7c6302..a66d3eb 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -696,17 +696,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
>  {
>  	int ret = 0;
>  	struct branch_info old;
> +	char *path;
>  	unsigned char rev[20];
>  	int flag;
>  	memset(&old, 0, sizeof(old));
> -	old.path = resolve_ref("HEAD", rev, 0, &flag);
> -	if (old.path)
> -		old.path = xstrdup(old.path);
> +	old.path = path = resolve_refdup("HEAD", rev, 0, &flag);

This uses "one 'const char *' pointer that is used for reading data from
and an extra 'char *' pointer that is used only for freeing" approach,
which has two advantages and one disadvantage:

 + Obviously, we do not have to cast away constness when freeing.

 + A caller that follows the pattern could move the "for-data" pointer
   without having to worry about affecting "for-freeing" pointer. It could
   even do something like this:

     char *for_freeing;
     const char *for_data;
     for_data = for_freeing = resolve_refdup(...);

     if (prefixcmp(for_data, "refs/heads/"))
     	for_data = skip_prefix(for_data, "refs/heads/");
     ... do other things using for_data pointer ...

     free(for_freeing);

 - If the for-freeing and for-data pointers are named too similarly, the
   code becomes harder to read. It is easy for a person who is new to the
   codebase to start using the for-freeing pointer to manipulate the data
   (mostly harmless) or even advance the pointer by mistake (bad).

A handful of places in the existing codebase use this "two pointers"
approach primarily for the second advantage. The way they avoid the
disadvantage is by naming the other pointer "$something_to_free" (and the
"$something_" part is optional if there is only one such variable in the
context) to make it clear that the variable is not meant to be looked at
in the code and its primary purpose is for cleaning up at the end.

Perhaps renaming this "path" to "to_free" (or "path_to_free" if you want
to hint the "path-ness" to the readers, but see below) would make it less
likely that future tweakers mistakenly look at, modify the string thru, or
worse yet move the pointer itself.

> diff --git a/builtin/merge.c b/builtin/merge.c
> index a1c8534..6437db2 100644
> --- a/builtin/merge.c
> +++ b/builtin/merge.c
> @@ -1096,6 +1096,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>  	struct commit_list *common = NULL;
>  	const char *best_strategy = NULL, *wt_strategy = NULL;
>  	struct commit_list **remotes = &remoteheads;
> +	char *branch_ref;
>  
>  	if (argc == 2 && !strcmp(argv[1], "-h"))
>  		usage_with_options(builtin_merge_usage, builtin_merge_options);
> @@ -1104,12 +1105,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>  	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
>  	 * current branch.
>  	 */
> -	branch = resolve_ref("HEAD", head_sha1, 0, &flag);
> -	if (branch) {
> -		if (!prefixcmp(branch, "refs/heads/"))
> -			branch += 11;
> -		branch = xstrdup(branch);
> -	}
> +	branch = branch_ref = resolve_refdup("HEAD", head_sha1, 0, &flag);

Without s/branch_ref/to_free/ this part is unreadable and unmaintainable,
as it invites the "which variable should I use?" question.

It is more important to clarify that the "branch" variable is what the
code should look at and manipulate by *not* calling this for-freeing
pointer after what it contains (i.e. "branch").

When naming a "for-freeing" pointer variable, the kind of data the area of
memory happens to contain is of secondary importance. Being deliberately
vague about what the area of memory may contain is a good thing, because
it actively discourages the program from looking at the area via the
pointer if the variable is named "to_free" or something that does not
specify what it contains.

Other places in this patch that call the for-freeing variable "ref" share
the same issue but they are less specific than their for-data variable
counterpart, which makes it slightly less risky than this instance.

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

* Re: [PATCH 3/4] Guard memory overwriting in resolve_ref's static buffer
  2011-12-12 11:20 ` [PATCH 3/4] Guard memory overwriting in resolve_ref's static buffer Nguyễn Thái Ngọc Duy
@ 2011-12-13  0:37   ` Junio C Hamano
  0 siblings, 0 replies; 9+ messages in thread
From: Junio C Hamano @ 2011-12-13  0:37 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy; +Cc: git, Jonathan Nieder

Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:

> diff --git a/cache.h b/cache.h
> index 4887a3e..ba5e911 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -865,7 +865,8 @@ extern int read_ref(const char *filename, unsigned char *sha1);
>   *
>   * errno is sometimes set on errors, but not always.
>   */
> -extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
> +#define resolve_ref(ref, sha1, reading, flag) resolve_ref_real(ref, sha1, reading, flag, __FILE__, __LINE__)
> +extern const char *resolve_ref_real(const char *ref, unsigned char *sha1, int reading, int *flag, const char *file, int line);
>  extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
>  
>  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);

Eek.

> diff --git a/wrapper.c b/wrapper.c
> index 85f09df..02b6c81 100644
> --- a/wrapper.c
> +++ b/wrapper.c
> @@ -60,6 +60,33 @@ void *xmallocz(size_t size)
>  	return ret;
>  }
>  
> +void *xmalloc_mmap(size_t size, const char *file, int line)
> +{
> +	struct alloc_header *block;
> +	size += offsetof(struct alloc_header,buf);
> +	block = mmap(NULL, size, PROT_READ | PROT_WRITE,
> +		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
> +	if (block == (struct alloc_header*)-1)
> +		die_errno("unable to mmap %lu bytes anonymously",
> +			  (unsigned long)size);
> +
> +	block->file = file;
> +	block->line = line;
> +	block->size = size;
> +	return block->buf;
> +}
> +

Double eek. A refname is ordinarily way too small than a page and we spend
a full page every time resolve_ref_unsafe() is called. That is acceptable
only for debugging build, but then having to patch the main codepath like
the following, renaming the "real" implementation of a rather important
function is not acceptable in a non-debugging build.

> diff --git a/refs.c b/refs.c
> index 8ffb32f..cf8dfcc 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -497,12 +497,21 @@ static int get_packed_ref(const char *ref, unsigned char *sha1)
>  	return -1;
>  }
>  
> -const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
> +const char *resolve_ref_real(const char *ref, unsigned char *sha1,
> +			     int reading, int *flag, const char *file, int line)
>  {
>  	int depth = MAXDEPTH;
>  	ssize_t len;
>  	char buffer[256];
> -	static char ref_buffer[256];
> +	static char real_ref_buffer[256];
> +	static char *ref_buffer;
> +
> +	if (!ref_buffer && !getenv("GIT_DEBUG_MEMCHECK"))
> +		ref_buffer = real_ref_buffer;
> +	if (ref_buffer != real_ref_buffer) {
> +		xfree_mmap(ref_buffer);
> +		ref_buffer = xmalloc_mmap(256, file, line);
> +	}

I'll drop 3/4 from the series, adjust 4/4, and queue the result as a
three-patch series for now.

Thanks.

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

* [PATCH] Convert resolve_ref+xstrdup to new resolve_refdup function
  2011-12-12 18:07   ` Junio C Hamano
@ 2011-12-13 12:31     ` Nguyễn Thái Ngọc Duy
  2011-12-13 14:09     ` [PATCH 2/4] " Michael Haggerty
  1 sibling, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2011-12-13 12:31 UTC (permalink / raw)
  To: git, Junio C Hamano
  Cc: Jonathan Nieder, Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 2011/12/13 Junio C Hamano <gitster@pobox.com>:
 > A handful of places in the existing codebase use this "two pointers"
 > approach primarily for the second advantage. The way they avoid the
 > disadvantage is by naming the other pointer "$something_to_free" (and the
 > "$something_" part is optional if there is only one such variable in the
 > context) to make it clear that the variable is not meant to be looked at
 > in the code and its primary purpose is for cleaning up at the end.
 >
 > Perhaps renaming this "path" to "to_free" (or "path_to_free" if you want
 > to hint the "path-ness" to the readers, but see below) would make it less
 > likely that future tweakers mistakenly look at, modify the string thru, or
 > worse yet move the pointer itself.

 Thanks. The patch looks better with "_to_free" naming convention. I
 learn something new today.

 builtin/branch.c        |   12 +++++-------
 builtin/checkout.c      |   15 +++++++--------
 builtin/fmt-merge-msg.c |    7 ++++---
 builtin/for-each-ref.c  |    7 ++-----
 builtin/merge.c         |   12 +++++-------
 builtin/notes.c         |    7 ++++---
 builtin/receive-pack.c  |    7 +++----
 builtin/show-branch.c   |    4 +---
 cache.h                 |    1 +
 reflog-walk.c           |   15 ++++++++-------
 refs.c                  |    6 ++++++
 wt-status.c             |    4 +---
 12 files changed, 47 insertions(+), 50 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index e1e486e..59efe2c 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -104,6 +104,7 @@ static int branch_merged(int kind, const char *name,
 	 */
 	struct commit *reference_rev = NULL;
 	const char *reference_name = NULL;
+	char *reference_name_to_free = NULL;
 	int merged;
 
 	if (kind == REF_LOCAL_BRANCH) {
@@ -114,11 +115,9 @@ static int branch_merged(int kind, const char *name,
 		    branch->merge &&
 		    branch->merge[0] &&
 		    branch->merge[0]->dst &&
-		    (reference_name =
-		     resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL) {
-			reference_name = xstrdup(reference_name);
+		    (reference_name = reference_name_to_free =
+		     resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
 			reference_rev = lookup_commit_reference(sha1);
-		}
 	}
 	if (!reference_rev)
 		reference_rev = head_rev;
@@ -143,7 +142,7 @@ static int branch_merged(int kind, const char *name,
 				"         '%s', even though it is merged to HEAD."),
 				name, reference_name);
 	}
-	free((char *)reference_name);
+	free(reference_name_to_free);
 	return merged;
 }
 
@@ -731,10 +730,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	track = git_branch_track;
 
-	head = resolve_ref("HEAD", head_sha1, 0, NULL);
+	head = resolve_refdup("HEAD", head_sha1, 0, NULL);
 	if (!head)
 		die(_("Failed to resolve HEAD as a valid ref."));
-	head = xstrdup(head);
 	if (!strcmp(head, "HEAD")) {
 		detached = 1;
 	} else {
diff --git a/builtin/checkout.c b/builtin/checkout.c
index b7c6302..dde44d7 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -696,17 +696,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 {
 	int ret = 0;
 	struct branch_info old;
+	char *path_to_free;
 	unsigned char rev[20];
 	int flag;
 	memset(&old, 0, sizeof(old));
-	old.path = resolve_ref("HEAD", rev, 0, &flag);
-	if (old.path)
-		old.path = xstrdup(old.path);
+	old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
 	old.commit = lookup_commit_reference_gently(rev, 1);
-	if (!(flag & REF_ISSYMREF)) {
-		free((char *)old.path);
+	if (!(flag & REF_ISSYMREF))
 		old.path = NULL;
-	}
 
 	if (old.path && !prefixcmp(old.path, "refs/heads/"))
 		old.name = old.path + strlen("refs/heads/");
@@ -720,8 +717,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 	}
 
 	ret = merge_working_tree(opts, &old, new);
-	if (ret)
+	if (ret) {
+		free(path_to_free);
 		return ret;
+	}
 
 	if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
 		orphaned_commit_warning(old.commit);
@@ -729,7 +728,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 	update_refs_for_switch(opts, &old, new);
 
 	ret = post_checkout_hook(old.commit, new->commit, 1);
-	free((char *)old.path);
+	free(path_to_free);
 	return ret || opts->writeout_error;
 }
 
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index bdfa0ea..e55e994 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -372,14 +372,15 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 	int i = 0, pos = 0;
 	unsigned char head_sha1[20];
 	const char *current_branch;
+	char *current_branch_to_free;
 
 	/* get current branch */
-	current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+	current_branch = current_branch_to_free =
+		resolve_refdup("HEAD", head_sha1, 1, NULL);
 	if (!current_branch)
 		die("No current branch");
 	if (!prefixcmp(current_branch, "refs/heads/"))
 		current_branch += 11;
-	current_branch = xstrdup(current_branch);
 
 	/* get a line */
 	while (pos < in->len) {
@@ -421,7 +422,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 	}
 
 	strbuf_complete_line(out);
-	free((char *)current_branch);
+	free(current_branch_to_free);
 	return 0;
 }
 
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index d90e5d2..b01d76a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -628,11 +628,8 @@ static void populate_value(struct refinfo *ref)
 
 	if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
 		unsigned char unused1[20];
-		const char *symref;
-		symref = resolve_ref(ref->refname, unused1, 1, NULL);
-		if (symref)
-			ref->symref = xstrdup(symref);
-		else
+		ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+		if (!ref->symref)
 			ref->symref = "";
 	}
 
diff --git a/builtin/merge.c b/builtin/merge.c
index a1c8534..d4079e6 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1096,6 +1096,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	struct commit_list *common = NULL;
 	const char *best_strategy = NULL, *wt_strategy = NULL;
 	struct commit_list **remotes = &remoteheads;
+	char *branch_to_free;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1104,12 +1105,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
 	 * current branch.
 	 */
-	branch = resolve_ref("HEAD", head_sha1, 0, &flag);
-	if (branch) {
-		if (!prefixcmp(branch, "refs/heads/"))
-			branch += 11;
-		branch = xstrdup(branch);
-	}
+	branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
 	if (!branch || is_null_sha1(head_sha1))
 		head_commit = NULL;
 	else
@@ -1520,6 +1518,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		ret = suggest_conflicts(option_renormalize);
 
 done:
-	free((char *)branch);
+	free(branch_to_free);
 	return ret;
 }
diff --git a/builtin/notes.c b/builtin/notes.c
index 10b8bc7..11a24d7 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -804,6 +804,7 @@ static int merge_commit(struct notes_merge_options *o)
 	struct notes_tree *t;
 	struct commit *partial;
 	struct pretty_print_context pretty_ctx;
+	char *local_ref_to_free;
 	int ret;
 
 	/*
@@ -826,10 +827,10 @@ static int merge_commit(struct notes_merge_options *o)
 	t = xcalloc(1, sizeof(struct notes_tree));
 	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
-	o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL);
+	o->local_ref = local_ref_to_free =
+		resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
 	if (!o->local_ref)
 		die("Failed to resolve NOTES_MERGE_REF");
-	o->local_ref = xstrdup(o->local_ref);
 
 	if (notes_merge_commit(o, t, partial, sha1))
 		die("Failed to finalize notes merge");
@@ -846,7 +847,7 @@ static int merge_commit(struct notes_merge_options *o)
 	free_notes(t);
 	strbuf_release(&msg);
 	ret = merge_abort(o);
-	free((char *)o->local_ref);
+	free(local_ref_to_free);
 	return ret;
 }
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b6d957c..fa251ec 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -37,6 +37,7 @@ static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
 static const char *head_name;
+static char *head_name_to_free;
 static int sent_capabilities;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -695,10 +696,8 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
 
 	check_aliased_updates(commands);
 
-	free((char *)head_name);
-	head_name = resolve_ref("HEAD", sha1, 0, NULL);
-	if (head_name)
-		head_name = xstrdup(head_name);
+	free(head_name_to_free);
+	head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
 
 	for (cmd = commands; cmd; cmd = cmd->next)
 		if (!cmd->skip_update)
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 4b480d7..a1f148e 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -726,10 +726,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 
 		if (ac == 0) {
 			static const char *fake_av[2];
-			const char *refname;
 
-			refname = resolve_ref("HEAD", sha1, 1, NULL);
-			fake_av[0] = xstrdup(refname);
+			fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
 			fake_av[1] = NULL;
 			av = fake_av;
 			ac = 1;
diff --git a/cache.h b/cache.h
index 8c98d05..4887a3e 100644
--- a/cache.h
+++ b/cache.h
@@ -866,6 +866,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
  * errno is sometimes set on errors, but not always.
  */
 extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
 
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/reflog-walk.c b/reflog-walk.c
index da71a85..52eddb7 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -50,11 +50,12 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 	for_each_reflog_ent(ref, read_one_reflog, reflogs);
 	if (reflogs->nr == 0) {
 		unsigned char sha1[20];
-		const char *name = resolve_ref(ref, sha1, 1, NULL);
+		const char *name;
+		char *name_to_free;
+		name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
 		if (name) {
-			name = xstrdup(name);
 			for_each_reflog_ent(name, read_one_reflog, reflogs);
-			free((char *)name);
+			free(name_to_free);
 		}
 	}
 	if (reflogs->nr == 0) {
@@ -171,11 +172,11 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
 	else {
 		if (*branch == '\0') {
 			unsigned char sha1[20];
-			const char *head = resolve_ref("HEAD", sha1, 0, NULL);
-			if (!head)
-				die ("No current branch");
 			free(branch);
-			branch = xstrdup(head);
+			branch = resolve_refdup("HEAD", sha1, 0, NULL);
+			if (!branch)
+				die ("No current branch");
+
 		}
 		reflogs = read_complete_reflog(branch);
 		if (!reflogs || reflogs->nr == 0) {
diff --git a/refs.c b/refs.c
index f5cb297..8ffb32f 100644
--- a/refs.c
+++ b/refs.c
@@ -605,6 +605,12 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
 	return ref;
 }
 
+char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+{
+	const char *ret = resolve_ref(ref, sha1, reading, flag);
+	return ret ? xstrdup(ret) : NULL;
+}
+
 /* The argument to filter_refs */
 struct ref_filter {
 	const char *pattern;
diff --git a/wt-status.c b/wt-status.c
index 70fdb76..9ffc535 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -111,7 +111,6 @@ void status_printf_more(struct wt_status *s, const char *color,
 void wt_status_prepare(struct wt_status *s)
 {
 	unsigned char sha1[20];
-	const char *head;
 
 	memset(s, 0, sizeof(*s));
 	memcpy(s->color_palette, default_wt_status_colors,
@@ -119,8 +118,7 @@ void wt_status_prepare(struct wt_status *s)
 	s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 	s->use_color = -1;
 	s->relative_paths = 1;
-	head = resolve_ref("HEAD", sha1, 0, NULL);
-	s->branch = head ? xstrdup(head) : NULL;
+	s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
 	s->reference = "HEAD";
 	s->fp = stdout;
 	s->index_file = get_index_file();
-- 
1.7.8.36.g69ee2

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

* Re: [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function
  2011-12-12 18:07   ` Junio C Hamano
  2011-12-13 12:31     ` [PATCH] " Nguyễn Thái Ngọc Duy
@ 2011-12-13 14:09     ` Michael Haggerty
  2011-12-13 14:17       ` [PATCH] " Nguyễn Thái Ngọc Duy
  1 sibling, 1 reply; 9+ messages in thread
From: Michael Haggerty @ 2011-12-13 14:09 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Nguyễn Thái Ngọc Duy, git, Jonathan Nieder

On 12/12/2011 07:07 PM, Junio C Hamano wrote:
> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> writes:
> 
>> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
> 
> Thanks.
> 
> The patch looks correct but I have a slight maintainability concern and a
> suggestion for possible improvement.
> 
>> ...
>> diff --git a/builtin/checkout.c b/builtin/checkout.c
>> index b7c6302..a66d3eb 100644
>> --- a/builtin/checkout.c
>> +++ b/builtin/checkout.c
>> @@ -696,17 +696,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
>>  {
>>  	int ret = 0;
>>  	struct branch_info old;
>> +	char *path;
>>  	unsigned char rev[20];
>>  	int flag;
>>  	memset(&old, 0, sizeof(old));
>> -	old.path = resolve_ref("HEAD", rev, 0, &flag);
>> -	if (old.path)
>> -		old.path = xstrdup(old.path);
>> +	old.path = path = resolve_refdup("HEAD", rev, 0, &flag);
> 
> This uses "one 'const char *' pointer that is used for reading data from
> and an extra 'char *' pointer that is used only for freeing" approach,
> which has two advantages and one disadvantage:
> [...]
> When naming a "for-freeing" pointer variable, the kind of data the area of
> memory happens to contain is of secondary importance. Being deliberately
> vague about what the area of memory may contain is a good thing, because
> it actively discourages the program from looking at the area via the
> pointer if the variable is named "to_free" or something that does not
> specify what it contains.

The to_free variable could even be declared void* to make it even less
(ab)usable.

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/

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

* [PATCH] Convert resolve_ref+xstrdup to new resolve_refdup function
  2011-12-13 14:09     ` [PATCH 2/4] " Michael Haggerty
@ 2011-12-13 14:17       ` Nguyễn Thái Ngọc Duy
  0 siblings, 0 replies; 9+ messages in thread
From: Nguyễn Thái Ngọc Duy @ 2011-12-13 14:17 UTC (permalink / raw)
  To: git, Michael Haggerty
  Cc: Junio C Hamano, Jonathan Nieder,
	Nguyễn Thái Ngọc Duy


Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 2011/12/13 Michael Haggerty <mhagger@alum.mit.edu>:
 >> This uses "one 'const char *' pointer that is used for reading data from
 >> and an extra 'char *' pointer that is used only for freeing" approach,
 >> which has two advantages and one disadvantage:
 >> [...]
 >> When naming a "for-freeing" pointer variable, the kind of data the area of
 >> memory happens to contain is of secondary importance. Being deliberately
 >> vague about what the area of memory may contain is a good thing, because
 >> it actively discourages the program from looking at the area via the
 >> pointer if the variable is named "to_free" or something that does not
 >> specify what it contains.
 >
 > The to_free variable could even be declared void* to make it even less
 > (ab)usable.

 Good thinking. Here's the version with "char *foo_to_free" converted
 to "void *foo_to_free".

 builtin/branch.c        |   12 +++++-------
 builtin/checkout.c      |   15 +++++++--------
 builtin/fmt-merge-msg.c |    7 ++++---
 builtin/for-each-ref.c  |    7 ++-----
 builtin/merge.c         |   12 +++++-------
 builtin/notes.c         |    7 ++++---
 builtin/receive-pack.c  |    7 +++----
 builtin/show-branch.c   |    4 +---
 cache.h                 |    1 +
 reflog-walk.c           |   15 ++++++++-------
 refs.c                  |    6 ++++++
 wt-status.c             |    4 +---
 12 files changed, 47 insertions(+), 50 deletions(-)

diff --git a/builtin/branch.c b/builtin/branch.c
index e1e486e..c459f0b 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -104,6 +104,7 @@ static int branch_merged(int kind, const char *name,
 	 */
 	struct commit *reference_rev = NULL;
 	const char *reference_name = NULL;
+	void *reference_name_to_free = NULL;
 	int merged;
 
 	if (kind == REF_LOCAL_BRANCH) {
@@ -114,11 +115,9 @@ static int branch_merged(int kind, const char *name,
 		    branch->merge &&
 		    branch->merge[0] &&
 		    branch->merge[0]->dst &&
-		    (reference_name =
-		     resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL) {
-			reference_name = xstrdup(reference_name);
+		    (reference_name = reference_name_to_free =
+		     resolve_refdup(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
 			reference_rev = lookup_commit_reference(sha1);
-		}
 	}
 	if (!reference_rev)
 		reference_rev = head_rev;
@@ -143,7 +142,7 @@ static int branch_merged(int kind, const char *name,
 				"         '%s', even though it is merged to HEAD."),
 				name, reference_name);
 	}
-	free((char *)reference_name);
+	free(reference_name_to_free);
 	return merged;
 }
 
@@ -731,10 +730,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
 	track = git_branch_track;
 
-	head = resolve_ref("HEAD", head_sha1, 0, NULL);
+	head = resolve_refdup("HEAD", head_sha1, 0, NULL);
 	if (!head)
 		die(_("Failed to resolve HEAD as a valid ref."));
-	head = xstrdup(head);
 	if (!strcmp(head, "HEAD")) {
 		detached = 1;
 	} else {
diff --git a/builtin/checkout.c b/builtin/checkout.c
index b7c6302..00740bd 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -696,17 +696,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 {
 	int ret = 0;
 	struct branch_info old;
+	void *path_to_free;
 	unsigned char rev[20];
 	int flag;
 	memset(&old, 0, sizeof(old));
-	old.path = resolve_ref("HEAD", rev, 0, &flag);
-	if (old.path)
-		old.path = xstrdup(old.path);
+	old.path = path_to_free = resolve_refdup("HEAD", rev, 0, &flag);
 	old.commit = lookup_commit_reference_gently(rev, 1);
-	if (!(flag & REF_ISSYMREF)) {
-		free((char *)old.path);
+	if (!(flag & REF_ISSYMREF))
 		old.path = NULL;
-	}
 
 	if (old.path && !prefixcmp(old.path, "refs/heads/"))
 		old.name = old.path + strlen("refs/heads/");
@@ -720,8 +717,10 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 	}
 
 	ret = merge_working_tree(opts, &old, new);
-	if (ret)
+	if (ret) {
+		free(path_to_free);
 		return ret;
+	}
 
 	if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
 		orphaned_commit_warning(old.commit);
@@ -729,7 +728,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
 	update_refs_for_switch(opts, &old, new);
 
 	ret = post_checkout_hook(old.commit, new->commit, 1);
-	free((char *)old.path);
+	free(path_to_free);
 	return ret || opts->writeout_error;
 }
 
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index bdfa0ea..c81a7fe 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -372,14 +372,15 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 	int i = 0, pos = 0;
 	unsigned char head_sha1[20];
 	const char *current_branch;
+	void *current_branch_to_free;
 
 	/* get current branch */
-	current_branch = resolve_ref("HEAD", head_sha1, 1, NULL);
+	current_branch = current_branch_to_free =
+		resolve_refdup("HEAD", head_sha1, 1, NULL);
 	if (!current_branch)
 		die("No current branch");
 	if (!prefixcmp(current_branch, "refs/heads/"))
 		current_branch += 11;
-	current_branch = xstrdup(current_branch);
 
 	/* get a line */
 	while (pos < in->len) {
@@ -421,7 +422,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 	}
 
 	strbuf_complete_line(out);
-	free((char *)current_branch);
+	free(current_branch_to_free);
 	return 0;
 }
 
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index d90e5d2..b01d76a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -628,11 +628,8 @@ static void populate_value(struct refinfo *ref)
 
 	if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
 		unsigned char unused1[20];
-		const char *symref;
-		symref = resolve_ref(ref->refname, unused1, 1, NULL);
-		if (symref)
-			ref->symref = xstrdup(symref);
-		else
+		ref->symref = resolve_refdup(ref->refname, unused1, 1, NULL);
+		if (!ref->symref)
 			ref->symref = "";
 	}
 
diff --git a/builtin/merge.c b/builtin/merge.c
index a1c8534..a3cd5e8 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1096,6 +1096,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	struct commit_list *common = NULL;
 	const char *best_strategy = NULL, *wt_strategy = NULL;
 	struct commit_list **remotes = &remoteheads;
+	void *branch_to_free;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_merge_usage, builtin_merge_options);
@@ -1104,12 +1105,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	 * Check if we are _not_ on a detached HEAD, i.e. if there is a
 	 * current branch.
 	 */
-	branch = resolve_ref("HEAD", head_sha1, 0, &flag);
-	if (branch) {
-		if (!prefixcmp(branch, "refs/heads/"))
-			branch += 11;
-		branch = xstrdup(branch);
-	}
+	branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
+	if (branch && !prefixcmp(branch, "refs/heads/"))
+		branch += 11;
 	if (!branch || is_null_sha1(head_sha1))
 		head_commit = NULL;
 	else
@@ -1520,6 +1518,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		ret = suggest_conflicts(option_renormalize);
 
 done:
-	free((char *)branch);
+	free(branch_to_free);
 	return ret;
 }
diff --git a/builtin/notes.c b/builtin/notes.c
index 10b8bc7..667e20a 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -804,6 +804,7 @@ static int merge_commit(struct notes_merge_options *o)
 	struct notes_tree *t;
 	struct commit *partial;
 	struct pretty_print_context pretty_ctx;
+	void *local_ref_to_free;
 	int ret;
 
 	/*
@@ -826,10 +827,10 @@ static int merge_commit(struct notes_merge_options *o)
 	t = xcalloc(1, sizeof(struct notes_tree));
 	init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
-	o->local_ref = resolve_ref("NOTES_MERGE_REF", sha1, 0, NULL);
+	o->local_ref = local_ref_to_free =
+		resolve_refdup("NOTES_MERGE_REF", sha1, 0, NULL);
 	if (!o->local_ref)
 		die("Failed to resolve NOTES_MERGE_REF");
-	o->local_ref = xstrdup(o->local_ref);
 
 	if (notes_merge_commit(o, t, partial, sha1))
 		die("Failed to finalize notes merge");
@@ -846,7 +847,7 @@ static int merge_commit(struct notes_merge_options *o)
 	free_notes(t);
 	strbuf_release(&msg);
 	ret = merge_abort(o);
-	free((char *)o->local_ref);
+	free(local_ref_to_free);
 	return ret;
 }
 
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index b6d957c..62afac3 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -37,6 +37,7 @@ static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
 static const char *head_name;
+static void *head_name_to_free;
 static int sent_capabilities;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
@@ -695,10 +696,8 @@ static void execute_commands(struct command *commands, const char *unpacker_erro
 
 	check_aliased_updates(commands);
 
-	free((char *)head_name);
-	head_name = resolve_ref("HEAD", sha1, 0, NULL);
-	if (head_name)
-		head_name = xstrdup(head_name);
+	free(head_name_to_free);
+	head_name = head_name_to_free = resolve_refdup("HEAD", sha1, 0, NULL);
 
 	for (cmd = commands; cmd; cmd = cmd->next)
 		if (!cmd->skip_update)
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 4b480d7..a1f148e 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -726,10 +726,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 
 		if (ac == 0) {
 			static const char *fake_av[2];
-			const char *refname;
 
-			refname = resolve_ref("HEAD", sha1, 1, NULL);
-			fake_av[0] = xstrdup(refname);
+			fake_av[0] = resolve_refdup("HEAD", sha1, 1, NULL);
 			fake_av[1] = NULL;
 			av = fake_av;
 			ac = 1;
diff --git a/cache.h b/cache.h
index 8c98d05..4887a3e 100644
--- a/cache.h
+++ b/cache.h
@@ -866,6 +866,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
  * errno is sometimes set on errors, but not always.
  */
 extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
 
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
diff --git a/reflog-walk.c b/reflog-walk.c
index da71a85..64c677f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -50,11 +50,12 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
 	for_each_reflog_ent(ref, read_one_reflog, reflogs);
 	if (reflogs->nr == 0) {
 		unsigned char sha1[20];
-		const char *name = resolve_ref(ref, sha1, 1, NULL);
+		const char *name;
+		void *name_to_free;
+		name = name_to_free = resolve_refdup(ref, sha1, 1, NULL);
 		if (name) {
-			name = xstrdup(name);
 			for_each_reflog_ent(name, read_one_reflog, reflogs);
-			free((char *)name);
+			free(name_to_free);
 		}
 	}
 	if (reflogs->nr == 0) {
@@ -171,11 +172,11 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
 	else {
 		if (*branch == '\0') {
 			unsigned char sha1[20];
-			const char *head = resolve_ref("HEAD", sha1, 0, NULL);
-			if (!head)
-				die ("No current branch");
 			free(branch);
-			branch = xstrdup(head);
+			branch = resolve_refdup("HEAD", sha1, 0, NULL);
+			if (!branch)
+				die ("No current branch");
+
 		}
 		reflogs = read_complete_reflog(branch);
 		if (!reflogs || reflogs->nr == 0) {
diff --git a/refs.c b/refs.c
index f5cb297..8ffb32f 100644
--- a/refs.c
+++ b/refs.c
@@ -605,6 +605,12 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
 	return ref;
 }
 
+char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
+{
+	const char *ret = resolve_ref(ref, sha1, reading, flag);
+	return ret ? xstrdup(ret) : NULL;
+}
+
 /* The argument to filter_refs */
 struct ref_filter {
 	const char *pattern;
diff --git a/wt-status.c b/wt-status.c
index 70fdb76..9ffc535 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -111,7 +111,6 @@ void status_printf_more(struct wt_status *s, const char *color,
 void wt_status_prepare(struct wt_status *s)
 {
 	unsigned char sha1[20];
-	const char *head;
 
 	memset(s, 0, sizeof(*s));
 	memcpy(s->color_palette, default_wt_status_colors,
@@ -119,8 +118,7 @@ void wt_status_prepare(struct wt_status *s)
 	s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
 	s->use_color = -1;
 	s->relative_paths = 1;
-	head = resolve_ref("HEAD", sha1, 0, NULL);
-	s->branch = head ? xstrdup(head) : NULL;
+	s->branch = resolve_refdup("HEAD", sha1, 0, NULL);
 	s->reference = "HEAD";
 	s->fp = stdout;
 	s->index_file = get_index_file();
-- 
1.7.8.36.g69ee2

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

end of thread, other threads:[~2011-12-13 14:19 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-12 11:20 [PATCH 1/4] revert: convert resolve_ref() to read_ref_full() Nguyễn Thái Ngọc Duy
2011-12-12 11:20 ` [PATCH 2/4] Convert resolve_ref+xstrdup to new resolve_refdup function Nguyễn Thái Ngọc Duy
2011-12-12 18:07   ` Junio C Hamano
2011-12-13 12:31     ` [PATCH] " Nguyễn Thái Ngọc Duy
2011-12-13 14:09     ` [PATCH 2/4] " Michael Haggerty
2011-12-13 14:17       ` [PATCH] " Nguyễn Thái Ngọc Duy
2011-12-12 11:20 ` [PATCH 3/4] Guard memory overwriting in resolve_ref's static buffer Nguyễn Thái Ngọc Duy
2011-12-13  0:37   ` Junio C Hamano
2011-12-12 11:20 ` [PATCH 4/4] Rename resolve_ref() to resolve_ref_unsafe() Nguyễn Thái Ngọc Duy

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