CVE-2014-4501: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: Multiple stack-based buffer overflows in sgminer before 4.2.2, cgminer before 4.3.5, and BFGMiner before 3.3.0 allow remote pool servers to have unspecified impact via a long URL in a client.reconnect stratum message to the (1) extract_sockaddr or (2) parse_reconnect functions in util.c.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
Published: 2014-07-23T14:55Z
Repository: https://github.com/ckolivas/cgminer
Language: C
Files changed: util.c
Commit date: 2014-06-10 11:36:22 +1000
Commit message: Do some random sanity checking for stratum message parsing

Diff: diff --git a/util.c b/util.c
index 756b77d5..6401bb82 100644
--- a/util.c
+++ b/util.c
@@ -1,7 +1,7 @@
 /*
- * Copyright 2011-2013 Con Kolivas
+ * Copyright 2011-2014 Con Kolivas
  * Copyright 2010 Jeff Garzik
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
  * Software Foundation; either version 3 of the License, or (at your option)
@@ -718,10 +718,38 @@ bool hex2bin(unsigned char *p, const char *hexstr, size_t len)
 	if (likely(len == 0 && *hexstr == 0))
 		ret = true;
 	return ret;
 }
 
+static bool _valid_hex(char *s, const char *file, const char *func, const int line)
+{
+	bool ret = false;
+	int i, len;
+
+	if (unlikely(!s)) {
+		applog(LOG_ERR, "Null string passed to valid_hex from"IN_FMT_FFL, file, func, line);
+		return ret;
+	}
+	len = strlen(s);
+	if (unlikely(!len)) {
+		applog(LOG_ERR, "Zero length string passed to valid_hex from"IN_FMT_FFL, file, func, line);
+		return ret;
+	}
+	for (i = 0; i < len; i++) {
+		unsigned char idx = s[i];
+
+		if (unlikely(hex2bin_tbl[idx] < 0)) {
+			applog(LOG_ERR, "Invalid char %x passed to valid_hex from"IN_FMT_FFL, idx, file, func, line);
+			return ret;
+		}
+	}
+	ret = true;
+	return ret;
+}
+
+#define valid_hex(s) _valid_hex(s, __FILE__, __func__, __LINE__)
+
 static const int b58tobin_tbl[] = {
 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 	-1,  0,  1,  2,  3,  4,  5,  6,  7,  8, -1, -1, -1, -1, -1, -1,
@@ -1373,11 +1401,11 @@ bool extract_sockaddr(char *url, char **sockaddr_url, char **sockaddr_port)
 		url_len = strlen(url_begin);
 
 	if (url_len < 1)
 		return false;
 
-	sprintf(url_address, "%.*s", url_len, url_begin);
+	snprintf(url_address, 254, "%.*s", url_len, url_begin);
 
 	if (port_len) {
 		char *slash;
 
 		snprintf(port, 6, "%.*s", port_len, port_start);
@@ -1683,18 +1711,17 @@ static bool parse_notify(struct pool *pool, json_t *val)
 	bbversion = __json_array_string(val, 5);
 	nbit = __json_array_string(val, 6);
 	ntime = __json_array_string(val, 7);
 	clean = json_is_true(json_array_get(val, 8));
 
-	if (!job_id || !prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime) {
+	if (!valid_hex(job_id) || !valid_hex(prev_hash) || !valid_hex(coinbase1) ||
+	    !valid_hex(coinbase2) || !valid_hex(bbversion) || !valid_hex(nbit) ||
+	    !valid_hex(ntime)) {
 		/* Annoying but we must not leak memory */
-		if (job_id)
-			free(job_id);
-		if (coinbase1)
-			free(coinbase1);
-		if (coinbase2)
-			free(coinbase2);
+		free(job_id);
+		free(coinbase1);
+		free(coinbase2);
 		goto out;
 	}
 
 	cg_wlock(&pool->data_lock);
 	free(pool->swork.job_id);
