Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 29 Aug 2013 15:14:56 +0400
From:      Sergey Kandaurov <pluknet@freebsd.org>
To:        Bruce Evans <brde@optusnet.com.au>
Cc:        svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org, Andrey Chernov <ache@freebsd.org>, Jilles Tjoelker <jilles@stack.nl>
Subject:   Re: svn commit: r254600 - head/lib/libutil
Message-ID:  <CAE-mSO%2BuzOG2QbhiPbcSwPCUupaMtHvhPc61JEjyb4E-XuHLiw@mail.gmail.com>
In-Reply-To: <20130822185756.Y1297@besplex.bde.org>
References:  <201308211646.r7LGk6eV051215@svn.freebsd.org> <5214F72B.7070006@freebsd.org> <20130821190309.GB52908@omg> <20130821202725.GA4991@stack.nl> <20130821212413.GC52908@omg> <20130821213755.GA8052@stack.nl> <20130822185756.Y1297@besplex.bde.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On 22 August 2013 14:51, Bruce Evans <brde@optusnet.com.au> wrote:
> On Wed, 21 Aug 2013, Jilles Tjoelker wrote:
>
>> On Thu, Aug 22, 2013 at 01:24:13AM +0400, Sergey Kandaurov wrote:
>>>
>>> On Wed, Aug 21, 2013 at 10:27:25PM +0200, Jilles Tjoelker wrote:
>>>>
>>>> On Wed, Aug 21, 2013 at 11:03:10PM +0400, Sergey Kandaurov wrote:
>>>>>
>>>>> On Wed, Aug 21, 2013 at 09:21:47PM +0400, Andrey Chernov wrote:
>>>>>>
>>>>>> On 21.08.2013 20:46, Sergey Kandaurov wrote:
>>>>>>>
>>>>>>>         number = strtoumax(buf, &endptr, 0);
>>>>>>>
>>>>>>> +       if (number == UINTMAX_MAX && errno == ERANGE) {
>>>>>>> +               return (-1);
>>>>>>> +       }
>>
>>
>>>>>> You need to reset errno before strtoumax() call (errno = 0), because
>>>>>> any
>>>>>> of previous functions may left it as ERANGE.
>
>
> This also has a bogus test for UINTMAX_MAX wheich prevents input of
> the number UINTMAX_MAX on systems where the limit for the function
> (UINT64_T_MAX) is the same as the limit for strtoumax().  This test is
> a wrong way of doing the errno thing.
>
> This also has a high density of style bugs:
> - excessive braces
> - extra blank line
>
> expand_number() remains a very badly designed and implemented function.
> Its design errors start with its name.  It doesn't expand numbers.  It
> converts strings to numbers.  The strtoumax() family is missing this
> bug and others.  Its design errors continue with it taking a uint64_t
> arg and not a uint64_t arg.
>
> Its implementation errors begin with broken range checking.  The above
> is an incomplete fix for this.  Unless uintmax_t is the same as uint64_t,
> there are still the following bugs:
> - the return value of strotumax() is assigned to a uint64_t.  This may
>   overflow.  After overflow, the bogus (number == UINTMAX_MAX) check
>   has no effect, since when overflow is possible 'number' cannot equal
>   UINTMAX_MAX.
> - values that exceed the maximum supported (UINT64_MAX) are not detected.
>   They are silently truncated to uint64_t.
Ack.

[...]
>
> Some of the other bugs in the old version:
>
> % /*
> %  * Convert an expression of the following forms to a uint64_t.
> %  *    1) A positive decimal number.
> %  *    2) A positive decimal number followed by a 'b' or 'B' (mult by 1).
> %  *    3) A positive decimal number followed by a 'k' or 'K' (mult by 1 <<
> 10).
> %  *    4) A positive decimal number followed by a 'm' or 'M' (mult by 1 <<
> 20).
> %  *    5) A positive decimal number followed by a 'g' or 'G' (mult by 1 <<
> 30).
> %  *    6) A positive decimal number followed by a 't' or 'T' (mult by 1 <<
> 40).
> %  *    7) A positive decimal number followed by a 'p' or 'P' (mult by 1 <<
> 50).
> %  *    8) A positive decimal number followed by a 'e' or 'E' (mult by 1 <<
> 60).
>
> This garbage was copied from a similar comment in dd/args.c.  The number is
> actually neither positive nor decimal, especially in dd where there is a
> variant of this function that returns negative numbers.  Another design
> error
> in this function is that it can't return negative numbers.

Well, I see no useful contents in this comment. It is obvious and verbose.
Probably it should be trimmed.

>
> %  */
> %
>
> This bogus blank line detaches the comment from the code that it describes,
> resulting in the comment describing null code.

