Commit 2d35ac256782f4e6547a066dfbf3361290a76cf0

Con Kolivas 2012-11-24T10:47:20

Track all dynamically allocated memory within the work struct by copying work structs in a common place, creating freshly allocated heap ram for all arrays within the copied struct. Clear all work structs from the same place to ensure memory does not leak from arrays within the struct. Convert the gbt coinbase and stratum strings within the work struct to heap ram. This will allow arbitrary lengths without an upper limit for the strings, preventing the overflows that happen with GBT.

diff --git a/cgminer.c b/cgminer.c
index 042661d..f6ac12d 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -239,7 +239,7 @@ int swork_id;
 struct stratum_share {
 	UT_hash_handle hh;
 	bool block;
-	struct work work;
+	struct work *work;
 	int id;
 };
 
@@ -1469,9 +1469,7 @@ static void calc_diff(struct work *work, int known);
 static void gen_gbt_work(struct pool *pool, struct work *work)
 {
 	unsigned char *merkleroot;
-	char *cbhex;
 
-	memset(work->job_id, 0, 64);
 	mutex_lock(&pool->gbt_lock);
 	__build_gbt_coinbase(pool);
 	merkleroot = __gbt_merkleroot(pool);
@@ -1483,15 +1481,13 @@ static void gen_gbt_work(struct pool *pool, struct work *work)
 
 	memcpy(work->target, pool->gbt_target, 32);
 
-	cbhex = bin2hex(pool->gbt_coinbase, pool->coinbase_len);
-	sprintf(work->gbt_coinbase, "%s", cbhex);
-	free(cbhex);
+	work->gbt_coinbase = bin2hex(pool->gbt_coinbase, pool->coinbase_len);
 
 	/* For encoding the block data on submission */
 	work->gbt_txns = pool->gbt_txns + 1;
 
 	if (pool->gbt_workid)
-		sprintf(work->job_id, "%s", pool->gbt_workid);
+		work->job_id = strdup(pool->gbt_workid);
 	mutex_unlock(&pool->gbt_lock);
 
 	memcpy(work->data + 4 + 32, merkleroot, 32);
@@ -2307,7 +2303,7 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit)
 
 	/* build JSON-RPC request */
 	if (work->gbt) {
-		char gbt_block[1024], *varint, *header;
+		char gbt_block[4096], *varint, *header;
 		unsigned char data[80];
 
 		flip80(data, work->data);
@@ -2334,7 +2330,7 @@ static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit)
 		free(varint);
 		strcat(gbt_block, work->gbt_coinbase);
 
-		if (strlen(work->job_id))
+		if (work->job_id)
 			sprintf(s, "{\"id\": 0, \"method\": \"submitblock\", \"params\": [\"%s\", {\"workid\": \"%s\"}]}", gbt_block, work->job_id);
 		else
 			sprintf(s, "{\"id\": 0, \"method\": \"submitblock\", \"params\": [\"%s\", {}]}", gbt_block);
@@ -2634,8 +2630,25 @@ static struct work *make_work(void)
 	return work;
 }
 
-static void free_work(struct work *work)
+/* This is the central place all work that is about to be retired should be
+ * cleaned to remove any dynamically allocated arrays within the struct */
+void clean_work(struct work *work)
+{
+	free(work->job_id);
+	free(work->nonce2);
+	free(work->ntime);
+	free(work->gbt_coinbase);
+	work->job_id = NULL;
+	work->nonce2 = NULL;
+	work->ntime = NULL;
+	work->gbt_coinbase = NULL;
+}
+
+/* All dynamically allocated work structs should be freed here to not leak any
+ * ram from arrays allocated within the work struct */
+void free_work(struct work *work)
 {
+	clean_work(work);
 	free(work);
 }
 
@@ -2948,11 +2961,37 @@ static void roll_work(struct work *work)
 	work->id = total_work++;
 }
 
