Josh Junon <[email protected]> writes:

> Previously, int64_t's were used when parsing virtual / physical
> addresses for the [p]memsave commands over QMP (HMP was unaffected).
> This caused issues when the virtual addresses were in the higher
> half of a 64-bit address space, as various portions of the parsing
> code would consider them out of bounds.
>
> This PR addresses that, creating uint64_t analogs for much of the
> parsing/qdict implementations, as well as the expression parser,
> to allow for higher-half virtual addresses (or any address with a
> MSB=1).

Too many things for a single patch.  Let's ignore that for now.

> ---
>  hmp-commands.hx            |   4 +-
>  hw/core/machine-hmp-cmds.c |   8 +-
>  include/qapi/qmp/qdict.h   |   4 +
>  monitor/hmp-expr.inc       | 200 ++++++++++++++++++++++++++++++
>  monitor/hmp.c              | 241 ++++++++-----------------------------
>  qapi/machine.json          |   4 +-
>  qobject/qdict.c            |  38 ++++++
>  system/cpus.c              |   4 +-
>  tests/unit/check-qdict.c   |  39 ++++++
>  9 files changed, 343 insertions(+), 199 deletions(-)
>  create mode 100644 monitor/hmp-expr.inc
>
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 06746f0afc..9e0fe980b1 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -801,7 +801,7 @@ ERST
>  
>      {
>          .name       = "memsave",
> -        .args_type  = "val:l,size:i,filename:s",
> +        .args_type  = "val:u,size:u,filename:s",
>          .params     = "addr size file",
>          .help       = "save to disk virtual memory dump starting at 'addr' 
> of size 'size'",
>          .cmd        = hmp_memsave,
> @@ -814,7 +814,7 @@ ERST
>  
>      {
>          .name       = "pmemsave",
> -        .args_type  = "val:l,size:i,filename:s",
> +        .args_type  = "val:u,size:u,filename:s",
>          .params     = "addr size file",
>          .help       = "save to disk physical memory dump starting at 'addr' 
> of size 'size'",
>          .cmd        = hmp_pmemsave,

Arg type 'u' is new in this patch, and takes quite some code.  Sure we
want it?  'l' has been good enough for decades...

If yes: more commands use arg type 'l' for addresses: x, xp, gpa2hva,
gpa2hpa, gva2gpa, dump-guest-memory.

> diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
> index 8701f00cc7..10512a034b 100644
> --- a/hw/core/machine-hmp-cmds.c
> +++ b/hw/core/machine-hmp-cmds.c
> @@ -196,9 +196,9 @@ void hmp_system_powerdown(Monitor *mon, const QDict 
> *qdict)
>  
>  void hmp_memsave(Monitor *mon, const QDict *qdict)
>  {
> -    uint32_t size = qdict_get_int(qdict, "size");
> +    uint64_t size = qdict_get_uint(qdict, "size");
>      const char *filename = qdict_get_str(qdict, "filename");
> -    uint64_t addr = qdict_get_int(qdict, "val");
> +    uint64_t addr = qdict_get_uint(qdict, "val");
>      Error *err = NULL;
>      int cpu_index = monitor_get_cpu_index(mon);
>  
> @@ -213,9 +213,9 @@ void hmp_memsave(Monitor *mon, const QDict *qdict)
>  
>  void hmp_pmemsave(Monitor *mon, const QDict *qdict)
>  {
> -    uint32_t size = qdict_get_int(qdict, "size");
> +    uint64_t size = qdict_get_uint(qdict, "size");
>      const char *filename = qdict_get_str(qdict, "filename");
> -    uint64_t addr = qdict_get_int(qdict, "val");
> +    uint64_t addr = qdict_get_uint(qdict, "val");
>      Error *err = NULL;
>  
>      qmp_pmemsave(addr, size, filename, &err);

Compare hmp_memory_dump():

   void hmp_memory_dump(Monitor *mon, const QDict *qdict)
   {
       int count = qdict_get_int(qdict, "count");
       int format = qdict_get_int(qdict, "format");
       int size = qdict_get_int(qdict, "size");
       target_long addr = qdict_get_int(qdict, "addr");

       memory_dump(mon, count, format, size, addr, 0);
   }

   void hmp_physical_memory_dump(Monitor *mon, const QDict *qdict)
   {
       int count = qdict_get_int(qdict, "count");
       int format = qdict_get_int(qdict, "format");
       int size = qdict_get_int(qdict, "size");
       hwaddr addr = qdict_get_int(qdict, "addr");

       memory_dump(mon, count, format, size, addr, 1);
   }

Both convert argument "addr" from signed to unsigned silently

hmp_gpa2hva(), hmp_gva2gpa(), hmp_gpa2hpa() do the same.

hmp_dump_guest_memory() keeps it signed, because the QMP command takes
its address signed, like memsave and pmemsave do before this patch.

> diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
> index 82e90fc072..38c310becb 100644
> --- a/include/qapi/qmp/qdict.h
> +++ b/include/qapi/qmp/qdict.h
> @@ -52,17 +52,21 @@ const QDictEntry *qdict_next(const QDict *qdict, const 
> QDictEntry *entry);
>  
>  void qdict_put_bool(QDict *qdict, const char *key, bool value);
>  void qdict_put_int(QDict *qdict, const char *key, int64_t value);
> +void qdict_put_uint(QDict *qdict, const char *key, uint64_t value);
>  void qdict_put_null(QDict *qdict, const char *key);
>  void qdict_put_str(QDict *qdict, const char *key, const char *value);
>  
>  double qdict_get_double(const QDict *qdict, const char *key);
>  int64_t qdict_get_int(const QDict *qdict, const char *key);
> +uint64_t qdict_get_uint(const QDict *qdict, const char *key);
>  bool qdict_get_bool(const QDict *qdict, const char *key);
>  QList *qdict_get_qlist(const QDict *qdict, const char *key);
>  QDict *qdict_get_qdict(const QDict *qdict, const char *key);
>  const char *qdict_get_str(const QDict *qdict, const char *key);
>  int64_t qdict_get_try_int(const QDict *qdict, const char *key,
>                            int64_t def_value);
> +uint64_t qdict_get_try_uint(const QDict *qdict, const char *key,
> +                          uint64_t def_value);
>  bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value);
>  const char *qdict_get_try_str(const QDict *qdict, const char *key);
>  
> diff --git a/monitor/hmp-expr.inc b/monitor/hmp-expr.inc
> new file mode 100644
> index 0000000000..789a957ed2
> --- /dev/null
> +++ b/monitor/hmp-expr.inc
> @@ -0,0 +1,200 @@
> +#ifndef HMP_EXPR_INC_TY
> +#error "missing HMP_EXPR_INC_TY"
> +#endif
> +
> +#ifndef HMP_EXPR_INC_IDENT
> +#error "missing HMP_EXPR_INC_IDENT"
> +#endif
> +
> +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_sum)(Monitor *mon);
> +
> +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_unary)(Monitor *mon)
> +{
> +    HMP_EXPR_INC_TY n;
> +    char *p;
> +    int ret;
> +
> +    switch (*pch) {
> +    case '+':
> +        next();
> +        n = HMP_EXPR_INC_IDENT(expr_unary)(mon);
> +        break;
> +    case '-':
> +        next();
> +        n = -HMP_EXPR_INC_IDENT(expr_unary)(mon);
> +        break;
> +    case '~':
> +        next();
> +        n = ~HMP_EXPR_INC_IDENT(expr_unary)(mon);
> +        break;
> +    case '(':
> +        next();
> +        n = HMP_EXPR_INC_IDENT(expr_sum)(mon);
> +        if (*pch != ')') {
> +            expr_error(mon, "')' expected");
> +        }
> +        next();
> +        break;
> +    case '\'':
> +        pch++;
> +        if (*pch == '\0') {
> +            expr_error(mon, "character constant expected");
> +        }
> +        n = *pch;
> +        pch++;
> +        if (*pch != '\'') {
> +            expr_error(mon, "missing terminating \' character");
> +        }
> +        next();
> +        break;
> +    case '$':
> +        {
> +            char buf[128], *q;
> +            int64_t reg = 0;
> +
> +            pch++;
> +            q = buf;
> +            while ((*pch >= 'a' && *pch <= 'z') ||
> +                   (*pch >= 'A' && *pch <= 'Z') ||
> +                   (*pch >= '0' && *pch <= '9') ||
> +                   *pch == '_' || *pch == '.') {
> +                if ((q - buf) < sizeof(buf) - 1) {
> +                    *q++ = *pch;
> +                }
> +                pch++;
> +            }
> +            while (qemu_isspace(*pch)) {
> +                pch++;
> +            }
> +            *q = 0;
> +            ret = get_monitor_def(mon, &reg, buf);
> +            if (ret < 0) {
> +                expr_error(mon, "unknown register");
> +            }
> +            n = (HMP_EXPR_INC_TY)reg;
> +        }
> +        break;
> +    case '\0':
> +        expr_error(mon, "unexpected end of expression");
> +        n = 0;
> +        break;
> +    default:
> +        errno = 0;
> +        n = strtoull(pch, &p, 0);
> +        if (errno == ERANGE) {
> +            expr_error(mon, "number too large");
> +        }
> +        if (pch == p) {
> +            expr_error(mon, "invalid char '%c' in expression", *p);
> +        }
> +        pch = p;
> +        while (qemu_isspace(*pch)) {
> +            pch++;
> +        }
> +        break;
> +    }
> +    return n;
> +}
> +
> +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_prod)(Monitor *mon)
> +{
> +    HMP_EXPR_INC_TY val, val2;
> +    int op;
> +
> +    val = HMP_EXPR_INC_IDENT(expr_unary)(mon);
> +    for (;;) {
> +        op = *pch;
> +        if (op != '*' && op != '/' && op != '%') {
> +            break;
> +        }
> +        next();
> +        val2 = HMP_EXPR_INC_IDENT(expr_unary)(mon);
> +        switch (op) {
> +        default:
> +        case '*':
> +            val *= val2;
> +            break;
> +        case '/':
> +        case '%':
> +            if (val2 == 0) {
> +                expr_error(mon, "division by zero");
> +            }
> +            if (op == '/') {
> +                val /= val2;
> +            } else {
> +                val %= val2;
> +            }
> +            break;
> +        }
> +    }
> +    return val;
> +}
> +
> +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_logic)(Monitor *mon)
> +{
> +    HMP_EXPR_INC_TY val, val2;
> +    int op;
> +
> +    val = HMP_EXPR_INC_IDENT(expr_prod)(mon);
> +    for (;;) {
> +        op = *pch;
> +        if (op != '&' && op != '|' && op != '^') {
> +            break;
> +        }
> +        next();
> +        val2 = HMP_EXPR_INC_IDENT(expr_prod)(mon);
> +        switch (op) {
> +        default:
> +        case '&':
> +            val &= val2;
> +            break;
> +        case '|':
> +            val |= val2;
> +            break;
> +        case '^':
> +            val ^= val2;
> +            break;
> +        }
> +    }
> +    return val;
> +}
> +
> +static HMP_EXPR_INC_TY HMP_EXPR_INC_IDENT(expr_sum)(Monitor *mon)
> +{
> +    HMP_EXPR_INC_TY val, val2;
> +    int op;
> +
> +    val = HMP_EXPR_INC_IDENT(expr_logic)(mon);
> +    for (;;) {
> +        op = *pch;
> +        if (op != '+' && op != '-') {
> +            break;
> +        }
> +        next();
> +        val2 = HMP_EXPR_INC_IDENT(expr_logic)(mon);
> +        if (op == '+') {
> +            val += val2;
> +        } else {
> +            val -= val2;
> +        }
> +    }
> +    return val;
> +}
> +
> +static int HMP_EXPR_INC_IDENT(get_expr)(Monitor *mon, HMP_EXPR_INC_TY *pval, 
> const char **pp)
> +{
> +    pch = *pp;
> +    if (sigsetjmp(expr_env, 0)) {
> +        *pp = pch;
> +        return -1;
> +    }
> +    while (qemu_isspace(*pch)) {
> +        pch++;
> +    }
> +    *pval = HMP_EXPR_INC_IDENT(expr_sum)(mon);
> +    *pp = pch;
> +    return 0;
> +}
> +
> +#undef HMP_EXPR_INC_TY
> +#undef HMP_EXPR_INC_IDENT
> diff --git a/monitor/hmp.c b/monitor/hmp.c
> index 460e8832f6..95d965a20a 100644
> --- a/monitor/hmp.c
> +++ b/monitor/hmp.c
> @@ -332,195 +332,13 @@ static void next(void)
>      }
>  }
>  
> -static int64_t expr_sum(Monitor *mon);
> +#define HMP_EXPR_INC_TY int64_t
> +#define HMP_EXPR_INC_IDENT(name) name ## _int64
> +#include "monitor/hmp-expr.inc"
>  
> -static int64_t expr_unary(Monitor *mon)
> -{
> -    int64_t n;
> -    char *p;
> -    int ret;
> -
> -    switch (*pch) {
> -    case '+':
> -        next();
> -        n = expr_unary(mon);
> -        break;
> -    case '-':
> -        next();
> -        n = -expr_unary(mon);
> -        break;
> -    case '~':
> -        next();
> -        n = ~expr_unary(mon);
> -        break;
> -    case '(':
> -        next();
> -        n = expr_sum(mon);
> -        if (*pch != ')') {
> -            expr_error(mon, "')' expected");
> -        }
> -        next();
> -        break;
> -    case '\'':
> -        pch++;
> -        if (*pch == '\0') {
> -            expr_error(mon, "character constant expected");
> -        }
> -        n = *pch;
> -        pch++;
> -        if (*pch != '\'') {
> -            expr_error(mon, "missing terminating \' character");
> -        }
> -        next();
> -        break;
> -    case '$':
> -        {
> -            char buf[128], *q;
> -            int64_t reg = 0;
> -
> -            pch++;
> -            q = buf;
> -            while ((*pch >= 'a' && *pch <= 'z') ||
> -                   (*pch >= 'A' && *pch <= 'Z') ||
> -                   (*pch >= '0' && *pch <= '9') ||
> -                   *pch == '_' || *pch == '.') {
> -                if ((q - buf) < sizeof(buf) - 1) {
> -                    *q++ = *pch;
> -                }
> -                pch++;
> -            }
> -            while (qemu_isspace(*pch)) {
> -                pch++;
> -            }
> -            *q = 0;
> -            ret = get_monitor_def(mon, &reg, buf);
> -            if (ret < 0) {
> -                expr_error(mon, "unknown register");
> -            }
> -            n = reg;
> -        }
> -        break;
> -    case '\0':
> -        expr_error(mon, "unexpected end of expression");
> -        n = 0;
> -        break;
> -    default:
> -        errno = 0;
> -        n = strtoull(pch, &p, 0);
> -        if (errno == ERANGE) {
> -            expr_error(mon, "number too large");
> -        }
> -        if (pch == p) {
> -            expr_error(mon, "invalid char '%c' in expression", *p);
> -        }
> -        pch = p;
> -        while (qemu_isspace(*pch)) {
> -            pch++;
> -        }
> -        break;
> -    }
> -    return n;
> -}
> -
> -static int64_t expr_prod(Monitor *mon)
> -{
> -    int64_t val, val2;
> -    int op;
> -
> -    val = expr_unary(mon);
> -    for (;;) {
> -        op = *pch;
> -        if (op != '*' && op != '/' && op != '%') {
> -            break;
> -        }
> -        next();
> -        val2 = expr_unary(mon);
> -        switch (op) {
> -        default:
> -        case '*':
> -            val *= val2;
> -            break;
> -        case '/':
> -        case '%':
> -            if (val2 == 0) {
> -                expr_error(mon, "division by zero");
> -            }
> -            if (op == '/') {
> -                val /= val2;
> -            } else {
> -                val %= val2;
> -            }
> -            break;
> -        }
> -    }
> -    return val;
> -}
> -
> -static int64_t expr_logic(Monitor *mon)
> -{
> -    int64_t val, val2;
> -    int op;
> -
> -    val = expr_prod(mon);
> -    for (;;) {
> -        op = *pch;
> -        if (op != '&' && op != '|' && op != '^') {
> -            break;
> -        }
> -        next();
> -        val2 = expr_prod(mon);
> -        switch (op) {
> -        default:
> -        case '&':
> -            val &= val2;
> -            break;
> -        case '|':
> -            val |= val2;
> -            break;
> -        case '^':
> -            val ^= val2;
> -            break;
> -        }
> -    }
> -    return val;
> -}
> -
> -static int64_t expr_sum(Monitor *mon)
> -{
> -    int64_t val, val2;
> -    int op;
> -
> -    val = expr_logic(mon);
> -    for (;;) {
> -        op = *pch;
> -        if (op != '+' && op != '-') {
> -            break;
> -        }
> -        next();
> -        val2 = expr_logic(mon);
> -        if (op == '+') {
> -            val += val2;
> -        } else {
> -            val -= val2;
> -        }
> -    }
> -    return val;
> -}
> -
> -static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
> -{
> -    pch = *pp;
> -    if (sigsetjmp(expr_env, 0)) {
> -        *pp = pch;
> -        return -1;
> -    }
> -    while (qemu_isspace(*pch)) {
> -        pch++;
> -    }
> -    *pval = expr_sum(mon);
> -    *pp = pch;
> -    return 0;
> -}
> +#define HMP_EXPR_INC_TY uint64_t
> +#define HMP_EXPR_INC_IDENT(name) name ## _uint64
> +#include "monitor/hmp-expr.inc"

