Previous safe_atoi didn't check range of input values so if for example
user used -1 s token timeout, it was converted to UINT32_MAX without
letting user know.
Another safe_atoi problem was using strtol. This works pretty well on
64-bit systems, where long integer is usually 64-bits long, sadly on
32-bit systems, it is usually 32-bit long. And because strtol returns
signed integer, it was not possible to enter 32-bit value with highest
bit set.
Solution is to use strtoll which is guaranteed to be at least 64-bits
long and check value range.
Also error message now contains also information about expected value
range.
Signed-off-by: Jan Friesse <jfriesse@xxxxxxxxxx>
---
exec/coroparse.c | 117 ++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 79 insertions(+), 38 deletions(-)
diff --git a/exec/coroparse.c b/exec/coroparse.c
index 6b883b5..bf46539 100644
--- a/exec/coroparse.c
+++ b/exec/coroparse.c
@@ -405,14 +405,36 @@ static int parse_section(FILE *fp,
return 0;
}
-static int safe_atoi(const char *str, int *res)
+static int safe_atoq_range(icmap_value_types_t value_type, long long int *min_val, long long int *max_val)
{
- int val;
+ switch (value_type) {
+ case ICMAP_VALUETYPE_INT8: *min_val = INT8_MIN; *max_val = INT8_MAX; break;
+ case ICMAP_VALUETYPE_UINT8: *min_val = 0; *max_val = UINT8_MAX; break;
+ case ICMAP_VALUETYPE_INT16: *min_val = INT16_MIN; *max_val = INT16_MAX; break;
+ case ICMAP_VALUETYPE_UINT16: *min_val = 0; *max_val = UINT16_MAX; break;
+ case ICMAP_VALUETYPE_INT32: *min_val = INT32_MIN; *max_val = INT32_MAX; break;
+ case ICMAP_VALUETYPE_UINT32: *min_val = 0; *max_val = UINT32_MAX; break;
+ default:
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Convert string str to long long int res. Type of result is target_type and currently only
+ * ICMAP_VALUETYPE_[U]INT[8|16|32] is supported.
+ * Return 0 on success, -1 on failure.
+ */
+static int safe_atoq(const char *str, long long int *res, icmap_value_types_t target_type)
+{
+ long long int val;
+ long long int min_val, max_val;
char *endptr;
errno = 0;
- val = strtol(str, &endptr, 10);
+ val = strtoll(str, &endptr, 10);
if (errno == ERANGE) {
return (-1);
}
@@ -425,6 +447,14 @@ static int safe_atoi(const char *str, int *res)
return (-1);
}
+ if (safe_atoq_range(target_type, &min_val, &max_val) != 0) {
+ return (-1);
+ }
+
+ if (val < min_val || val > max_val) {
+ return (-1);
+ }
+
*res = val;
return (0);
}
@@ -461,7 +491,10 @@ static int main_config_parser_cb(const char *path,
icmap_map_t config_map,
void *user_data)
{
- int i;
+ int ii;
+ long long int val;
+ long long int min_val, max_val;
+ icmap_value_types_t val_type = ICMAP_VALUETYPE_BINARY;
unsigned long long int ull;
int add_as_string;
char key_name[ICMAP_KEYNAME_MAXLEN];
@@ -487,10 +520,11 @@ static int main_config_parser_cb(const char *path,
case MAIN_CP_CB_DATA_STATE_PLOAD:
if ((strcmp(path, "pload.count") == 0) ||
(strcmp(path, "pload.size") == 0)) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT32;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- icmap_set_uint32_r(config_map, path, i);
+ icmap_set_uint32_r(config_map, path, val);
add_as_string = 0;
}
break;
@@ -499,10 +533,11 @@ static int main_config_parser_cb(const char *path,
(strcmp(path, "quorum.votes") == 0) ||
(strcmp(path, "quorum.last_man_standing_window") == 0) ||
(strcmp(path, "quorum.leaving_timeout") == 0)) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT32;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- icmap_set_uint32_r(config_map, path, i);
+ icmap_set_uint32_r(config_map, path, val);
add_as_string = 0;
}
@@ -512,27 +547,30 @@ static int main_config_parser_cb(const char *path,
(strcmp(path, "quorum.wait_for_all") == 0) ||
(strcmp(path, "quorum.auto_tie_breaker") == 0) ||
(strcmp(path, "quorum.last_man_standing") == 0)) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT8;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- icmap_set_uint8_r(config_map, path, i);
+ icmap_set_uint8_r(config_map, path, val);
add_as_string = 0;
}
break;
case MAIN_CP_CB_DATA_STATE_QDEVICE:
if ((strcmp(path, "quorum.device.timeout") == 0) ||
(strcmp(path, "quorum.device.votes") == 0)) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT32;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- icmap_set_uint32_r(config_map, path, i);
+ icmap_set_uint32_r(config_map, path, val);
add_as_string = 0;
}
if ((strcmp(path, "quorum.device.master_wins") == 0)) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT8;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- icmap_set_uint8_r(config_map, path, i);
+ icmap_set_uint8_r(config_map, path, val);
add_as_string = 0;
}
break;
@@ -563,10 +601,11 @@ static int main_config_parser_cb(const char *path,
(strcmp(path, "totem.max_messages") == 0) ||
(strcmp(path, "totem.miss_count_const") == 0) ||
(strcmp(path, "totem.netmtu") == 0)) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT32;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- icmap_set_uint32_r(config_map,path, i);
+ icmap_set_uint32_r(config_map,path, val);
add_as_string = 0;
}
if (strcmp(path, "totem.config_version") == 0) {
@@ -634,11 +673,12 @@ static int main_config_parser_cb(const char *path,
case MAIN_CP_CB_DATA_STATE_INTERFACE:
if (strcmp(path, "totem.interface.ringnumber") == 0) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT8;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- data->ringnumber = i;
+ data->ringnumber = val;
add_as_string = 0;
}
if (strcmp(path, "totem.interface.bindnetaddr") == 0) {
@@ -654,27 +694,19 @@ static int main_config_parser_cb(const char *path,
add_as_string = 0;
}
if (strcmp(path, "totem.interface.mcastport") == 0) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT16;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- data->mcastport = i;
- if (data->mcastport < 0 || data->mcastport > 65535) {
- *error_string = "Invalid multicast port (should be 0..65535)";
-
- return (0);
- };
+ data->mcastport = val;
add_as_string = 0;
}
if (strcmp(path, "totem.interface.ttl") == 0) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT8;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- data->ttl = i;
- if (data->ttl < 0 || data->ttl > 255) {
- *error_string = "Invalid TTL (should be 0..255)";
-
- return (0);
- };
+ data->ttl = val;
add_as_string = 0;
}
break;
@@ -804,11 +836,12 @@ static int main_config_parser_cb(const char *path,
snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.%s", data->node_number, key);
if ((strcmp(key, "nodeid") == 0) ||
(strcmp(key, "quorum_votes") == 0)) {
- if (safe_atoi(value, &i) != 0) {
+ val_type = ICMAP_VALUETYPE_UINT32;
+ if (safe_atoq(value, &val, val_type) != 0) {
goto atoi_error;
}
- icmap_set_uint32_r(config_map, key_name, i);
+ icmap_set_uint32_r(config_map, key_name, val);
add_as_string = 0;
}
@@ -923,13 +956,13 @@ static int main_config_parser_cb(const char *path,
icmap_set_uint8_r(config_map, key_name, data->ttl);
}
- i = 0;
+ ii = 0;
for (iter = data->member_items_head.next;
iter != &data->member_items_head; iter = iter_next) {
kv_item = list_entry(iter, struct key_value_list_item, list);
snprintf(key_name, ICMAP_KEYNAME_MAXLEN, "totem.interface.%u.member.%u",
- data->ringnumber, i);
+ data->ringnumber, ii);
icmap_set_string_r(config_map, key_name, kv_item->value);
iter_next = iter->next;
@@ -937,7 +970,7 @@ static int main_config_parser_cb(const char *path,
free(kv_item->value);
free(kv_item->key);
free(kv_item);
- i++;
+ ii++;
}
data->state = MAIN_CP_CB_DATA_STATE_TOTEM;
@@ -1079,8 +1112,16 @@ static int main_config_parser_cb(const char *path,
return (1);
atoi_error:
+ min_val = max_val = 0;
+ /*
+ * This is really assert, because developer ether doesn't set val_type correctly or
+ * we've got here after some nasty memory overwrite
+ */
+ assert(safe_atoq_range(val_type, &min_val, &max_val) == 0);
+
snprintf(formated_err, sizeof(formated_err),
- "Value of key \"%s\" must be integer, but \"%s\" was given", key, value);
+ "Value of key \"%s\" is expected to be integer in range (%lld..%lld), but \"%s\" was given",
+ key, min_val, max_val, value);
*error_string = formated_err;
return (0);