+/* Duplicates any dynamically allocated arrays within the work struct to
+ * prevent a copied work struct from freeing ram belonging to another struct */
+void __copy_work(struct work *work, struct work *base_work)
+{
+	clean_work(work);
+	memcpy(work, base_work, sizeof(struct work));
+	if (work->job_id)
+		work->job_id = strdup(base_work->job_id);
+	if (work->nonce2)
+		work->nonce2 = strdup(base_work->nonce2);
+	if (work->ntime)
+		work->ntime = strdup(base_work->ntime);
+	if (work->gbt_coinbase)
+		work->gbt_coinbase = strdup(base_work->gbt_coinbase);
+}
+
+/* Generates a copy of an existing work struct, creating fresh heap allocations
+ * for all dynamically allocated arrays within the struct */
+struct work *copy_work(struct work *base_work)
+{
+	struct work *work = make_work();
+
+ 	__copy_work(work, base_work);
+
+	return work;
+}
+
 static struct work *make_clone(struct work *work)
 {
-	struct work *work_clone = make_work();
+	struct work *work_clone = copy_work(work);
 
-	memcpy(work_clone, work, sizeof(struct work));
 	work_clone->clone = true;
 	gettimeofday((struct timeval *)&(work_clone->tv_cloned), NULL);
 	work_clone->longpoll = false;
@@ -3016,7 +3055,7 @@ static void gen_stratum_work(struct pool *pool, struct work *work);
 static void *get_work_thread(void *userdata)
 {
 	struct workio_cmd *wc = (struct workio_cmd *)userdata;
-	struct work *ret_work= NULL;
+	struct work *ret_work = NULL;
 	struct curl_ent *ce = NULL;
 	struct pool *pool;
 
@@ -3232,7 +3271,7 @@ static void *submit_work_thread(void *userdata)
 		char *noncehex;
 		char s[1024];
 
-		memcpy(&sshare->work, work, sizeof(struct work));
+		sshare->work = copy_work(work);
 		mutex_lock(&sshare_lock);
 		/* Give the stratum share a unique id */
 		sshare->id = swork_id++;
@@ -4538,7 +4577,7 @@ out_unlock:
 static void stratum_share_result(json_t *val, json_t *res_val, json_t *err_val,
 				 struct stratum_share *sshare)
 {
-	struct work *work = &sshare->work;
+	struct work *work = sshare->work;
 	uint64_t sharediff = share_diff(work);
 	char hashshow[65];
 	uint32_t *hash32;
@@ -4602,6 +4641,7 @@ static bool parse_stratum_response(struct pool *pool, char *s)
 		goto out;
 	}
 	stratum_share_result(val, res_val, err_val, sshare);
+	free_work(sshare->work);
 	free(sshare);
 
 	ret = true;
@@ -4621,8 +4661,9 @@ static void clear_stratum_shares(struct pool *pool)
 
 	mutex_lock(&sshare_lock);
 	HASH_ITER(hh, stratum_shares, sshare, tmpshare) {
-		if (sshare->work.pool == pool) {
+		if (sshare->work->pool == pool) {
 			HASH_DEL(stratum_shares, sshare);
+			free_work(sshare->work);
 			free(sshare);
 			cleared++;
 		}
@@ -5129,17 +5170,13 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 {
 	unsigned char *coinbase, merkle_root[32], merkle_sha[64], *merkle_hash;
 	int len, cb1_len, n1_len, cb2_len, i;
-	char header[260], *nonce2;
 	uint32_t *data32, *swap32;
-
-	memset(work->job_id, 0, 64);
-	memset(work->nonce2, 0, 64);
-	memset(work->ntime, 0, 16);
+	char header[260];
 
 	mutex_lock(&pool->pool_lock);
 
 	/* Generate coinbase */
-	nonce2 = bin2hex((const unsigned char *)&pool->nonce2, pool->n2size);
+	work->nonce2 = bin2hex((const unsigned char *)&pool->nonce2, pool->n2size);
 	pool->nonce2++;
 	cb1_len = strlen(pool->swork.coinbase1) / 2;
 	n1_len = strlen(pool->nonce1) / 2;
@@ -5148,7 +5185,7 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	coinbase = alloca(len + 1);
 	hex2bin(coinbase, pool->swork.coinbase1, cb1_len);
 	hex2bin(coinbase + cb1_len, pool->nonce1, n1_len);
-	hex2bin(coinbase + cb1_len + n1_len, nonce2, pool->n2size);
+	hex2bin(coinbase + cb1_len + n1_len, work->nonce2, pool->n2size);
 	hex2bin(coinbase + cb1_len + n1_len + pool->n2size, pool->swork.coinbase2, cb2_len);
 
 	/* Generate merkle root */
@@ -5181,10 +5218,8 @@ static void gen_stratum_work(struct pool *pool, struct work *work)
 	work->sdiff = pool->swork.diff;
 
 	/* Copy parameters required for share submission */
-	sprintf(work->job_id, "%s", pool->swork.job_id);
-	sprintf(work->nonce2, "%s", nonce2);
-	sprintf(work->ntime, "%s", pool->swork.ntime);
-	free(nonce2);
+	work->job_id = strdup(pool->swork.job_id);
+	work->ntime = strdup(pool->swork.ntime);
 
 	mutex_unlock(&pool->pool_lock);
 
@@ -5225,6 +5260,8 @@ static void get_work(struct work *work, struct thr_info *thr, const int thr_id)
 	 * should not be restarted */
 	thread_reportout(thr);
 
+	clean_work(work);
+
 	if (opt_benchmark) {
 		get_benchmark_work(work);
 		goto out;
@@ -5298,7 +5335,7 @@ retry:
 			pool_resus(pool);
 	}
 
-	memcpy(work, work_heap, sizeof(struct work));
+	__copy_work(work, work_heap);
 	free_work(work_heap);
 
 out:
@@ -5307,7 +5344,7 @@ out:
 	work->mined = true;
 }
 
-bool submit_work_sync(struct thr_info *thr, const struct work *work_in, struct timeval *tv_work_found)
+bool submit_work_sync(struct thr_info *thr, struct work *work_in, struct timeval *tv_work_found)
 {
 	struct workio_cmd *wc;
 
@@ -5318,10 +5355,9 @@ bool submit_work_sync(struct thr_info *thr, const struct work *work_in, struct t
 		return false;
 	}
 
-	wc->work = make_work();
+	wc->work = copy_work(work_in);
 	wc->cmd = WC_SUBMIT_WORK;
 	wc->thr = thr;
-	memcpy(wc->work, work_in, sizeof(*work_in));
 	if (tv_work_found)
 		memcpy(&(wc->work->tv_work_found), tv_work_found, sizeof(struct timeval));
 
diff --git a/driver-modminer.c b/driver-modminer.c
index d169cfd..3fbedb4 100644
--- a/driver-modminer.c
+++ b/driver-modminer.c
@@ -645,7 +645,7 @@ static int64_t modminer_scanhash(struct thr_info *thr, struct work *work, int64_
 	if (startwork) {
 		if (!modminer_start_work(thr))
 			return -1;
-		memcpy(&state->running_work, work, sizeof(state->running_work));
+		__copy_work(&state->running_work, work);
 	}
 
 	// This is intentionally early
diff --git a/driver-opencl.c b/driver-opencl.c
index 3ce14a5..8c801e1 100644
--- a/driver-opencl.c
+++ b/driver-opencl.c
@@ -1464,10 +1464,8 @@ static void opencl_free_work(struct thr_info *thr, struct work *work)
 
 	clFinish(clState->commandQueue);
 
-	if (thrdata->res[FOUND]) {
-		thrdata->last_work = &thrdata->_last_work;
-		memcpy(thrdata->last_work, work, sizeof(*thrdata->last_work));
-	}
+	if (thrdata->res[FOUND])
+		thrdata->last_work = copy_work(&thrdata->_last_work);
 }
 
 static bool opencl_prepare_work(struct thr_info __maybe_unused *thr, struct work *work)
@@ -1535,6 +1533,7 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 		if (unlikely(thrdata->last_work)) {
 			applog(LOG_DEBUG, "GPU %d found something in last work?", gpu->device_id);
 			postcalc_hash_async(thr, thrdata->last_work, thrdata->res);
+			free_work(thrdata->last_work);
 			thrdata->last_work = NULL;
 		} else {
 			applog(LOG_DEBUG, "GPU %d found something?", gpu->device_id);
diff --git a/miner.h b/miner.h
index f098e23..0a97e4a 100644
--- a/miner.h
+++ b/miner.h
@@ -962,15 +962,13 @@ struct work {
 	bool		queued;
 
 	bool		stratum;
-	/* These are arbitrary lengths as it is too hard to keep track of
-	 * dynamically allocated ram in work structs */
-	char 		job_id[64];
-	char		nonce2[64];
-	char		ntime[16];
+	char 		*job_id;
+	char		*nonce2;
+	char		*ntime;
 	double		sdiff;
 
 	bool		gbt;
-	char		gbt_coinbase[512];
+	char		*gbt_coinbase;
 	int		gbt_txns;
 
 	unsigned int	work_block;
@@ -1031,6 +1029,10 @@ extern void tq_thaw(struct thread_q *tq);
 extern bool successful_connect;
 extern void adl(void);
 extern void app_restart(void);
+extern void clean_work(struct work *work);
+extern void free_work(struct work *work);
+extern void __copy_work(struct work *work, struct work *base_work);
+extern struct work *copy_work(struct work *base_work);
 
 enum api_data_type {
 	API_ESCAPE,