You arrange to have the silly integrated desk calculator compiled twice,
once for signed and once for unsigned.  Meh.

>  
>  static int get_double(Monitor *mon, double *pval, const char **pp)
>  {
> @@ -882,7 +700,7 @@ static QDict *monitor_parse_arguments(Monitor *mon,
>                      }
>                      typestr++;
>                  }
> -                if (get_expr(mon, &val, &p)) {
> +                if (get_expr_int64(mon, &val, &p)) {
>                      goto fail;
>                  }
>                  /* Check if 'i' is greater than 32-bit */
> @@ -900,6 +718,51 @@ static QDict *monitor_parse_arguments(Monitor *mon,
>                  qdict_put_int(qdict, key, val);
>              }
>              break;
> +        case 'd':
> +        case 'u':
> +        case 'm':
> +            {
> +                uint64_t val;
> +
> +                while (qemu_isspace(*p)) {
> +                    p++;
> +                }
> +                if (*typestr == '?' || *typestr == '.') {
> +                    if (*typestr == '?') {
> +                        if (*p == '\0') {
> +                            typestr++;
> +                            break;
> +                        }
> +                    } else {
> +                        if (*p == '.') {
> +                            p++;
> +                            while (qemu_isspace(*p)) {
> +                                p++;
> +                            }
> +                        } else {
> +                            typestr++;
> +                            break;
> +                        }
> +                    }
> +                    typestr++;
> +                }
> +
> +                if (get_expr_uint64(mon, &val, &p)) {
> +                    goto fail;
> +                }
> +
> +                /* Check if 'd' is greater than 32-bit */
> +                if ((c == 'd') && ((val >> 32) & 0xffffffff)) {
> +                    monitor_printf(mon, "\'%s\' has failed: ", cmd->name);
> +                    monitor_printf(mon, "integer is for 32-bit values\n");
> +                    goto fail;
> +                } else if (c == 'm') {
> +                    val *= MiB;
> +                }
> +
> +                qdict_put_uint(qdict, key, val);
> +            }
> +            break;

You duplicate the code for signed arg types 'i', l', and 'M' for the new
unsigned arg types 'd', 'u', and 'm'.

Sure we want these?

If yes: add them to the arg type comment in monitor-internal.h

>          case 'o':
>              {
>                  int ret;
> diff --git a/qapi/machine.json b/qapi/machine.json
> index fcfd249e2d..90f4ecbfd0 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -852,7 +852,7 @@
>  #     <- { "return": {} }
>  ##
>  { 'command': 'memsave',
> -  'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 
> 'int'} }
> +  'data': {'val': 'uint64', 'size': 'uint64', 'filename': 'str', 
> '*cpu-index': 'int'} }

