Vulnerability: The parse_notify function in util.c in sgminer before 4.2.2 and cgminer 3.3.0 through 4.0.1 allows man-in-the-middle attackers to cause a denial of service (application exit) via a crafted (1) bbversion, (2) prev_hash, (3) nbit, or (4) ntime parameter in a mining.notify action stratum message.
Vulnerability ID: CVE-2014-4503
CWE: CWE-20
Severity score: 2.9
Project: https://github.com/sgminer-dev/sgminer
Published: 2014-07-23
File: util.c
Commit message: stratum: parse_notify(): Don't die on malformed bbversion/prev_hash/nbit/ntime.

Might have introduced a memory leak, don't have time to check. :(

Should the other hex2bin()'s be checked?

Thanks to Mick Ayzenberg <mick.dejavusecurity.com> for finding this.
Commit author: Noel Maersk

Vulnerable version (before fix):
 static bool parse_notify(struct pool *pool, json_t *val) 		pool->swork.nbit, 		"00000000", /* nonce */ 		workpadding); 	if (unlikely(!hex2bin(pool->header_bin, header, 128))) 		quit(1, "Failed to convert header to header_bin in parse_notify");  	cb1 = (unsigned char *)calloc(cb1_len, 1); 	if (unlikely(!cb1)) 		quithere(1, "Failed to calloc cb1 in parse_notify"); 	hex2bin(cb1, coinbase1, cb1_len); 	cb2 = (unsigned char *)calloc(cb2_len, 1); 	if (unlikely(!cb2)) 		quithere(1, "Failed to calloc cb2 in parse_notify"); 	hex2bin(cb2, coinbase2, cb2_len); 	free(pool->coinbase); 	align_len(&alloc_len); 	pool->coinbase = (unsigned char *)calloc(alloc_len, 1); 	if (unlikely(!pool->coinbase)) 		quit(1, "Failed to calloc pool coinbase in parse_notify"); 	memcpy(pool->coinbase, cb1, cb1_len); 	memcpy(pool->coinbase + cb1_len, pool->nonce1bin, pool->n1_len); 	memcpy(pool->coinbase + cb1_len + pool->n1_len + pool->n2size, cb2, cb2_len); 	cg_wunlock(&pool->data_lock); 

Fixed version (after fix):
 static bool parse_notify(struct pool *pool, json_t *val) 		pool->swork.nbit, 		"00000000", /* nonce */ 		workpadding); 	if (unlikely(!hex2bin(pool->header_bin, header, 128))) { 		applog(LOG_WARNING, "%s: Failed to convert header to header_bin, got %s", __func__, header); 		pool_failed(pool); 		// TODO: memory leaks? goto out, clean up there? 		return false; 	}  	cb1 = (unsigned char *)calloc(cb1_len, 1); 	if (unlikely(!cb1)) 		quithere(1, "Failed to calloc cb1 in parse_notify"); 	hex2bin(cb1, coinbase1, cb1_len);  	cb2 = (unsigned char *)calloc(cb2_len, 1); 	if (unlikely(!cb2)) 		quithere(1, "Failed to calloc cb2 in parse_notify"); 	hex2bin(cb2, coinbase2, cb2_len);  	free(pool->coinbase); 	align_len(&alloc_len); 	pool->coinbase = (unsigned char *)calloc(alloc_len, 1); 	if (unlikely(!pool->coinbase)) 		quit(1, "Failed to calloc pool coinbase in parse_notify"); 	memcpy(pool->coinbase, cb1, cb1_len); 	memcpy(pool->coinbase + cb1_len, pool->nonce1bin, pool->n1_len); 	// NOTE: gap for nonce2, filled at work generation time 	memcpy(pool->coinbase + cb1_len + pool->n1_len + pool->n2size, cb2, cb2_len); 	cg_wunlock(&pool->data_lock); 

Patch:
patch: @@ -1600,24 +1600,31 @@ static bool parse_notify(struct pool *pool, json_t *val)
 		pool->swork.nbit,
 		"00000000", /* nonce */
 		workpadding);
-	if (unlikely(!hex2bin(pool->header_bin, header, 128)))
-		quit(1, "Failed to convert header to header_bin in parse_notify");
+	if (unlikely(!hex2bin(pool->header_bin, header, 128))) {
+		applog(LOG_WARNING, "%s: Failed to convert header to header_bin, got %s", __func__, header);
+		pool_failed(pool);
+		// TODO: memory leaks? goto out, clean up there?
+		return false;
+	}
 
 	cb1 = (unsigned char *)calloc(cb1_len, 1);
 	if (unlikely(!cb1))
 		quithere(1, "Failed to calloc cb1 in parse_notify");
 	hex2bin(cb1, coinbase1, cb1_len);
+
 	cb2 = (unsigned char *)calloc(cb2_len, 1);
 	if (unlikely(!cb2))
 		quithere(1, "Failed to calloc cb2 in parse_notify");
 	hex2bin(cb2, coinbase2, cb2_len);
+
 	free(pool->coinbase);
 	align_len(&alloc_len);
 	pool->coinbase = (unsigned char *)calloc(alloc_len, 1);
 	if (unlikely(!pool->coinbase))
 		quit(1, "Failed to calloc pool coinbase in parse_notify");
 	memcpy(pool->coinbase, cb1, cb1_len);
 	memcpy(pool->coinbase + cb1_len, pool->nonce1bin, pool->n1_len);
+	// NOTE: gap for nonce2, filled at work generation time
 	memcpy(pool->coinbase + cb1_len + pool->n1_len + pool->n2size, cb2, cb2_len);
 	cg_wunlock(&pool->data_lock);
path: util.c