From b628b8937beef3c27bf9d5409ae19a1edaa01345 Mon Sep 17 00:00:00 2001 From: JesterHodl Date: Tue, 10 Dec 2024 23:07:21 +0100 Subject: [PATCH 1/8] Improve use of sprintf and snprintf --- src/datum_blocktemplates.c | 12 +++++----- src/datum_logger.c | 9 ++++---- src/datum_stratum.c | 45 +++++++++++++++++++------------------- src/datum_submitblock.c | 9 +++----- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/datum_blocktemplates.c b/src/datum_blocktemplates.c index e8478140..3ec7232c 100644 --- a/src/datum_blocktemplates.c +++ b/src/datum_blocktemplates.c @@ -331,7 +331,7 @@ T_DATUM_TEMPLATE_DATA *datum_gbt_parser(json_t *gbt) { void *datum_gateway_fallback_notifier(void *args) { CURL *tcurl = NULL; - char userpass[512]; + char userpass[1024]; char req[512]; char p1[72]; p1[0] = 0; @@ -345,10 +345,10 @@ void *datum_gateway_fallback_notifier(void *args) { } DLOG_DEBUG("Fallback notifier thread ready."); - sprintf(userpass, "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); + snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); while(1) { - sprintf(req, "{\"jsonrpc\":\"1.0\",\"id\":\"%"PRIu64"\",\"method\":\"getbestblockhash\",\"params\":[]}", current_time_millis()); + snprintf(req, sizeof(req), "{\"jsonrpc\":\"1.0\",\"id\":\"%"PRIu64"\",\"method\":\"getbestblockhash\",\"params\":[]}", current_time_millis()); gbbh = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, req); if (gbbh) { res_val = json_object_get(gbbh, "result"); @@ -383,7 +383,7 @@ void *datum_gateway_template_thread(void *args) { uint64_t i = 0; char gbt_req[1024]; int j; - char userpass[512]; + char userpass[1024]; T_DATUM_TEMPLATE_DATA *t; bool was_notified = false; int wnc = 0; @@ -411,13 +411,13 @@ void *datum_gateway_template_thread(void *args) { char p1[72]; p1[0] = 0; - sprintf(userpass, "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); + snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); while(1) { i++; // fetch latest template - sprintf(gbt_req, "{\"method\":\"getblocktemplate\",\"params\":[{\"rules\":[\"segwit\"]}],\"id\":%"PRIu64"}",(uint64_t)((uint64_t)time(NULL)<<(uint64_t)8)|(uint64_t)(i&255)); + snprintf(gbt_req, sizeof(gbt_req), "{\"method\":\"getblocktemplate\",\"params\":[{\"rules\":[\"segwit\"]}],\"id\":%"PRIu64"}",(uint64_t)((uint64_t)time(NULL)<<(uint64_t)8)|(uint64_t)(i&255)); gbt = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, gbt_req); if (!gbt) { diff --git a/src/datum_logger.c b/src/datum_logger.c index 9b713b70..8f825ba0 100644 --- a/src/datum_logger.c +++ b/src/datum_logger.c @@ -201,7 +201,7 @@ int datum_logger_queue_msg(const char *func, int level, const char *format, ...) msg->calling_function[47] = 0; msg->msg = &msg_buffer[buffer_id][msg_buf_idx[buffer_id]]; va_start(args, format); - i = vsnprintf(msg->msg, 1023, format, args); + i = vsnprintf(msg->msg, sizeof(msg->msg), format, args); msg->msg[i] = 0; va_end(args); @@ -348,9 +348,9 @@ void * datum_logger_thread(void *ptr) { tm_info = localtime_r(&seconds, &tm_info_storage); strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", tm_info); if (log_calling_function) { - j = snprintf(log_line, 1199, "%s.%03d [%44s] %s: %s\n", time_buffer, millis, msg->calling_function, level_text[msg->level], msg->msg); + j = snprintf(log_line, sizeof(log_line), "%s.%03d [%44s] %s: %s\n", time_buffer, millis, msg->calling_function, level_text[msg->level], msg->msg); } else { - j = snprintf(log_line, 1199, "%s.%03d %s: %s\n", time_buffer, millis, level_text[msg->level], msg->msg); + j = snprintf(log_line, sizeof(log_line), "%s.%03d %s: %s\n", time_buffer, millis, level_text[msg->level], msg->msg); } log_line[1199] = 0; @@ -390,8 +390,7 @@ void * datum_logger_thread(void *ptr) { tm_info = localtime_r(&log_file_opened, &tm_info_storage); strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d", tm_info); - snprintf(log_line, 1199, "%s.%s", log_file, time_buffer); - log_line[1199] = 0; + snprintf(log_line, sizeof(log_line), "%s.%s", log_file, time_buffer); fclose(log_handle); diff --git a/src/datum_stratum.c b/src/datum_stratum.c index 60de2821..4676d5ab 100644 --- a/src/datum_stratum.c +++ b/src/datum_stratum.c @@ -690,7 +690,7 @@ void datum_stratum_v1_socket_thread_loop(T_DATUM_THREAD_DATA *my) { void send_error_to_client(T_DATUM_CLIENT_DATA *c, uint64_t id, char *e) { // "e" must be valid JSON string char s[1024]; - snprintf(s, 1023, "{\"error\":%s,\"id\":%"PRIu64",\"result\":null}\n", e, id); + snprintf(s, sizeof(s), "{\"error\":%s,\"id\":%"PRIu64",\"result\":null}\n", e, id); datum_socket_send_string_to_client(c, s); } @@ -1283,7 +1283,7 @@ int client_mining_submit(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_obj } char s[256]; - snprintf(s, 255, "{\"error\":null,\"id\":%"PRIu64",\"result\":true}\n", id); + snprintf(s, sizeof(s), "{\"error\":null,\"id\":%"PRIu64",\"result\":true}\n", id); datum_socket_send_string_to_client(c, s); // update connection totals @@ -1370,15 +1370,15 @@ int client_mining_configure(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_ } } - i = snprintf(sa, 1023, "{\"error\":null,\"id\":%"PRIu64",\"result\":{", id); + i = snprintf(sa, sizeof(sa), "{\"error\":null,\"id\":%"PRIu64",\"result\":{", id); if (new_vroll) { - i+= snprintf(&sa[i], 1023-i, "\"version-rolling\":true,\"version-rolling.mask\":\"%08x\"", m->extension_version_rolling_mask); + i+= snprintf(&sa[i], sizeof(sa)-i, "\"version-rolling\":true,\"version-rolling.mask\":\"%08x\"", m->extension_version_rolling_mask); } if (new_mdiff) { // we don't currently support miner specified minimum difficulty. - i+= snprintf(&sa[i], 1023-i, ",\"minimum-difficulty\":false"); + i+= snprintf(&sa[i], sizeof(sa)-i, ",\"minimum-difficulty\":false"); } - i+= snprintf(&sa[i], 1023-i, "}}\n"); + i+= snprintf(&sa[i], sizeof(sa)-i, "}}\n"); datum_socket_send_string_to_client(c, sa); @@ -1406,10 +1406,10 @@ int client_mining_authorize(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_ } } - strncpy(m->last_auth_username, username_s, 191); - m->last_auth_username[191] = 0; + strncpy(m->last_auth_username, username_s, sizeof(m->last_auth_username) - 1); + m->last_auth_username[sizeof(m->last_auth_username)-1] = 0; - snprintf(s, 255, "{\"error\":null,\"id\":%"PRIu64",\"result\":true}\n", id); + snprintf(s, sizeof(s), "{\"error\":null,\"id\":%"PRIu64",\"result\":true}\n", id); datum_socket_send_string_to_client(c, s); m->authorized = true; @@ -1523,12 +1523,12 @@ int send_mining_notify(T_DATUM_CLIENT_DATA *c, bool clean, bool quickdiff, bool // new block work always is just a blank coinbase, for now if (quickdiff) { - snprintf(s, 511, "\"Q%s%2.2x\",\"%s\",\"", j->job_id, cbselect, j->prevhash); + snprintf(s, sizeof(s), "\"Q%s%2.2x\",\"%s\",\"", j->job_id, cbselect, j->prevhash); } else { if (!new_block) { - snprintf(s, 511, "\"%s%2.2x\",\"%s\",\"", j->job_id, cbselect, j->prevhash); + snprintf(s, sizeof(s), "\"%s%2.2x\",\"%s\",\"", j->job_id, cbselect, j->prevhash); } else { - snprintf(s, 511, "\"N%s%2.2x\",\"%s\",\"", j->job_id, 255, j->prevhash); // empty coinbase for new block + snprintf(s, sizeof(s), "\"N%s%2.2x\",\"%s\",\"", j->job_id, 255, j->prevhash); // empty coinbase for new block cb = &j->subsidy_only_coinbase; } } @@ -1571,7 +1571,7 @@ int send_mining_notify(T_DATUM_CLIENT_DATA *c, bool clean, bool quickdiff, bool // send empty merkle leafs datum_socket_send_string_to_client(c, "[]"); } - snprintf(s, 511, ",\"%s\",\"%s\",\"%s\",", j->version, j->nbits, j->ntime); + snprintf(s, sizeof(s), ",\"%s\",\"%s\",\"%s\",", j->version, j->nbits, j->ntime); datum_socket_send_string_to_client(c, s); // bunch of reasons we may need to discard old work @@ -1594,7 +1594,7 @@ int send_mining_set_difficulty(T_DATUM_CLIENT_DATA *c) { m->current_diff = datum_config.stratum_v1_vardiff_min; } - snprintf(s, 255, "{\"id\":null,\"method\":\"mining.set_difficulty\",\"params\":[%"PRIu64"]}\n", (uint64_t)m->current_diff); + snprintf(s, sizeof(s), "{\"id\":null,\"method\":\"mining.set_difficulty\",\"params\":[%"PRIu64"]}\n", (uint64_t)m->current_diff); datum_socket_send_string_to_client(c, s); m->last_sent_diff = m->current_diff; @@ -1712,7 +1712,7 @@ int client_mining_subscribe(T_DATUM_CLIENT_DATA *c, uint64_t id, json_t *params_ m->sid_inv = ((sid>>24)&0xff) | (((sid>>16)&0xff)<<8) | (((sid>>8)&0xff)<<16) | ((sid&0xff)<<24); // tell them about all of this - snprintf(s, 1023, "{\"error\":null,\"id\":%"PRIu64",\"result\":[[[\"mining.notify\",\"%8.8x1\"],[\"mining.set_difficulty\",\"%8.8x2\"]],\"%8.8x\",8]}\n", id, sid, sid, sid); + snprintf(s, sizeof(s), "{\"error\":null,\"id\":%"PRIu64",\"result\":[[[\"mining.notify\",\"%8.8x1\"],[\"mining.set_difficulty\",\"%8.8x2\"]],\"%8.8x\",8]}\n", id, sid, sid, sid); datum_socket_send_string_to_client(c, s); // send them their current difficulty before sending a job @@ -1967,14 +1967,14 @@ void update_stratum_job(T_DATUM_TEMPLATE_DATA *block_template, bool new_block, i } s->prevhash[64] = 0; - snprintf(s->version, 9, "%8.8x", block_template->version); + snprintf(s->version, sizeof(s->version), "%8.8x", block_template->version); s->version_uint = block_template->version; - strncpy(s->nbits, block_template->bits, 9); + strncpy(s->nbits, block_template->bits, sizeof(s->nbits) - 1); // TODO: Should we use local time, and just verify is valid for the block? // Perhaps as an option. // The template's time is 100% safe, so we'll use that for now. - snprintf(s->ntime, 9, "%8.8llx", (unsigned long long)block_template->curtime); + snprintf(s->ntime, sizeof(s->ntime), "%8.8llx", (unsigned long long)block_template->curtime); // Set the coinbase value of this job based on the template s->coinbase_value = block_template->coinbasevalue; @@ -2054,7 +2054,7 @@ void update_stratum_job(T_DATUM_TEMPLATE_DATA *block_template, bool new_block, i } s->global_index = global_latest_stratum_job_index; - snprintf(s->job_id, 23, "%8.8x%2.2x%4.4x", (uint32_t)time(NULL), (uint8_t)stratum_job_next, (uint16_t)s->global_index ^ STRATUM_JOB_INDEX_XOR); + snprintf(s->job_id, sizeof(s->job_id), "%8.8x%2.2x%4.4x", (uint32_t)time(NULL), (uint8_t)stratum_job_next, (uint16_t)s->global_index ^ STRATUM_JOB_INDEX_XOR); global_cur_stratum_jobs[global_latest_stratum_job_index] = s; pthread_rwlock_unlock(&stratum_global_job_ptr_lock); @@ -2071,7 +2071,7 @@ int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t size_t i; json_t *r; CURL *tcurl; - char userpass[512]; + char userpass[1024]; int ret = 0; bool free_submitblock_req = false; char *s = NULL; @@ -2140,8 +2140,7 @@ int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t if (datum_config.mining_save_submitblocks_dir[0] != 0) { // save the block submission to a file named by the block's hash FILE *f; - snprintf(userpass, 511, "%s/datum_submitblock_%s.json", datum_config.mining_save_submitblocks_dir, block_hash_hex); - userpass[511] = 0; + snprintf(userpass, sizeof(userpass), "%s/datum_submitblock_%s.json", datum_config.mining_save_submitblocks_dir, block_hash_hex); f = fopen(userpass, "w"); if (!f) { DLOG_ERROR("Could not open %s for writing submitblock record to disk: %s!", userpass, strerror(errno)); @@ -2162,7 +2161,7 @@ int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t return 0; } - sprintf(userpass, "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); + snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); // make the call! r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, submitblock_req); diff --git a/src/datum_submitblock.c b/src/datum_submitblock.c index adc8acca..022e2202 100644 --- a/src/datum_submitblock.c +++ b/src/datum_submitblock.c @@ -55,10 +55,8 @@ void preciousblock(CURL *curl, char *blockhash) { char userpass[1024]; // TODO: Move these types of things to the conf file - snprintf(userpass, 1023, "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); - userpass[1023] = 0; - - sprintf(rpc_data,"{\"method\":\"preciousblock\",\"params\":[\"%s\"],\"id\":1}",blockhash); + snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); + snprintf(rpc_data, sizeof(rpc_data), "{\"method\":\"preciousblock\",\"params\":[\"%s\"],\"id\":1}", blockhash); json = json_rpc_call(curl, datum_config.bitcoind_rpcurl, userpass, rpc_data); if (!json) return; @@ -72,8 +70,7 @@ void datum_submitblock_doit(CURL *tcurl, char *url, const char *submitblock_req, char *s = NULL; // TODO: Move these types of things to the conf file if (!url) { - snprintf(userpass, 1023, "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); - userpass[1023] = 0; + snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, submitblock_req); } else { From 96d3b1f9eecfd3431b67fac32db173caf9847fd1 Mon Sep 17 00:00:00 2001 From: JesterHodl Date: Sat, 14 Dec 2024 22:54:52 +0100 Subject: [PATCH 2/8] handle truncation in vsnprintf and reduce size of char time_buffer[] to 20 20 is sufficient --- src/datum_logger.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/datum_logger.c b/src/datum_logger.c index 8f825ba0..62af06f3 100644 --- a/src/datum_logger.c +++ b/src/datum_logger.c @@ -118,7 +118,7 @@ int datum_logger_queue_msg(const char *func, int level, const char *format, ...) va_list args; struct timeval tv; struct tm tm_info; - char time_buffer[40]; + char time_buffer[20]; if ((level < log_level_console) && (level < log_level_file)) { return 0; @@ -201,8 +201,13 @@ int datum_logger_queue_msg(const char *func, int level, const char *format, ...) msg->calling_function[47] = 0; msg->msg = &msg_buffer[buffer_id][msg_buf_idx[buffer_id]]; va_start(args, format); - i = vsnprintf(msg->msg, sizeof(msg->msg), format, args); - msg->msg[i] = 0; + i = vsnprintf(msg->msg, 1023, format, args); + + // clamp i to actual written value in order not to waste buffer space + if (i >= 1023) { + i = 1022; + } + va_end(args); if (((msg_buf_idx[buffer_id]+i+2) > msg_buf_maxsz) || (dlog_queue_next[buffer_id] >= dlog_queue_max_entries)) { @@ -251,7 +256,7 @@ void * datum_logger_thread(void *ptr) { struct tm tm_info_storage; struct tm *tm_info; DLOG_MSG *msg; - char time_buffer[40]; + char time_buffer[20]; char log_line[1200]; FILE *log_handle = NULL; time_t next_log_rotate = get_midnight_timestamp(); From 90e6b020a6c7344912d1efa5150015206445ead0 Mon Sep 17 00:00:00 2001 From: JesterHodl Date: Sun, 15 Dec 2024 19:46:09 +0100 Subject: [PATCH 3/8] - in order to stop populating userpass over and over again, populate once and reuse - in order to eliminate discrepancies between char array sizes, use sizeof() in max_string_length of datum_config_options - introduce a warning in case a config option is truncated - where userpass was reused for a filepath, create a separate variable - reduce user and pass to 128 bytes - exit during config upon parsing error, makes more sense to me --- src/datum_blocktemplates.c | 6 ------ src/datum_conf.c | 40 +++++++++++++++++++++++++------------- src/datum_conf.h | 5 +++-- src/datum_stratum.c | 25 +++++++++++++----------- src/datum_submitblock.c | 5 ----- 5 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/datum_blocktemplates.c b/src/datum_blocktemplates.c index 3ec7232c..a262a81f 100644 --- a/src/datum_blocktemplates.c +++ b/src/datum_blocktemplates.c @@ -331,7 +331,6 @@ T_DATUM_TEMPLATE_DATA *datum_gbt_parser(json_t *gbt) { void *datum_gateway_fallback_notifier(void *args) { CURL *tcurl = NULL; - char userpass[1024]; char req[512]; char p1[72]; p1[0] = 0; @@ -345,8 +344,6 @@ void *datum_gateway_fallback_notifier(void *args) { } DLOG_DEBUG("Fallback notifier thread ready."); - snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); - while(1) { snprintf(req, sizeof(req), "{\"jsonrpc\":\"1.0\",\"id\":\"%"PRIu64"\",\"method\":\"getbestblockhash\",\"params\":[]}", current_time_millis()); gbbh = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, req); @@ -383,7 +380,6 @@ void *datum_gateway_template_thread(void *args) { uint64_t i = 0; char gbt_req[1024]; int j; - char userpass[1024]; T_DATUM_TEMPLATE_DATA *t; bool was_notified = false; int wnc = 0; @@ -411,8 +407,6 @@ void *datum_gateway_template_thread(void *args) { char p1[72]; p1[0] = 0; - snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); - while(1) { i++; diff --git a/src/datum_conf.c b/src/datum_conf.c index 103e5d76..99caaa99 100644 --- a/src/datum_conf.c +++ b/src/datum_conf.c @@ -47,17 +47,19 @@ #include "datum_sockets.h" global_config_t datum_config; +// since we null-terminate the user and password, we'll have enough for the semicolon and NUL +char userpass[sizeof(datum_config.bitcoind_rpcuser) + sizeof(datum_config.bitcoind_rpcpassword)]; const char *datum_conf_var_type_text[] = { "N/A", "boolean", "integer", "string", "string_array" }; const T_DATUM_CONFIG_ITEM datum_config_options[] = { // Bitcoind configs { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcuser", .description = "RPC username for communication with local bitcoind.", - .required = true, .ptr = datum_config.bitcoind_rpcuser, .max_string_len = 128 }, + .required = true, .ptr = datum_config.bitcoind_rpcuser, .max_string_len = sizeof(datum_config.bitcoind_rpcuser) }, { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcpassword", .description = "RPC password for communication with local bitcoind.", - .required = true, .ptr = datum_config.bitcoind_rpcpassword, .max_string_len = 128 }, + .required = true, .ptr = datum_config.bitcoind_rpcpassword, .max_string_len = sizeof(datum_config.bitcoind_rpcpassword) }, { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcurl", .description = "RPC URL for communication with local bitcoind. (GBT Template Source)", - .required = true, .ptr = datum_config.bitcoind_rpcurl, .max_string_len = 128 }, + .required = true, .ptr = datum_config.bitcoind_rpcurl, .max_string_len = sizeof(datum_config.bitcoind_rpcurl) }, { .var_type = DATUM_CONF_INT, .category = "bitcoind", .name = "work_update_seconds", .description = "How many seconds between normal work updates? (5-120, 40 suggested)", .required = false, .ptr = &datum_config.bitcoind_work_update_seconds, .default_int = 40 }, { .var_type = DATUM_CONF_BOOL, .category = "bitcoind", .name = "notify_fallback", .description = "Fall back to less efficient methods for new block notifications. Can disable if you use blocknotify.", @@ -93,15 +95,15 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { // mining settings { .var_type = DATUM_CONF_STRING, .category = "mining", .name = "pool_address", .description = "Bitcoin address used for mining rewards.", - .required = true, .ptr = datum_config.mining_pool_address, .max_string_len = 128 }, + .required = true, .ptr = datum_config.mining_pool_address, .max_string_len = sizeof(datum_config.mining_pool_address) }, { .var_type = DATUM_CONF_STRING, .category = "mining", .name = "coinbase_tag_primary", .description = "Text to have in the primary coinbase tag when not using pool (overridden by DATUM Pool)", - .required = false, .ptr = datum_config.mining_coinbase_tag_primary, .default_string[0] = "DATUM Gateway", .max_string_len = 64 }, + .required = false, .ptr = datum_config.mining_coinbase_tag_primary, .default_string[0] = "DATUM Gateway", .max_string_len = sizeof(datum_config.mining_coinbase_tag_primary) }, { .var_type = DATUM_CONF_STRING, .category = "mining", .name = "coinbase_tag_secondary", .description = "Text to have in the secondary coinbase tag (Short name/identifier)", - .required = false, .ptr = datum_config.mining_coinbase_tag_secondary, .default_string[0] = "DATUM User", .max_string_len = 64 }, + .required = false, .ptr = datum_config.mining_coinbase_tag_secondary, .default_string[0] = "DATUM User", .max_string_len = sizeof(datum_config.mining_coinbase_tag_secondary) }, { .var_type = DATUM_CONF_INT, .category = "mining", .name = "coinbase_unique_id", .description = "A unique ID between 1 and 65535. This is appended to the coinbase. Make unique per instance of datum with the same coinbase tags.", .required = false, .ptr = &datum_config.coinbase_unique_id, .default_int = 4242 }, { .var_type = DATUM_CONF_STRING, .category = "mining", .name = "save_submitblocks_dir", .description = "Directory to save all submitted blocks to as submitblock JSON files", - .required = false, .ptr = datum_config.mining_save_submitblocks_dir, .default_string[0] = "", .max_string_len = 256 }, + .required = false, .ptr = datum_config.mining_save_submitblocks_dir, .default_string[0] = "", .max_string_len = sizeof(datum_config.mining_save_submitblocks_dir) }, // API/dashboard { .var_type = DATUM_CONF_INT, .category = "api", .name = "listen_port", .description = "Port to listen for API/dashboard requests (0=disabled)", @@ -109,7 +111,7 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { // extra block submissions list { .var_type = DATUM_CONF_STRING_ARRAY, .category = "extra_block_submissions", .name = "urls", .description = "Array of bitcoind RPC URLs to submit our blocks to directly. Include auth info: http://user:pass@IP", - .required = false, .ptr = datum_config.extra_block_submissions_urls[0], .max_string_len = 512 }, + .required = false, .ptr = datum_config.extra_block_submissions_urls[0], .max_string_len = sizeof(datum_config.extra_block_submissions_urls[0]) }, // logger { .var_type = DATUM_CONF_BOOL, .category = "logger", .name = "log_to_console", .description = "Enable logging of messages to the console", @@ -119,7 +121,7 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { { .var_type = DATUM_CONF_BOOL, .category = "logger", .name = "log_to_file", .description = "Enable logging of messages to a file", .required = false, .ptr = &datum_config.clog_to_file, .default_bool = false }, { .var_type = DATUM_CONF_STRING, .category = "logger", .name = "log_file", .description = "Path to file to write log messages, when enabled", - .required = false, .ptr = datum_config.clog_file, .default_string[0] = "", .max_string_len = 1023 }, + .required = false, .ptr = datum_config.clog_file, .default_string[0] = "", .max_string_len = sizeof(datum_config.clog_file) }, { .var_type = DATUM_CONF_BOOL, .category = "logger", .name = "log_rotate_daily", .description = "Rotate the message log file at midnight", .required = false, .ptr = &datum_config.clog_rotate_daily, .default_bool = true }, { .var_type = DATUM_CONF_BOOL, .category = "logger", .name = "log_calling_function", .description = "Log the name of the calling function when logging", @@ -131,11 +133,11 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { // datum options { .var_type = DATUM_CONF_STRING, .category = "datum", .name = "pool_host", .description = "Remote DATUM server host/ip to use for decentralized pooled mining (set to \"\" to disable pooled mining)", - .required = false, .ptr = datum_config.datum_pool_host, .default_string[0] = "datum-beta1.mine.ocean.xyz", .max_string_len = 1023 }, + .required = false, .ptr = datum_config.datum_pool_host, .default_string[0] = "datum-beta1.mine.ocean.xyz", .max_string_len = sizeof(datum_config.datum_pool_host) }, { .var_type = DATUM_CONF_INT, .category = "datum", .name = "pool_port", .description = "Remote DATUM server port", .required = false, .ptr = &datum_config.datum_pool_port, .default_int = 28915 }, { .var_type = DATUM_CONF_STRING, .category = "datum", .name = "pool_pubkey", .description = "Public key of the DATUM server for initiating encrypted connection. Get from secure location, or set to empty to auto-fetch.", - .required = false, .ptr = datum_config.datum_pool_pubkey, .default_string[0] = "f21f2f0ef0aa1970468f22bad9bb7f4535146f8e4a8f646bebc93da3d89b1406f40d032f09a417d94dc068055df654937922d2c89522e3e8f6f0e649de473003", .max_string_len = 1023 }, + .required = false, .ptr = datum_config.datum_pool_pubkey, .default_string[0] = "f21f2f0ef0aa1970468f22bad9bb7f4535146f8e4a8f646bebc93da3d89b1406f40d032f09a417d94dc068055df654937922d2c89522e3e8f6f0e649de473003", .max_string_len = sizeof(datum_config.datum_pool_pubkey) }, { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "pool_pass_workers", .description = "Pass stratum miner usernames as sub-worker names to the pool (pool_username.miner's username)", .required = false, .ptr = &datum_config.datum_pool_pass_workers, .default_bool = true }, { .var_type = DATUM_CONF_BOOL, .category = "datum", .name = "pool_pass_full_users", .description = "Pass stratum miner usernames as raw usernames to the pool (use if putting multiple payout addresses on miners behind this gateway)", @@ -221,8 +223,11 @@ int datum_config_parse_value(const T_DATUM_CONFIG_ITEM *c, json_t *item) { return 1; } if (!json_is_string(item)) return -1; - strncpy((char *)c->ptr, json_string_value(item), c->max_string_len-1); - ((char *)c->ptr)[c->max_string_len-1] = 0; + // check for overflow + int written = snprintf((char *)c->ptr, c->max_string_len, "%s", json_string_value(item)); + if (written >= c->max_string_len) { + return -2; + } return 1; } @@ -286,8 +291,12 @@ int datum_read_config(const char *conffile) { // item might be valid j = datum_config_parse_value(&datum_config_options[i], item); - if (j != 1) { + if (j == -1) { DLOG_ERROR("Could not parse configuration option %s.%s. Type should be %s", datum_config_options[i].category, datum_config_options[i].name, (datum_config_options[i].var_type= sizeof(submitblockpath)) { + DLOG_ERROR("Overflow in construction of submitblock path!"); } else { - if (!fwrite(submitblock_req, ptr-submitblock_req, 1, f)) { - DLOG_ERROR("Could not write to %s when writing submitblock record to disk: %s!", userpass, strerror(errno)); + FILE *f; + f = fopen(submitblockpath, "w"); + if (!f) { + DLOG_ERROR("Could not open %s for writing submitblock record to disk: %s!", submitblockpath, strerror(errno)); + } else { + if (!fwrite(submitblock_req, ptr-submitblock_req, 1, f)) { + DLOG_ERROR("Could not write to %s when writing submitblock record to disk: %s!", submitblockpath, strerror(errno)); + } + fclose(f); } - fclose(f); } } @@ -2161,8 +2166,6 @@ int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t return 0; } - snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); - // make the call! r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, submitblock_req); curl_easy_cleanup(tcurl); diff --git a/src/datum_submitblock.c b/src/datum_submitblock.c index 022e2202..83e0cc4a 100644 --- a/src/datum_submitblock.c +++ b/src/datum_submitblock.c @@ -52,10 +52,8 @@ char submitblock_hash[256] = { 0 }; void preciousblock(CURL *curl, char *blockhash) { json_t *json; char rpc_data[384]; - char userpass[1024]; // TODO: Move these types of things to the conf file - snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); snprintf(rpc_data, sizeof(rpc_data), "{\"method\":\"preciousblock\",\"params\":[\"%s\"],\"id\":1}", blockhash); json = json_rpc_call(curl, datum_config.bitcoind_rpcurl, userpass, rpc_data); if (!json) return; @@ -65,13 +63,10 @@ void preciousblock(CURL *curl, char *blockhash) { } void datum_submitblock_doit(CURL *tcurl, char *url, const char *submitblock_req, const char *block_hash_hex) { - char userpass[1024]; json_t *r; char *s = NULL; // TODO: Move these types of things to the conf file if (!url) { - snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); - r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, submitblock_req); } else { r = json_rpc_call(tcurl, url, NULL, submitblock_req); From c320b45aca228580e3860e7fc8a4c02a7f699941 Mon Sep 17 00:00:00 2001 From: JesterHodl Date: Sat, 25 Jan 2025 18:40:55 +0100 Subject: [PATCH 4/8] use datum_config.bitcoind_rpcuserpass instead of a global --- src/datum_blocktemplates.c | 4 ++-- src/datum_conf.c | 10 +++++----- src/datum_conf.h | 4 +--- src/datum_stratum.c | 2 +- src/datum_submitblock.c | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/datum_blocktemplates.c b/src/datum_blocktemplates.c index 60608b52..21d0a656 100644 --- a/src/datum_blocktemplates.c +++ b/src/datum_blocktemplates.c @@ -349,7 +349,7 @@ void *datum_gateway_fallback_notifier(void *args) { while(1) { snprintf(req, sizeof(req), "{\"jsonrpc\":\"1.0\",\"id\":\"%"PRIu64"\",\"method\":\"getbestblockhash\",\"params\":[]}", current_time_millis()); - gbbh = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, req); + gbbh = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, datum_config.bitcoind_rpcuserpass, req); if (gbbh) { res_val = json_object_get(gbbh, "result"); if (!res_val) { @@ -415,7 +415,7 @@ void *datum_gateway_template_thread(void *args) { // fetch latest template snprintf(gbt_req, sizeof(gbt_req), "{\"method\":\"getblocktemplate\",\"params\":[{\"rules\":[\"segwit\"]}],\"id\":%"PRIu64"}",(uint64_t)((uint64_t)time(NULL)<<(uint64_t)8)|(uint64_t)(i&255)); - gbt = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, gbt_req); + gbt = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, datum_config.bitcoind_rpcuserpass, gbt_req); if (!gbt) { DLOG_ERROR("Could not fetch new template from %s!", datum_config.bitcoind_rpcurl); diff --git a/src/datum_conf.c b/src/datum_conf.c index b118061c..018e577a 100644 --- a/src/datum_conf.c +++ b/src/datum_conf.c @@ -47,17 +47,17 @@ #include "datum_sockets.h" global_config_t datum_config; -// since we null-terminate the user and password, we'll have enough for the semicolon and NUL -char userpass[sizeof(datum_config.bitcoind_rpcuser) + sizeof(datum_config.bitcoind_rpcpassword)]; +char bitcoind_rpcuser[128]; +char bitcoind_rpcpassword[128]; const char *datum_conf_var_type_text[] = { "N/A", "boolean", "integer", "string", "string_array" }; const T_DATUM_CONFIG_ITEM datum_config_options[] = { // Bitcoind configs { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcuser", .description = "RPC username for communication with local bitcoind.", - .required = true, .ptr = datum_config.bitcoind_rpcuser, .max_string_len = sizeof(datum_config.bitcoind_rpcuser) }, + .required = true, .ptr = bitcoind_rpcuser, .max_string_len = sizeof(bitcoind_rpcuser) }, { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcpassword", .description = "RPC password for communication with local bitcoind.", - .required = true, .ptr = datum_config.bitcoind_rpcpassword, .max_string_len = sizeof(datum_config.bitcoind_rpcpassword) }, + .required = true, .ptr = bitcoind_rpcpassword, .max_string_len = sizeof(bitcoind_rpcpassword) }, { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcurl", .description = "RPC URL for communication with local bitcoind. (GBT Template Source)", .required = true, .ptr = datum_config.bitcoind_rpcurl, .max_string_len = sizeof(datum_config.bitcoind_rpcurl) }, { .var_type = DATUM_CONF_INT, .category = "bitcoind", .name = "work_update_seconds", .description = "How many seconds between normal work updates? (5-120, 40 suggested)", @@ -305,7 +305,7 @@ int datum_read_config(const char *conffile) { } // populate userpass for further reuse - snprintf(userpass, sizeof(userpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); + snprintf(datum_config.bitcoind_rpcuserpass, sizeof(datum_config.bitcoind_rpcuserpass), "%s:%s", bitcoind_rpcuser, bitcoind_rpcpassword); // pass configuration options to the logger datum_logger_config(datum_config.clog_to_file, datum_config.clog_to_console, datum_config.clog_level_console, datum_config.clog_level_file, datum_config.clog_calling_function, datum_config.clog_to_stderr, datum_config.clog_rotate_daily, datum_config.clog_file); diff --git a/src/datum_conf.h b/src/datum_conf.h index 2d926286..94d55ff6 100644 --- a/src/datum_conf.h +++ b/src/datum_conf.h @@ -67,8 +67,7 @@ typedef struct { // Globally accessable config options typedef struct { - char bitcoind_rpcuser[128]; - char bitcoind_rpcpassword[128]; + char bitcoind_rpcuserpass[256]; char bitcoind_rpcurl[256]; int bitcoind_work_update_seconds; bool bitcoind_notify_fallback; @@ -126,7 +125,6 @@ typedef struct { } global_config_t; extern global_config_t datum_config; -extern char userpass[sizeof(datum_config.bitcoind_rpcuser) + sizeof(datum_config.bitcoind_rpcpassword)]; int datum_read_config(const char *conffile); void datum_gateway_help(void); diff --git a/src/datum_stratum.c b/src/datum_stratum.c index 23655239..3f282e99 100644 --- a/src/datum_stratum.c +++ b/src/datum_stratum.c @@ -2183,7 +2183,7 @@ int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t } // make the call! - r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, submitblock_req); + r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, datum_config.bitcoind_rpcuserpass, submitblock_req); curl_easy_cleanup(tcurl); if (!r) { // oddly, this means success here. diff --git a/src/datum_submitblock.c b/src/datum_submitblock.c index 83e0cc4a..4971c648 100644 --- a/src/datum_submitblock.c +++ b/src/datum_submitblock.c @@ -55,7 +55,7 @@ void preciousblock(CURL *curl, char *blockhash) { // TODO: Move these types of things to the conf file snprintf(rpc_data, sizeof(rpc_data), "{\"method\":\"preciousblock\",\"params\":[\"%s\"],\"id\":1}", blockhash); - json = json_rpc_call(curl, datum_config.bitcoind_rpcurl, userpass, rpc_data); + json = json_rpc_call(curl, datum_config.bitcoind_rpcurl, datum_config.bitcoind_rpcuserpass, rpc_data); if (!json) return; json_decref(json); @@ -67,7 +67,7 @@ void datum_submitblock_doit(CURL *tcurl, char *url, const char *submitblock_req, char *s = NULL; // TODO: Move these types of things to the conf file if (!url) { - r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, userpass, submitblock_req); + r = json_rpc_call(tcurl, datum_config.bitcoind_rpcurl, datum_config.bitcoind_rpcuserpass, submitblock_req); } else { r = json_rpc_call(tcurl, url, NULL, submitblock_req); } From f192e9059a6738edffd545b59ea5698fbfa1ca94 Mon Sep 17 00:00:00 2001 From: JesterHodl <103882255+jesterhodl@users.noreply.github.com> Date: Sat, 25 Jan 2025 18:57:50 +0100 Subject: [PATCH 5/8] Clearer sizing of max_string_len for extra_block_submissions Instead of sizing the first element, size the type Co-authored-by: Luke Dashjr --- src/datum_conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datum_conf.c b/src/datum_conf.c index 018e577a..622087c0 100644 --- a/src/datum_conf.c +++ b/src/datum_conf.c @@ -111,7 +111,7 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { // extra block submissions list { .var_type = DATUM_CONF_STRING_ARRAY, .category = "extra_block_submissions", .name = "urls", .description = "Array of bitcoind RPC URLs to submit our blocks to directly. Include auth info: http://user:pass@IP", - .required = false, .ptr = datum_config.extra_block_submissions_urls[0], .max_string_len = sizeof(datum_config.extra_block_submissions_urls[0]) }, + .required = false, .ptr = datum_config.extra_block_submissions_urls[0], .max_string_len = sizeof(*datum_config.extra_block_submissions_urls) }, // logger { .var_type = DATUM_CONF_BOOL, .category = "logger", .name = "log_to_console", .description = "Enable logging of messages to the console", From e1996678562ee97bbe140f438aac56527758a7b3 Mon Sep 17 00:00:00 2001 From: JesterHodl Date: Sat, 25 Jan 2025 19:04:58 +0100 Subject: [PATCH 6/8] Resize coinbase tags to better values In fact the maximums are enforced to be not more than 60 in datum_conf.c --- src/datum_conf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datum_conf.h b/src/datum_conf.h index 94d55ff6..fb387486 100644 --- a/src/datum_conf.h +++ b/src/datum_conf.h @@ -88,8 +88,8 @@ typedef struct { int stratum_v1_idle_timeout_max_last_work; char mining_pool_address[256]; - char mining_coinbase_tag_primary[256]; - char mining_coinbase_tag_secondary[256]; + char mining_coinbase_tag_primary[64]; + char mining_coinbase_tag_secondary[64]; char mining_save_submitblocks_dir[256]; int coinbase_unique_id; From 8edf26e72d948aee196fc7edef1bf087a60709e7 Mon Sep 17 00:00:00 2001 From: JesterHodl Date: Sat, 25 Jan 2025 19:09:39 +0100 Subject: [PATCH 7/8] Reduce and rename DATUM_CONFIG_MAX_STRING_ARRAY_LEN --- src/datum_conf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datum_conf.h b/src/datum_conf.h index fb387486..ae34b267 100644 --- a/src/datum_conf.h +++ b/src/datum_conf.h @@ -38,7 +38,7 @@ #define DATUM_CONFIG_MAX_ARRAY_ENTRIES 32 #define DATUM_MAX_BLOCK_SUBMITS DATUM_CONFIG_MAX_ARRAY_ENTRIES -#define DATUM_CONFIG_MAX_STRING_ARRAY_LEN 1024 +#define DATUM_MAX_SUBMIT_URL_LEN 512 #include #include @@ -96,7 +96,7 @@ typedef struct { int api_listen_port; int extra_block_submissions_count; - char extra_block_submissions_urls[DATUM_MAX_BLOCK_SUBMITS][DATUM_CONFIG_MAX_STRING_ARRAY_LEN]; + char extra_block_submissions_urls[DATUM_MAX_BLOCK_SUBMITS][DATUM_MAX_SUBMIT_URL_LEN]; bool clog_to_file; bool clog_to_console; From 2b7442bcdcab036e977810693c33517189d86fca Mon Sep 17 00:00:00 2001 From: JesterHodl Date: Thu, 6 Feb 2025 20:44:54 +0100 Subject: [PATCH 8/8] Put rpcuser/pass from global back to datum config --- src/datum_conf.c | 8 +++----- src/datum_conf.h | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/datum_conf.c b/src/datum_conf.c index 622087c0..f18d281a 100644 --- a/src/datum_conf.c +++ b/src/datum_conf.c @@ -48,16 +48,14 @@ global_config_t datum_config; -char bitcoind_rpcuser[128]; -char bitcoind_rpcpassword[128]; const char *datum_conf_var_type_text[] = { "N/A", "boolean", "integer", "string", "string_array" }; const T_DATUM_CONFIG_ITEM datum_config_options[] = { // Bitcoind configs { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcuser", .description = "RPC username for communication with local bitcoind.", - .required = true, .ptr = bitcoind_rpcuser, .max_string_len = sizeof(bitcoind_rpcuser) }, + .required = true, .ptr = datum_config.bitcoind_rpcuser, .max_string_len = sizeof(datum_config.bitcoind_rpcuser) }, { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcpassword", .description = "RPC password for communication with local bitcoind.", - .required = true, .ptr = bitcoind_rpcpassword, .max_string_len = sizeof(bitcoind_rpcpassword) }, + .required = true, .ptr = datum_config.bitcoind_rpcpassword, .max_string_len = sizeof(datum_config.bitcoind_rpcpassword) }, { .var_type = DATUM_CONF_STRING, .category = "bitcoind", .name = "rpcurl", .description = "RPC URL for communication with local bitcoind. (GBT Template Source)", .required = true, .ptr = datum_config.bitcoind_rpcurl, .max_string_len = sizeof(datum_config.bitcoind_rpcurl) }, { .var_type = DATUM_CONF_INT, .category = "bitcoind", .name = "work_update_seconds", .description = "How many seconds between normal work updates? (5-120, 40 suggested)", @@ -305,7 +303,7 @@ int datum_read_config(const char *conffile) { } // populate userpass for further reuse - snprintf(datum_config.bitcoind_rpcuserpass, sizeof(datum_config.bitcoind_rpcuserpass), "%s:%s", bitcoind_rpcuser, bitcoind_rpcpassword); + snprintf(datum_config.bitcoind_rpcuserpass, sizeof(datum_config.bitcoind_rpcuserpass), "%s:%s", datum_config.bitcoind_rpcuser, datum_config.bitcoind_rpcpassword); // pass configuration options to the logger datum_logger_config(datum_config.clog_to_file, datum_config.clog_to_console, datum_config.clog_level_console, datum_config.clog_level_file, datum_config.clog_calling_function, datum_config.clog_to_stderr, datum_config.clog_rotate_daily, datum_config.clog_file); diff --git a/src/datum_conf.h b/src/datum_conf.h index ae34b267..5231f49a 100644 --- a/src/datum_conf.h +++ b/src/datum_conf.h @@ -68,6 +68,8 @@ typedef struct { // Globally accessable config options typedef struct { char bitcoind_rpcuserpass[256]; + char bitcoind_rpcuser[128]; + char bitcoind_rpcpassword[128]; char bitcoind_rpcurl[256]; int bitcoind_work_update_seconds; bool bitcoind_notify_fallback;