On 6 August 2014 19:11, Stefan Sperling <s...@elego.de> wrote:
> I'd like to remove svn__strtol() and svn__strtoul().
>
> Let's not get performance and micro-optimisation in the way of error checking.
> Even if the input does not come from the network or cannot for any other
> reason be inherently malicious it might be erroneous due to other
> problems such as disk corruption. I don't believe it's OK to willingly
> parse revision files without such checks. I think it's irresponsible
> to do so and makes diagnosing difficult problems unnecessarily harder.
>
I'm big +1 one this: we should not trade error checking for
performance. Especially for micro-optimization.

See my comments on patch inline.
> [[[
> Remove svn__strtol() and svn__strtoul(). They were introduced as a
> performance enhancement but don't provide sufficient error checking.
>
[...]

>
>    return TRUE;
> @@ -93,8 +102,13 @@ static svn_boolean_t
>  txn_id_parse(svn_fs_fs__id_part_t *txn_id,
>               const char *data)
>  {
> -  const char *end;
> -  txn_id->revision = svn__strtol(data, &end);
> +  char *end;
> +
> +  apr_set_os_error(0);
> +  txn_id->revision = strtol(data, &end, 10);
> +  if (data[0] == '\0' || apr_get_os_error() == ERANGE)
apr_get_os_error() uses GetLastError() on Windows, while strtol() uses
standard errno for error code.

> +    return FALSE;

> +
>    data = strchr(end, '-');
>    if (data == NULL)
>      return FALSE;
> @@ -478,7 +492,6 @@ svn_fs_fs__id_parse(char *data,
>    if (str[0] == 'r')
>      {
>        apr_int64_t val;
> -      const char *tmp;
>        svn_error_t *err;
>
>        /* This is a revision type ID */
> @@ -489,8 +502,15 @@ svn_fs_fs__id_parse(char *data,
>        str = svn_cstring_tokenize("/", &data);
>        if (str == NULL)
>          return NULL;
> -      id->private_id.rev_item.revision = svn__strtol(str, &tmp);
>
> +      err = svn_cstring_atoi64(&val, str);
> +      if (err)
> +        {
> +          svn_error_clear(err);
> +          return NULL;
> +        }
> +      id->private_id.rev_item.revision = (svn_revnum_t)val;
> +
>        err = svn_cstring_atoi64(&val, data);
>        if (err)
>          {
> Index: subversion/libsvn_subr/string.c
> ===================================================================
> --- subversion/libsvn_subr/string.c     (revision 1616191)
> +++ subversion/libsvn_subr/string.c     (working copy)
> @@ -1039,45 +1039,6 @@ svn_cstring_atoi(int *n, const char *str)
>    return SVN_NO_ERROR;
>  }
>
> -unsigned long
> -svn__strtoul(const char* buffer, const char** end)
> -{
> -  unsigned long result = 0;
> -
> -  /* this loop will execute in just 2 CPU cycles, confirmed by measurement:
> -     7 macro-ops (max 4 / cycle => 2 cycles)
> -       1 load (max 1 / cycle)
> -       1 jumps (compare + conditional jump == 1 macro op; max 1 / cycle)
> -       2 arithmetic ops (subtract, increment; max 3 / cycle)
> -       2 scale-and-add AGU ops (max 3 / cycle)
> -       1 compiler-generated move operation
> -     dependency chain: temp = result * 4 + result; result = temp * 2 + c
> -                       (2 ops with latency 1 => 2 cycles)
> -   */
> -  while (1)
> -    {
> -      unsigned long c = (unsigned char)*buffer - (unsigned char)'0';
> -      if (c > 9)
> -        break;
> -
> -      result = result * 10 + c;
> -      ++buffer;
> -    }
> -
> -  *end = buffer;
> -  return result;
> -}
> -
> -long
> -svn__strtol(const char* buffer, const char** end)
> -{
> -  if (*buffer == '-')
> -    return -(long)svn__strtoul(buffer+1, end);
> -  else
> -    return (long)svn__strtoul(buffer, end);
> -}
> -
> -
>  /* "Precalculated" itoa values for 2 places (including leading zeros).
>   * For maximum performance, make sure all table entries are word-aligned.
>   */
> Index: subversion/libsvn_subr/time.c
> ===================================================================
> --- subversion/libsvn_subr/time.c       (revision 1616191)
> +++ subversion/libsvn_subr/time.c       (working copy)
> @@ -28,6 +28,7 @@
>  #include <apr_pools.h>
>  #include <apr_time.h>
>  #include <apr_strings.h>
> +#include <apr_errno.h>
>  #include "svn_io.h"
>  #include "svn_time.h"
>  #include "svn_utf.h"
> @@ -34,9 +35,7 @@
>  #include "svn_error.h"
>  #include "svn_private_config.h"
>
> -#include "private/svn_string_private.h"
>
> -
>
>  /*** Code. ***/
>
> @@ -137,25 +136,32 @@ svn_time_from_cstring(apr_time_t *when, const char
>    apr_time_exp_t exploded_time;
>    apr_status_t apr_err;
>    char wday[4], month[4];
> -  const char *c;
> +  char *c;
>
> -  /* Open-code parsing of the new timestamp format, as this
> -     is a hot path for reading the entries file.  This format looks
> -     like:  "2001-08-31T04:24:14.966996Z"  */
> -  exploded_time.tm_year = (apr_int32_t) svn__strtoul(data, &c);
> -  if (*c++ != '-') goto fail;
> -  exploded_time.tm_mon = (apr_int32_t) svn__strtoul(c, &c);
> -  if (*c++ != '-') goto fail;
> -  exploded_time.tm_mday = (apr_int32_t) svn__strtoul(c, &c);
> -  if (*c++ != 'T') goto fail;
> -  exploded_time.tm_hour = (apr_int32_t) svn__strtoul(c, &c);
> -  if (*c++ != ':') goto fail;
> -  exploded_time.tm_min = (apr_int32_t) svn__strtoul(c, &c);
> -  if (*c++ != ':') goto fail;
> -  exploded_time.tm_sec = (apr_int32_t) svn__strtoul(c, &c);
> -  if (*c++ != '.') goto fail;
> -  exploded_time.tm_usec = (apr_int32_t) svn__strtoul(c, &c);
> -  if (*c++ != 'Z') goto fail;
> +  /* Open-code parsing of the new timestamp format.
> +     This format looks like:  "2001-08-31T04:24:14.966996Z"  */
> +  apr_set_os_error(0);
> +  exploded_time.tm_year = (apr_int32_t) strtol(data, &c, 10);
> +  if (apr_get_os_error() == ERANGE || *c++ != '-')
Same here.

> +    goto fail;
> +  exploded_time.tm_mon = (apr_int32_t) strtol(c, &c, 10);
> +  if (apr_get_os_error() == ERANGE || *c++ != '-')
> +    goto fail;
> +  exploded_time.tm_mday = (apr_int32_t) strtol(c, &c, 10);
> +  if (apr_get_os_error() == ERANGE || *c++ != 'T')
> +    goto fail;
> +  exploded_time.tm_hour = (apr_int32_t) strtol(c, &c, 10);
> +  if (apr_get_os_error() == ERANGE || *c++ != ':')
> +    goto fail;
> +  exploded_time.tm_min = (apr_int32_t) strtol(c, &c, 10);
> +  if (apr_get_os_error() == ERANGE || *c++ != ':')
> +    goto fail;
> +  exploded_time.tm_sec = (apr_int32_t) strtol(c, &c, 10);
> +  if (apr_get_os_error() == ERANGE || *c++ != '.')
> +    goto fail;
> +  exploded_time.tm_usec = (apr_int32_t) strtol(c, &c, 10);
> +  if (apr_get_os_error() == ERANGE || *c++ != 'Z')
> +    goto fail;
>
>    exploded_time.tm_year  -= 1900;
>    exploded_time.tm_mon   -= 1;
> Index: subversion/libsvn_subr/types.c
> ===================================================================
> --- subversion/libsvn_subr/types.c      (revision 1616191)
> +++ subversion/libsvn_subr/types.c      (working copy)
> @@ -21,6 +21,7 @@
>   * ====================================================================
>   */
>
> +#include <apr_errno.h>
>  #include <apr_pools.h>
>  #include <apr_uuid.h>
>
> @@ -39,19 +40,34 @@ svn_revnum_parse(svn_revnum_t *rev,
>                   const char *str,
>                   const char **endptr)
>  {
> -  const char *end;
> +  char *end;
> +  apr_int64_t result;
>
> -  svn_revnum_t result = (svn_revnum_t)svn__strtoul(str, &end);
> -
>    if (endptr)
>      *endptr = str;
>
> +  /* Use the same error message string as svn_cstring_strtoui64()
> +     to limit the burden on translators. */
> +  if (str[0] == '\0')
> +    return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
> +                             _("Could not convert '%s' into a number"), "");
> +
> +  apr_set_os_error(0);
> +
> +  result = (apr_int64_t)strtol(str, &end, 10);
> +
> +  if (apr_get_os_error() == ERANGE)
And here.

> +    return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
> +                             _("Number '%s' is out of range"), str);
> +
> +  if (result < 0)
> +    return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
> +                             _("Negative revision number found parsing 
> '%s'"),
> +                            str);
>    if (str == end)
> -    return svn_error_createf
> -              (SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
> -               *str == '-' ? _("Negative revision number found parsing '%s'")
> -                           : _("Invalid revision number found parsing '%s'"),
> -               str);
> +    return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
> +                             _("Invalid revision number found parsing '%s'"),
> +                             str);
>
>    /* a revision number with more than 9 digits is suspicious.
>       Have a closer look at those. */
> @@ -59,14 +75,12 @@ svn_revnum_parse(svn_revnum_t *rev,
>      {
>        /* we support 32 bit revision numbers only. check for overflows */
>        if (str + 10 < end)
> -        return svn_error_createf
> -                  (SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
> +        return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
>                    _("Revision number longer than 10 digits '%s'"), str);
>
>        /* we support 32 bit revision numbers only. check for overflows */
>        if (*str > '2' || (apr_uint32_t)result > APR_INT32_MAX)
> -        return svn_error_createf
> -                  (SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
> +        return svn_error_createf(SVN_ERR_REVNUM_PARSE_FAILURE, NULL,
>                    _("Revision number too large '%s'"), str);
>      }
>
> @@ -73,7 +87,7 @@ svn_revnum_parse(svn_revnum_t *rev,
>    if (endptr)
>      *endptr = end;
>
> -  *rev = result;
> +  *rev = (svn_revnum_t)result;
>
>    return SVN_NO_ERROR;
>  }



-- 
Ivan Zhakov
CTO | VisualSVN | http://www.visualsvn.com

Reply via email to