[Rsync-patches] [PATCH] Properly free filters loaded during a parent_dirscan to avoid a crash when they define another per-dir merge file.

Matt McCutchen <matt at mattmccutchen.net>
Sun Aug 17 08:36:58 PDT 2008


---
This patch, which is available in branch wip/perdir-merge of my rsync
repository ( http://mattmccutchen.net/rsync/rsync.git/ ), should fix the
crash but could use some additional testing.

 exclude.c |   39 +++++++++++++++++++++++++++++----------
 rsync.h   |    1 +
 2 files changed, 30 insertions(+), 10 deletions(-)

diff --git a/exclude.c b/exclude.c
index 6eb83cb..cf93629 100644
--- a/exclude.c
+++ b/exclude.c
@@ -40,9 +40,9 @@ extern char curr_dir[MAXPATHLEN];
 extern unsigned int curr_dir_len;
 extern unsigned int module_dirlen;
 
-struct filter_list_struct filter_list = { 0, 0, "" };
-struct filter_list_struct cvs_filter_list = { 0, 0, " [global CVS]" };
-struct filter_list_struct daemon_filter_list = { 0, 0, " [daemon]" };
+struct filter_list_struct filter_list = { 0, 0, 0, "" };
+struct filter_list_struct cvs_filter_list = { 0, 0, 0, " [global CVS]" };
+struct filter_list_struct daemon_filter_list = { 0, 0, 0, " [daemon]" };
 
 /* Need room enough for ":MODS " prefix plus some room to grow. */
 #define MAX_RULE_PREFIX (16)
@@ -102,9 +102,15 @@ static int mergelist_size = 0;
  * values (so we can pop back to them later) and set the tail to NULL.
  */
 
+static void free_filters(struct filter_struct *head);
+
 static void free_filter(struct filter_struct *ex)
 {
 	if (ex->match_flags & MATCHFLG_PERDIR_MERGE) {
+		/* Free the parent_dirscan list to clean up any per-dir
+		 * mergelists it defined.  Otherwise pop_local_filters may crash
+		 * trying to restore nonexistent state for those mergelists. */
+		free_filters(ex->u.mergelist->parent_dirscan_head);
 		free(ex->u.mergelist->debug_type);
 		free(ex->u.mergelist);
 		mergelist_cnt--;
@@ -113,6 +119,15 @@ static void free_filter(struct filter_struct *ex)
 	free(ex);
 }
 
+static void free_filters(struct filter_struct *head)
+{
+	while (head) {
+		struct filter_struct *next = head->next;
+		free_filter(head);
+		head = next;
+	}
+}
+
 /* Build a filter structure given a filter pattern.  The value in "pat"
  * is not null-terminated. */
 static void add_rule(struct filter_list_struct *listp, const char *pat,
@@ -237,7 +252,7 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
 
 		if (!(lp = new_array(struct filter_list_struct, 1)))
 			out_of_memory("add_rule");
-		lp->head = lp->tail = NULL;
+		lp->head = lp->tail = lp->parent_dirscan_head = NULL;
 		if (asprintf(&lp->debug_type, " [per-dir %s]", cp) < 0)
 			out_of_memory("add_rule");
 		ret->u.mergelist = lp;
@@ -269,14 +284,10 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
 static void clear_filter_list(struct filter_list_struct *listp)
 {
 	if (listp->tail) {
-		struct filter_struct *ent, *next;
 		/* Truncate any inherited items from the local list. */
 		listp->tail->next = NULL;
 		/* Now free everything that is left. */
-		for (ent = listp->head; ent; ent = next) {
-			next = ent->next;
-			free_filter(ent);
-		}
+		free_filters(listp->head);
 	}
 
 	listp->head = listp->tail = NULL;
@@ -414,12 +425,20 @@ static BOOL setup_merge_file(struct filter_struct *ex,
 		dirbuf_len = y - dirbuf;
 		strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
 		parse_filter_file(lp, buf, ex->match_flags, XFLG_ANCHORED2ABS);
-		if (ex->match_flags & MATCHFLG_NO_INHERIT)
+		if (ex->match_flags & MATCHFLG_NO_INHERIT) {
+			/* Free the undesired rules to clean up any per-dir
+			 * mergelists they defined.  Otherwise pop_local_filters
+			 * may crash trying to restore nonexistent state for
+			 * those mergelists. */
+			free_filters(lp->head);
 			lp->head = NULL;
+		}
 		lp->tail = NULL;
 		strlcpy(y, save, MAXPATHLEN);
 		while ((*x++ = *y++) != '/') {}
 	}
+	/* Save current head for freeing when the mergelist becomes inactive. */
+	lp->parent_dirscan_head = lp->head;
 	parent_dirscan = False;
 	free(pat);
 	return 1;
diff --git a/rsync.h b/rsync.h
index 320d340..6083c81 100644
--- a/rsync.h
+++ b/rsync.h
@@ -813,6 +813,7 @@ struct filter_struct {
 struct filter_list_struct {
 	struct filter_struct *head;
 	struct filter_struct *tail;
+	struct filter_struct *parent_dirscan_head;
 	char *debug_type;
 };
 
-- 
1.6.0.rc3.13.gdfe0a
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: This is a digitally signed message part
URL: <http://mattmccutchen.net/mailman/archives/rsync-patches/attachments/20080817/6b5eaf35/attachment.pgp>


More information about the rsync-patches mailing list