summaryrefslogtreecommitdiff
path: root/src/lib/confmgt/unitparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/confmgt/unitparse.c')
-rw-r--r--src/lib/confmgt/unitparse.c70
1 files changed, 59 insertions, 11 deletions
diff --git a/src/lib/confmgt/unitparse.c b/src/lib/confmgt/unitparse.c
index c3ed8285a4..61edc60694 100644
--- a/src/lib/confmgt/unitparse.c
+++ b/src/lib/confmgt/unitparse.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -13,8 +13,11 @@
#include "lib/confmgt/unitparse.h"
#include "lib/log/log.h"
#include "lib/log/util_bug.h"
+#include "lib/malloc/malloc.h"
#include "lib/string/parse_int.h"
+#include "lib/string/printf.h"
#include "lib/string/util_string.h"
+#include "lib/intmath/muldiv.h"
#include <string.h>
@@ -109,22 +112,30 @@ const struct unit_table_t time_msec_units[] = {
* table <b>u</b>, then multiply the number by the unit multiplier.
* On success, set *<b>ok</b> to 1 and return this product.
* Otherwise, set *<b>ok</b> to 0.
+ *
+ * If an error (like overflow or a negative value is detected), put an error
+ * message in *<b>errmsg_out</b> if that pointer is non-NULL, and otherwise
+ * log a warning.
*/
uint64_t
-config_parse_units(const char *val, const unit_table_t *u, int *ok)
+config_parse_units(const char *val, const unit_table_t *u, int *ok,
+ char **errmsg_out)
{
uint64_t v = 0;
double d = 0;
int use_float = 0;
char *cp;
+ char *errmsg = NULL;
tor_assert(ok);
v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
if (!*ok || (cp && *cp == '.')) {
d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
- if (!*ok)
+ if (!*ok) {
+ tor_asprintf(&errmsg, "Unable to parse %s as a number", val);
goto done;
+ }
use_float = 1;
}
@@ -142,18 +153,55 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok)
for ( ;u->unit;++u) {
if (!strcasecmp(u->unit, cp)) {
- if (use_float)
- v = (uint64_t)(u->multiplier * d);
- else
- v *= u->multiplier;
+ if (use_float) {
+ d = u->multiplier * d;
+
+ if (d < 0) {
+ tor_asprintf(&errmsg, "Got a negative value while parsing %s %s",
+ val, u->unit);
+ *ok = 0;
+ goto done;
+ }
+
+ // Some compilers may warn about casting a double to an unsigned type
+ // because they don't know if d is >= 0
+ if (d >= 0 && (d > (double)INT64_MAX || (uint64_t)d > INT64_MAX)) {
+ tor_asprintf(&errmsg, "Overflow while parsing %s %s",
+ val, u->unit);
+ *ok = 0;
+ goto done;
+ }
+
+ v = (uint64_t) d;
+ } else {
+ v = tor_mul_u64_nowrap(v, u->multiplier);
+
+ if (v > INT64_MAX) {
+ tor_asprintf(&errmsg, "Overflow while parsing %s %s",
+ val, u->unit);
+ *ok = 0;
+ goto done;
+ }
+ }
+
*ok = 1;
goto done;
}
}
- log_warn(LD_CONFIG, "Unknown unit '%s'.", cp);
+ tor_asprintf(&errmsg, "Unknown unit in %s", val);
*ok = 0;
done:
+ if (errmsg) {
+ tor_assert_nonfatal(!*ok);
+ if (errmsg_out) {
+ *errmsg_out = errmsg;
+ } else {
+ log_warn(LD_CONFIG, "%s", errmsg);
+ tor_free(errmsg);
+ }
+ }
+
if (*ok)
return v;
else
@@ -167,7 +215,7 @@ config_parse_units(const char *val, const unit_table_t *u, int *ok)
uint64_t
config_parse_memunit(const char *s, int *ok)
{
- uint64_t u = config_parse_units(s, memory_units, ok);
+ uint64_t u = config_parse_units(s, memory_units, ok, NULL);
return u;
}
@@ -179,7 +227,7 @@ int
config_parse_msec_interval(const char *s, int *ok)
{
uint64_t r;
- r = config_parse_units(s, time_msec_units, ok);
+ r = config_parse_units(s, time_msec_units, ok, NULL);
if (r > INT_MAX) {
log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
*ok = 0;
@@ -196,7 +244,7 @@ int
config_parse_interval(const char *s, int *ok)
{
uint64_t r;
- r = config_parse_units(s, time_units, ok);
+ r = config_parse_units(s, time_units, ok, NULL);
if (r > INT_MAX) {
log_warn(LD_CONFIG, "Interval '%s' is too long", s);
*ok = 0;