From dd3f58c0bdf1e9f7bb86d09e6f7faf155d6b5310 Mon Sep 17 00:00:00 2001
From: Andre Noll <maan@tuebingen.mpg.de>
Date: Tue, 3 Sep 2019 11:09:14 +0200
Subject: [PATCH] create_rsync_argv(): Allocate correctly sized arg array.

In the calculation of the length of the argv array we did not take
into account that --source-dir may be given multiple times. This can
result in an invalid write at the end of the allocated space.
---
 dss.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/dss.c b/dss.c
index f8bbb99..b6a3d3e 100644
--- a/dss.c
+++ b/dss.c
@@ -1386,7 +1386,7 @@ out:
 static void create_rsync_argv(char ***argv, int64_t *num)
 {
 	char *logname;
-	int i = 0, j, N = OPT_GIVEN(DSS, RSYNC_OPTION);
+	int i = 0, j, N;
 	struct snapshot_list sl;
 	static bool seeded;
 
@@ -1395,7 +1395,13 @@ static void create_rsync_argv(char ***argv, int64_t *num)
 	name_of_reference_snapshot = name_of_newest_complete_snapshot(&sl);
 	free_snapshot_list(&sl);
 
-	*argv = dss_malloc((15 + N) * sizeof(char *));
+	/*
+	 * We specify up to 6 arguments, one argument per given rsync option
+	 * and one argument per given source dir. We also need space for the
+	 * terminating NULL pointer.
+	 */
+	N = OPT_GIVEN(DSS, RSYNC_OPTION) + OPT_GIVEN(DSS, SOURCE_DIR);
+	*argv = dss_malloc((7 + N) * sizeof(char *));
 	(*argv)[i++] = dss_strdup("rsync");
 	(*argv)[i++] = dss_strdup("-a");
 	(*argv)[i++] = dss_strdup("--delete");
@@ -1407,7 +1413,7 @@ static void create_rsync_argv(char ***argv, int64_t *num)
 		DSS_NOTICE_LOG(("adding --checksum to rsync options\n"));
 		(*argv)[i++] = dss_strdup("--checksum");
 	}
-	for (j = 0; j < N; j++)
+	for (j = 0; j < OPT_GIVEN(DSS, RSYNC_OPTION); j++)
 		(*argv)[i++] = dss_strdup(lls_string_val(j,
 			OPT_RESULT(DSS, RSYNC_OPTION)));
 	if (name_of_reference_snapshot) {
@@ -1417,9 +1423,8 @@ static void create_rsync_argv(char ***argv, int64_t *num)
 	} else
 		DSS_INFO_LOG(("no suitable reference snapshot found\n"));
 	logname = dss_logname();
-	N = OPT_GIVEN(DSS, SOURCE_DIR);
 	if (use_rsync_locally(logname)) {
-		for (j = 0; j < N; j++)
+		for (j = 0; j < OPT_GIVEN(DSS, SOURCE_DIR); j++)
 			(*argv)[i++] = dss_strdup(lls_string_val(j,
 				OPT_RESULT(DSS, SOURCE_DIR)));
 	} else {
@@ -1436,7 +1441,7 @@ static void create_rsync_argv(char ***argv, int64_t *num)
 		 * directory on the destination match the source. To preserve
 		 * the old behaviour, we thus have to special-case N=1.
 		 */
-		for (j = 0; j < N; j++) {
+		for (j = 0; j < OPT_GIVEN(DSS, SOURCE_DIR); j++) {
 			(*argv)[i++] = make_message("%s@%s:%s%s",
 				OPT_GIVEN(DSS, REMOTE_USER)?
 					OPT_STRING_VAL(DSS, REMOTE_USER) : logname,
-- 
2.39.5