When we are parsing an integer or unsigned long, we use
the strto*max functions, which properly set errno to ERANGE
if we get a large value. However, we also do further range
checks after applying our multiplication factor, but do not
set ERANGE. This means that a caller cannot tell if an error
was caused by ERANGE or if the input was simply not a valid
number.

This patch teaches git_parse_signed and git_parse_unsigned
reliably set ERANGE for range errors, and EINVAL for other
errors.

Signed-off-by: Jeff King <p...@peff.net>
---
I'm a little iffy on using errno like this. The "normal" way in git
would be to return an enum like:

  enum config_parse_error {
    CONFIG_PARSE_SUCCESS = 0,
    CONFIG_PARSE_RANGE = -1,
    CONFIG_PARSE_INVALID_UNIT = -2
  };

But that would be changing the return semantics of git_parse_ulong
(which currently is "0" for fail, "1" for success) without changing its
signature. I figured this was the path of least resistance since we
already get ERANGE out of strtoimax, but I'm happy to switch it, too.

 config.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/config.c b/config.c
index 0a65ac2..7332b06 100644
--- a/config.c
+++ b/config.c
@@ -480,16 +480,21 @@ static int git_parse_signed(const char *value, intmax_t 
*ret, intmax_t max)
                val = strtoimax(value, &end, 0);
                if (errno == ERANGE)
                        return 0;
-               if (!parse_unit_factor(end, &factor))
+               if (!parse_unit_factor(end, &factor)) {
+                       errno = EINVAL;
                        return 0;
+               }
                uval = abs(val);
                uval *= factor;
-               if (uval > max || abs(val) > uval)
+               if (uval > max || abs(val) > uval) {
+                       errno = ERANGE;
                        return 0;
+               }
                val *= factor;
                *ret = val;
                return 1;
        }
+       errno = EINVAL;
        return 0;
 }
 
@@ -505,13 +510,18 @@ int git_parse_unsigned(const char *value, uintmax_t *ret, 
uintmax_t max)
                if (errno == ERANGE)
                        return 0;
                oldval = val;
-               if (!parse_unit_factor(end, &val))
+               if (!parse_unit_factor(end, &val)) {
+                       errno = EINVAL;
                        return 0;
-               if (val > max || oldval > val)
+               }
+               if (val > max || oldval > val) {
+                       errno = ERANGE;
                        return 0;
+               }
                *ret = val;
                return 1;
        }
+       errno = EINVAL;
        return 0;
 }
 
-- 
1.8.4.2.g87d4a77

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to