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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
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,