Let's use type 'size' for member 'size'.

PLease use the opportunity to wrap this line.

>  
>  ##
>  # @pmemsave:
> @@ -878,7 +878,7 @@
>  #     <- { "return": {} }
>  ##
>  { 'command': 'pmemsave',
> -  'data': {'val': 'int', 'size': 'int', 'filename': 'str'} }
> +  'data': {'val': 'uint64', 'size': 'uint64', 'filename': 'str'} }

Let's use type 'size' for member 'size'.

>  ##
>  # @Memdev:

Similarly broken: dump-guest-memory.

> diff --git a/qobject/qdict.c b/qobject/qdict.c
> index 8faff230d3..9696eee57d 100644
> --- a/qobject/qdict.c
> +++ b/qobject/qdict.c
> @@ -136,6 +136,11 @@ void qdict_put_int(QDict *qdict, const char *key, 
> int64_t value)
>      qdict_put(qdict, key, qnum_from_int(value));
>  }
>  
> +void qdict_put_uint(QDict *qdict, const char *key, uint64_t value)
> +{
> +    qdict_put(qdict, key, qnum_from_uint(value));
> +}
> +
>  void qdict_put_bool(QDict *qdict, const char *key, bool value)
>  {
>      qdict_put(qdict, key, qbool_from_bool(value));
> @@ -209,6 +214,19 @@ int64_t qdict_get_int(const QDict *qdict, const char 
> *key)
>      return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key)));
>  }
>  
> +/**
> + * qdict_get_int(): Get an unsigned integer mapped by 'key'

Pasto.

> + *
> + * This function assumes that 'key' exists and it stores a
> + * QNum representable as uint.
> + *
> + * Return integer mapped by 'key'.
> + */
> +uint64_t qdict_get_uint(const QDict *qdict, const char *key)
> +{
> +    return qnum_get_uint(qobject_to(QNum, qdict_get(qdict, key)));
> +}
> +
>  /**
>   * qdict_get_bool(): Get a bool mapped by 'key'
>   *
> @@ -272,6 +290,26 @@ int64_t qdict_get_try_int(const QDict *qdict, const char 
> *key,
>      return val;
>  }
>  
> +/**
> + * qdict_get_try_uint(): Try to get unsigned integer mapped by 'key'
> + *
> + * Return integer mapped by 'key', if it is not present in the
> + * dictionary or if the stored object is not a QNum representing an
> + * unsigned integer, 'def_value' will be returned.
> + */
> +uint64_t qdict_get_try_uint(const QDict *qdict, const char *key,
> +                          uint64_t def_value)

Indentation is off.

> +{
> +    QNum *qnum = qobject_to(QNum, qdict_get(qdict, key));
> +    uint64_t val;
> +
> +    if (!qnum || !qnum_get_try_uint(qnum, &val)) {
> +        return def_value;
> +    }
> +
> +    return val;
> +}
> +
>  /**
>   * qdict_get_try_bool(): Try to get a bool mapped by 'key'
>   *

[...]

I'd go for a much smaller solution: change the QMP commands to unsigned,
keep the HMP commands signed, with a silent conversion just like
hmp_memory_dump() & friends.


Reply via email to