Sorry, I didn't see such blank line in head.

>
> % int
> % expand_number(const char *buf, uint64_t *num)
> % {
> %       uint64_t number;
> %       unsigned shift;
> %       char *endptr;
> % %     number = strtoumax(buf, &endptr, 0);
>
> This supports non-positive non-decimal numbers.  The support for non-decimal
> numbers is more intentional (it is given by the base 0 in the call).
> Negative
> numbers are only supported by strtoumax() allowing a minus sign and
> returning a nonnegative number equal to 1 larger than UINTMAX_MAX less the
> number without the minus sign.
>

Fixing the verbose comment above would result in adding more verboseness
citing from man strtoumax(3) wrt. signedness and base details.
Yet another point to trim it.

[ dd dd details here ]
>
> To start fixing expand_number():
>
> /* Fix comment here. */
> int
>
> expand_number(const char *buf, uint64_t *num)
> {
>         char *endptr;
>         uintmax_t num;
>         uint64_t number;
>         unsigned shift;
>         int serrno;
>
>         serrno = errno;
>         errno = 0;
>         num = strtoumax(buf, &endptr, 0);
>         if (num > UINT64_MAX)
>                 errno = ERANGE;
>         if (errno != 0)
>                 return (-1);
>         errno = serrno;
>         number = num;
>         ...
> }
>
> This depends on the POSIX bug for detecting the no-digits case.
> Fortunately, the early error handling of this function is simple and
> compatible with that of strtoumax(), so we don't need to translate
> strtoumax()'s errors except for adjusting its range error.
>

Thanks for your valuable input. What about this change (on top of head)?

Index: expand_number.c
===================================================================
--- expand_number.c    (revision 255017)
+++ expand_number.c    (working copy)
@@ -35,42 +35,29 @@
 #include <libutil.h>
 #include <stdint.h>

-/*
- * Convert an expression of the following forms to a uint64_t.
- *    1) A positive decimal number.
- *    2) A positive decimal number followed by a 'b' or 'B' (mult by 1).
- *    3) A positive decimal number followed by a 'k' or 'K' (mult by 1 << 10).
- *    4) A positive decimal number followed by a 'm' or 'M' (mult by 1 << 20).
- *    5) A positive decimal number followed by a 'g' or 'G' (mult by 1 << 30).
- *    6) A positive decimal number followed by a 't' or 'T' (mult by 1 << 40).
- *    7) A positive decimal number followed by a 'p' or 'P' (mult by 1 << 50).
- *    8) A positive decimal number followed by a 'e' or 'E' (mult by 1 << 60).
- */
 int
 expand_number(const char *buf, uint64_t *num)
 {
+    char *endptr;
+    uintmax_t umaxval;
     uint64_t number;
-    int saved_errno;
     unsigned shift;
-    char *endptr;
+    int serrno;

-    saved_errno = errno;
+    serrno = errno;
     errno = 0;
-
-    number = strtoumax(buf, &endptr, 0);
-
-    if (number == UINTMAX_MAX && errno == ERANGE) {
-        return (-1);
-    }
-
-    if (errno == 0)
-        errno = saved_errno;
-
+    umaxval = strtoumax(buf, &endptr, 0);
     if (endptr == buf) {
         /* No valid digits. */
         errno = EINVAL;
         return (-1);
     }
+    if (umaxval > UINT64_MAX)
+        errno = ERANGE;
+    if (errno != 0)
+        return (-1);
+    errno = serrno;
+    number = umaxval;

     switch (tolower((unsigned char)*endptr)) {
     case 'e':

-- 
wbr,
pluknet



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAE-mSO%2BuzOG2QbhiPbcSwPCUupaMtHvhPc61JEjyb4E-XuHLiw>