diff mbox

[1/2] hmp: expr_unary(): check for overflow in strtoul()

Message ID 1335474606-22889-2-git-send-email-lcapitulino@redhat.com
State New
Headers show

Commit Message

Luiz Capitulino April 26, 2012, 9:10 p.m. UTC
It's not checked currently, so something like:

  (qemu) balloon -100000000000001111114334234
  (qemu)

Will just "work" (in this case the balloon command will get a random
value).

Fix it by checking if strtout() overflowed.

Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
---
 monitor.c |    6 ++++++
 1 file changed, 6 insertions(+)

Comments

Peter Maydell April 26, 2012, 9:30 p.m. UTC | #1
On 26 April 2012 22:10, Luiz Capitulino <lcapitulino@redhat.com> wrote:
> It's not checked currently, so something like:
>
>  (qemu) balloon -100000000000001111114334234
>  (qemu)
>
> Will just "work" (in this case the balloon command will get a random
> value).
>
> Fix it by checking if strtout() overflowed.

"strtoul()/strtoull()".

> Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
> ---
>  monitor.c |    6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/monitor.c b/monitor.c
> index 8946a10..6178f48 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -3122,8 +3122,14 @@ static int64_t expr_unary(Monitor *mon)
>     default:
>  #if TARGET_PHYS_ADDR_BITS > 32
>         n = strtoull(pch, &p, 0);
> +        if (n == ULLONG_MAX && errno == ERANGE) {
> +            expr_error(mon, "error: too long number");
> +        }
>  #else
>         n = strtoul(pch, &p, 0);
> +        if (n == ULONG_MAX && errno == ERANGE) {
> +            expr_error(mon, "error: too long number");
> +        }
>  #endif
>         if (pch == p) {
>             expr_error(mon, "invalid char in expression");

None of the other expr_error()s here say "error: ", so
for consistency this one shouldn't either.
Also, perhaps "number out of range" or "number too large"
would sound better?

(Incidentally I was surprised that this code cared about
TARGET_PHYS_ADDR_BITS...)

-- PMM
Eric Blake April 26, 2012, 9:34 p.m. UTC | #2
On 04/26/2012 03:10 PM, Luiz Capitulino wrote:
> It's not checked currently, so something like:
> 
>   (qemu) balloon -100000000000001111114334234
>   (qemu)
> 
> Will just "work" (in this case the balloon command will get a random
> value).
> 
> Fix it by checking if strtout() overflowed.
> 
> Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
> ---
>  monitor.c |    6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/monitor.c b/monitor.c
> index 8946a10..6178f48 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -3122,8 +3122,14 @@ static int64_t expr_unary(Monitor *mon)
>      default:
>  #if TARGET_PHYS_ADDR_BITS > 32
>          n = strtoull(pch, &p, 0);

strtoull and friends are evil.  That's why libvirt has a rule that all
string-to-integer conversion must go through a libvirt wrapper call, and
_only_ the wrapper may use strtoull (since that one use has been audited
for correctness).

> +        if (n == ULLONG_MAX && errno == ERANGE) {

Not quite right.  ULLONG_MAX is a valid return, but you did not prime
errno, so if errno has junk ERANGE from some earlier point in the
program, you will have a false negative.  You are guaranteed that errno
is unchanged on success, so prime things by setting errno to 0 before
calling strtoull.

> +            expr_error(mon, "error: too long number");

Grammar - this reads better as 'number too long'.

> +        }
>  #else
>          n = strtoul(pch, &p, 0);
> +        if (n == ULONG_MAX && errno == ERANGE) {
> +            expr_error(mon, "error: too long number");
> +        }

Same bug, repeated.

>  #endif
>          if (pch == p) {
>              expr_error(mon, "invalid char in expression");
Luiz Capitulino April 26, 2012, 11:14 p.m. UTC | #3
On Thu, 26 Apr 2012 22:30:20 +0100
Peter Maydell <peter.maydell@linaro.org> wrote:

> On 26 April 2012 22:10, Luiz Capitulino <lcapitulino@redhat.com> wrote:
> > It's not checked currently, so something like:
> >
> >  (qemu) balloon -100000000000001111114334234
> >  (qemu)
> >
> > Will just "work" (in this case the balloon command will get a random
> > value).
> >
> > Fix it by checking if strtout() overflowed.
> 
> "strtoul()/strtoull()".

Ah, thanks.

> 
> > Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
> > ---
> >  monitor.c |    6 ++++++
> >  1 file changed, 6 insertions(+)
> >
> > diff --git a/monitor.c b/monitor.c
> > index 8946a10..6178f48 100644
> > --- a/monitor.c
> > +++ b/monitor.c
> > @@ -3122,8 +3122,14 @@ static int64_t expr_unary(Monitor *mon)
> >     default:
> >  #if TARGET_PHYS_ADDR_BITS > 32
> >         n = strtoull(pch, &p, 0);
> > +        if (n == ULLONG_MAX && errno == ERANGE) {
> > +            expr_error(mon, "error: too long number");
> > +        }
> >  #else
> >         n = strtoul(pch, &p, 0);
> > +        if (n == ULONG_MAX && errno == ERANGE) {
> > +            expr_error(mon, "error: too long number");
> > +        }
> >  #endif
> >         if (pch == p) {
> >             expr_error(mon, "invalid char in expression");
> 
> None of the other expr_error()s here say "error: ", so
> for consistency this one shouldn't either.
> Also, perhaps "number out of range" or "number too large"
> would sound better?

Agree, will make the change.
Luiz Capitulino April 26, 2012, 11:18 p.m. UTC | #4
On Thu, 26 Apr 2012 15:34:50 -0600
Eric Blake <eblake@redhat.com> wrote:

> On 04/26/2012 03:10 PM, Luiz Capitulino wrote:
> > It's not checked currently, so something like:
> > 
> >   (qemu) balloon -100000000000001111114334234
> >   (qemu)
> > 
> > Will just "work" (in this case the balloon command will get a random
> > value).
> > 
> > Fix it by checking if strtout() overflowed.
> > 
> > Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
> > ---
> >  monitor.c |    6 ++++++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/monitor.c b/monitor.c
> > index 8946a10..6178f48 100644
> > --- a/monitor.c
> > +++ b/monitor.c
> > @@ -3122,8 +3122,14 @@ static int64_t expr_unary(Monitor *mon)
> >      default:
> >  #if TARGET_PHYS_ADDR_BITS > 32
> >          n = strtoull(pch, &p, 0);
> 
> strtoull and friends are evil.  That's why libvirt has a rule that all
> string-to-integer conversion must go through a libvirt wrapper call, and
> _only_ the wrapper may use strtoull (since that one use has been audited
> for correctness).

We could add a wrapper too, but I'll only fix the bug for now.

> 
> > +        if (n == ULLONG_MAX && errno == ERANGE) {
> 
> Not quite right.  ULLONG_MAX is a valid return, but you did not prime
> errno, so if errno has junk ERANGE from some earlier point in the
> program, you will have a false negative.  You are guaranteed that errno
> is unchanged on success, so prime things by setting errno to 0 before
> calling strtoull.

I thought about doing that but - hope that won't sound stupid - how can I
guarantee that ERANGE is not 0?

> > +            expr_error(mon, "error: too long number");
> 
> Grammar - this reads better as 'number too long'.

Yes, Peter suggested something similar. Will change that.
Luiz Capitulino April 26, 2012, 11:19 p.m. UTC | #5
On Thu, 26 Apr 2012 20:18:02 -0300
Luiz Capitulino <lcapitulino@redhat.com> wrote:

> > Not quite right.  ULLONG_MAX is a valid return, but you did not prime
> > errno, so if errno has junk ERANGE from some earlier point in the
> > program, you will have a false negative.  You are guaranteed that errno
> > is unchanged on success, so prime things by setting errno to 0 before
> > calling strtoull.
> 
> I thought about doing that but - hope that won't sound stupid - how can I
> guarantee that ERANGE is not 0?

Oh, that _was_ stupid. errno=0 is actually recommended in the documentation,
will change.
Luiz Capitulino April 26, 2012, 11:29 p.m. UTC | #6
On Thu, 26 Apr 2012 20:19:42 -0300
Luiz Capitulino <lcapitulino@redhat.com> wrote:

> On Thu, 26 Apr 2012 20:18:02 -0300
> Luiz Capitulino <lcapitulino@redhat.com> wrote:
> 
> > > Not quite right.  ULLONG_MAX is a valid return, but you did not prime
> > > errno, so if errno has junk ERANGE from some earlier point in the
> > > program, you will have a false negative.  You are guaranteed that errno
> > > is unchanged on success, so prime things by setting errno to 0 before
> > > calling strtoull.
> > 
> > I thought about doing that but - hope that won't sound stupid - how can I
> > guarantee that ERANGE is not 0?
> 
> Oh, that _was_ stupid. errno=0 is actually recommended in the documentation,
> will change.

Oh, and now I read that you wrote just that above. Dude, it's not a good idea
to answer emails while waiting for your food to come...
Eric Blake April 27, 2012, 2:07 p.m. UTC | #7
On 04/26/2012 05:18 PM, Luiz Capitulino wrote:
>> Not quite right.  ULLONG_MAX is a valid return, but you did not prime
>> errno, so if errno has junk ERANGE from some earlier point in the
>> program, you will have a false negative.  You are guaranteed that errno
>> is unchanged on success, so prime things by setting errno to 0 before
>> calling strtoull.
> 
> I thought about doing that but - hope that won't sound stupid - how can I
> guarantee that ERANGE is not 0?

Because POSIX requires that all E* constants are positive numbers,
reserving errno of 0 for success.
Luiz Capitulino April 27, 2012, 2:10 p.m. UTC | #8
On Fri, 27 Apr 2012 08:07:20 -0600
Eric Blake <eblake@redhat.com> wrote:

> On 04/26/2012 05:18 PM, Luiz Capitulino wrote:
> >> Not quite right.  ULLONG_MAX is a valid return, but you did not prime
> >> errno, so if errno has junk ERANGE from some earlier point in the
> >> program, you will have a false negative.  You are guaranteed that errno
> >> is unchanged on success, so prime things by setting errno to 0 before
> >> calling strtoull.
> > 
> > I thought about doing that but - hope that won't sound stupid - how can I
> > guarantee that ERANGE is not 0?
> 
> Because POSIX requires that all E* constants are positive numbers,
> reserving errno of 0 for success.

Yeah, I read about that a few hours later. But thanks anyway!
diff mbox

Patch

diff --git a/monitor.c b/monitor.c
index 8946a10..6178f48 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3122,8 +3122,14 @@  static int64_t expr_unary(Monitor *mon)
     default:
 #if TARGET_PHYS_ADDR_BITS > 32
         n = strtoull(pch, &p, 0);
+        if (n == ULLONG_MAX && errno == ERANGE) {
+            expr_error(mon, "error: too long number");
+        }
 #else
         n = strtoul(pch, &p, 0);
+        if (n == ULONG_MAX && errno == ERANGE) {
+            expr_error(mon, "error: too long number");
+        }
 #endif
         if (pch == p) {
             expr_error(mon, "invalid char in expression");