@@ -1878,11 +1905,11 @@ static bool parse_reconnect(struct pool *pool, json_t *val)
 
 	port = (char *)json_string_value(json_array_get(val, 1));
 	if (!port)
 		port = pool->stratum_port;
 
-	sprintf(address, "%s:%s", url, port);
+	snprintf(address, 254, "%s:%s", url, port);
 
 	if (!extract_sockaddr(address, &sockaddr_url, &stratum_port))
 		return false;
 
 	applog(LOG_WARNING, "Stratum reconnect requested from pool %d to %s", pool->pool_no, address);
@@ -2549,18 +2576,18 @@ resend:
 
 	sessionid = get_sessionid(res_val);
 	if (!sessionid)
 		applog(LOG_DEBUG, "Failed to get sessionid in initiate_stratum");
 	nonce1 = json_array_string(res_val, 1);
-	if (!nonce1) {
-		applog(LOG_INFO, "Failed to get nonce1 in initiate_stratum");
+	if (!valid_hex(nonce1)) {
+		applog(LOG_INFO, "Failed to get valid nonce1 in initiate_stratum");
 		free(sessionid);
 		goto out;
 	}
 	n2size = json_integer_value(json_array_get(res_val, 2));
-	if (!n2size) {
-		applog(LOG_INFO, "Failed to get n2size in initiate_stratum");
+	if (n2size < 2 || n2size > 16) {
+		applog(LOG_INFO, "Failed to get valid n2size in initiate_stratum");
 		free(sessionid);
 		free(nonce1);
 		goto out;
 	}
 

Vulnerable code:  * Copyright 2011-2013 Con Kolivas
	sprintf(url_address, "%.*s", url_len, url_begin);
	if (!job_id || !prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime) {
		if (job_id)
			free(job_id);
		if (coinbase1)
			free(coinbase1);
		if (coinbase2)
			free(coinbase2);
	sprintf(address, "%s:%s", url, port);
	if (!nonce1) {
		applog(LOG_INFO, "Failed to get nonce1 in initiate_stratum");
	if (!n2size) {
		applog(LOG_INFO, "Failed to get n2size in initiate_stratum");
Fixed code:  * Copyright 2011-2014 Con Kolivas
static bool _valid_hex(char *s, const char *file, const char *func, const int line)
{
	bool ret = false;
	int i, len;

	if (unlikely(!s)) {
		applog(LOG_ERR, "Null string passed to valid_hex from"IN_FMT_FFL, file, func, line);
		return ret;
	}
	len = strlen(s);
	if (unlikely(!len)) {
		applog(LOG_ERR, "Zero length string passed to valid_hex from"IN_FMT_FFL, file, func, line);
		return ret;
	}
	for (i = 0; i < len; i++) {
		unsigned char idx = s[i];

		if (unlikely(hex2bin_tbl[idx] < 0)) {
			applog(LOG_ERR, "Invalid char %x passed to valid_hex from"IN_FMT_FFL, idx, file, func, line);
			return ret;
		}
	}
	ret = true;
	return ret;
}

#define valid_hex(s) _valid_hex(s, __FILE__, __func__, __LINE__)

	snprintf(url_address, 254, "%.*s", url_len, url_begin);
	if (!valid_hex(job_id) || !valid_hex(prev_hash) || !valid_hex(coinbase1) ||
	    !valid_hex(coinbase2) || !valid_hex(bbversion) || !valid_hex(nbit) ||
	    !valid_hex(ntime)) {
		free(job_id);
		free(coinbase1);
		free(coinbase2);
	snprintf(address, 254, "%s:%s", url, port);
	if (!valid_hex(nonce1)) {
		applog(LOG_INFO, "Failed to get valid nonce1 in initiate_stratum");
	if (n2size < 2 || n2size > 16) {
		applog(LOG_INFO, "Failed to get valid n2size in initiate_stratum");