diff mbox series

[v2,7/8] thunk: supports flexible arrays

Message ID e0754f52180aee6418eae8b3b8aa5981fcac12fd.1597129029.git.scw@google.com (mailing list archive)
State New, archived
Headers show
Series fcntl, sockopt, and ioctl options | expand

Commit Message

Shu-Chun Weng Aug. 11, 2020, 7:09 a.m. UTC
Flexible arrays may appear in the last field of a struct and are heavily
used in the ioctl(SIOCETHTOOL) system call on Linux. E.g.

  struct ethtool_regs {
      __u32   cmd;
      __u32   version; /* driver-specific, indicates different chips/revs */
      __u32   len; /* bytes */
      __u8    data[0];
  };

where number of elements in `data` is specified in `len`. It is translated
into:

  STRUCT(ethtool_regs,
         TYPE_INT, /* cmd */
         TYPE_INT, /* version */
         TYPE_INT, /* len */
         MK_FLEXIBLE_ARRAY(TYPE_CHAR, 2)) /* data[0]: len */

where the "2" passed to `MK_FLEXIBLE_ARRAY` means the number of element
is specified by field number 2 (0-index).

Signed-off-by: Shu-Chun Weng <scw@google.com>
---
v1 -> v2:
  Fix style problems.

 include/exec/user/thunk.h |  24 ++++++
 thunk.c                   | 152 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 174 insertions(+), 2 deletions(-)

Comments

Shu-Chun Weng Aug. 11, 2020, 9:39 p.m. UTC | #1
Forgot to +riku.voipio@iki.fi when generating v2.

On Tue, Aug 11, 2020 at 12:10 AM Shu-Chun Weng <scw@google.com> wrote:

> Flexible arrays may appear in the last field of a struct and are heavily
> used in the ioctl(SIOCETHTOOL) system call on Linux. E.g.
>
>   struct ethtool_regs {
>       __u32   cmd;
>       __u32   version; /* driver-specific, indicates different chips/revs
> */
>       __u32   len; /* bytes */
>       __u8    data[0];
>   };
>
> where number of elements in `data` is specified in `len`. It is translated
> into:
>
>   STRUCT(ethtool_regs,
>          TYPE_INT, /* cmd */
>          TYPE_INT, /* version */
>          TYPE_INT, /* len */
>          MK_FLEXIBLE_ARRAY(TYPE_CHAR, 2)) /* data[0]: len */
>
> where the "2" passed to `MK_FLEXIBLE_ARRAY` means the number of element
> is specified by field number 2 (0-index).
>
> Signed-off-by: Shu-Chun Weng <scw@google.com>
> ---
> v1 -> v2:
>   Fix style problems.
>
>  include/exec/user/thunk.h |  24 ++++++
>  thunk.c                   | 152 +++++++++++++++++++++++++++++++++++++-
>  2 files changed, 174 insertions(+), 2 deletions(-)
>
> diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h
> index 7992475c9f..d0d7c83f1f 100644
> --- a/include/exec/user/thunk.h
> +++ b/include/exec/user/thunk.h
> @@ -39,12 +39,21 @@ typedef enum argtype {
>      TYPE_ARRAY,
>      TYPE_STRUCT,
>      TYPE_OLDDEVT,
> +    TYPE_FLEXIBLE_ARRAY,
>  } argtype;
>
>  #define MK_PTR(type) TYPE_PTR, type
>  #define MK_ARRAY(type, size) TYPE_ARRAY, size, type
>  #define MK_STRUCT(id) TYPE_STRUCT, id
>
> +/*
> + * Should only appear as the last element of a TYPE_STRUCT.
> `len_field_idx` is
> + * the index into the fields in the enclosing struct that specify the
> length of
> + * the flexibly array. The length field MUST be a TYPE_INT field.
> + */
> +#define MK_FLEXIBLE_ARRAY(type, len_field_idx) \
> +    TYPE_FLEXIBLE_ARRAY, (len_field_idx), type
> +
>  #define THUNK_TARGET 0
>  #define THUNK_HOST   1
>
> @@ -55,6 +64,8 @@ typedef struct {
>      int *field_offsets[2];
>      /* special handling */
>      void (*convert[2])(void *dst, const void *src);
> +    int (*thunk_size[2])(const void *src);
> +
>      int size[2];
>      int align[2];
>      const char *name;
> @@ -75,6 +86,11 @@ const argtype *thunk_convert(void *dst, const void *src,
>                               const argtype *type_ptr, int to_host);
>  const argtype *thunk_print(void *arg, const argtype *type_ptr);
>
> +bool thunk_type_has_flexible_array(const argtype *type_ptr);
> +/* thunk_type_size but can handle TYPE_FLEXIBLE_ARRAY */
> +int thunk_type_size_with_src(const void *src, const argtype *type_ptr,
> +                             int is_host);
> +
>  extern StructEntry *struct_entries;
>
>  int thunk_type_size_array(const argtype *type_ptr, int is_host);
> @@ -137,6 +153,12 @@ static inline int thunk_type_size(const argtype
> *type_ptr, int is_host)
>      case TYPE_STRUCT:
>          se = struct_entries + type_ptr[1];
>          return se->size[is_host];
> +    case TYPE_FLEXIBLE_ARRAY:
> +        /*
> +         * Flexible arrays do not count toward sizeof(). Users of
> structures
> +         * containing them need to calculate it themselves.
> +         */
> +        return 0;
>      default:
>          g_assert_not_reached();
>      }
> @@ -187,6 +209,8 @@ static inline int thunk_type_align(const argtype
> *type_ptr, int is_host)
>      case TYPE_STRUCT:
>          se = struct_entries + type_ptr[1];
>          return se->align[is_host];
> +    case TYPE_FLEXIBLE_ARRAY:
> +        return thunk_type_align_array(type_ptr + 2, is_host);
>      default:
>          g_assert_not_reached();
>      }
> diff --git a/thunk.c b/thunk.c
> index c5d9719747..d9c6cba3bd 100644
> --- a/thunk.c
> +++ b/thunk.c
> @@ -50,6 +50,8 @@ static inline const argtype *thunk_type_next(const
> argtype *type_ptr)
>          return thunk_type_next_ptr(type_ptr + 1);
>      case TYPE_STRUCT:
>          return type_ptr + 1;
> +    case TYPE_FLEXIBLE_ARRAY:
> +        return thunk_type_next_ptr(type_ptr + 1);
>      default:
>          return NULL;
>      }
> @@ -122,6 +124,34 @@ void thunk_register_struct_direct(int id, const char
> *name,
>      se->name = name;
>  }
>
> +static const argtype *
> +thunk_convert_flexible_array(void *dst, const void *src,
> +                             const uint8_t *dst_struct,
> +                             const uint8_t *src_struct, const argtype
> *type_ptr,
> +                             const StructEntry *se, int to_host) {
> +    int len_field_idx, dst_size, src_size, i;
> +    uint32_t array_length;
> +    uint8_t *d;
> +    const uint8_t *s;
> +
> +    assert(*type_ptr == TYPE_FLEXIBLE_ARRAY);
> +    type_ptr++;
> +    len_field_idx = *type_ptr++;
> +    array_length =
> +        *(const uint32_t *)(to_host ?
> +                            dst_struct +
> se->field_offsets[1][len_field_idx] :
> +                            src_struct +
> se->field_offsets[0][len_field_idx]);
> +    dst_size = thunk_type_size(type_ptr, to_host);
> +    src_size = thunk_type_size(type_ptr, to_host);
> +    d = dst;
> +    s = src;
> +    for (i = 0; i < array_length; i++) {
> +        thunk_convert(d, s, type_ptr, to_host);
> +        d += dst_size;
> +        s += src_size;
> +    }
> +    return thunk_type_next(type_ptr);
> +}
>
>  /* now we can define the main conversion functions */
>  const argtype *thunk_convert(void *dst, const void *src,
> @@ -246,7 +276,7 @@ const argtype *thunk_convert(void *dst, const void
> *src,
>
>              assert(*type_ptr < max_struct_entries);
>              se = struct_entries + *type_ptr++;
> -            if (se->convert[0] != NULL) {
> +            if (se->convert[to_host] != NULL) {
>                  /* specific conversion is needed */
>                  (*se->convert[to_host])(dst, src);
>              } else {
> @@ -256,7 +286,18 @@ const argtype *thunk_convert(void *dst, const void
> *src,
>                  src_offsets = se->field_offsets[1 - to_host];
>                  d = dst;
>                  s = src;
> -                for(i = 0;i < se->nb_fields; i++) {
> +                for (i = 0; i < se->nb_fields; i++) {
> +                    if (*field_types == TYPE_FLEXIBLE_ARRAY) {
> +                        field_types = thunk_convert_flexible_array(
> +                            d + dst_offsets[i],
> +                            s + src_offsets[i],
> +                            d,
> +                            s,
> +                            field_types,
> +                            se,
> +                            to_host);
> +                        continue;
> +                    }
>                      field_types = thunk_convert(d + dst_offsets[i],
>                                                  s + src_offsets[i],
>                                                  field_types, to_host);
> @@ -264,6 +305,11 @@ const argtype *thunk_convert(void *dst, const void
> *src,
>              }
>          }
>          break;
> +    case TYPE_FLEXIBLE_ARRAY:
> +        fprintf(stderr,
> +                "Invalid flexible array (type 0x%x) outside of a
> structure\n",
> +                type);
> +        break;
>      default:
>          fprintf(stderr, "Invalid type 0x%x\n", type);
>          break;
> @@ -271,6 +317,45 @@ const argtype *thunk_convert(void *dst, const void
> *src,
>      return type_ptr;
>  }
>
> +static const argtype *
> +thunk_print_flexible_array(void *arg, const uint8_t *arg_struct,
> +                           const argtype *type_ptr, const StructEntry
> *se) {
> +    int array_length, len_field_idx, arg_size, i;
> +    uint8_t *a;
> +    int is_string = 0;
> +
> +    assert(*type_ptr == TYPE_FLEXIBLE_ARRAY);
> +    type_ptr++;
> +    len_field_idx = *type_ptr++;
> +
> +    array_length = tswap32(
> +        *(const uint32_t *)(arg_struct +
> se->field_offsets[0][len_field_idx]));
> +    arg_size = thunk_type_size(type_ptr, 0);
> +    a = arg;
> +
> +    if (*type_ptr == TYPE_CHAR) {
> +        qemu_log("\"");
> +        is_string = 1;
> +    } else {
> +        qemu_log("[");
> +    }
> +
> +    for (i = 0; i < array_length; i++) {
> +        if (i > 0 && !is_string) {
> +            qemu_log(",");
> +        }
> +        thunk_print(a, type_ptr);
> +        a += arg_size;
> +    }
> +
> +    if (is_string) {
> +        qemu_log("\"");
> +    } else {
> +        qemu_log("]");
> +    }
> +    return thunk_type_next(type_ptr);
> +}
> +
>  const argtype *thunk_print(void *arg, const argtype *type_ptr)
>  {
>      int type;
> @@ -414,17 +499,80 @@ const argtype *thunk_print(void *arg, const argtype
> *type_ptr)
>                  if (i > 0) {
>                      qemu_log(",");
>                  }
> +                if (*field_types == TYPE_FLEXIBLE_ARRAY) {
> +                    field_types = thunk_print_flexible_array(
> +                        a + arg_offsets[i], a, field_types, se);
> +                    continue;
> +                }
>                  field_types = thunk_print(a + arg_offsets[i],
> field_types);
>              }
>              qemu_log("}");
>          }
>          break;
> +    case TYPE_FLEXIBLE_ARRAY:
> +        fprintf(stderr,
> +                "Invalid flexible array (type 0x%x) outside of a
> structure\n",
> +                type);
> +        break;
>      default:
>          g_assert_not_reached();
>      }
>      return type_ptr;
>  }
>
> +bool thunk_type_has_flexible_array(const argtype *type_ptr)
> +{
> +  int i;
> +  const StructEntry *se;
> +  const argtype *field_types;
> +    if (*type_ptr != TYPE_STRUCT) {
> +        return false;
> +    }
> +    se = struct_entries + type_ptr[1];
> +    field_types = se->field_types;
> +    for (i = 0; i < se->nb_fields; i++) {
> +        if (*field_types == TYPE_FLEXIBLE_ARRAY) {
> +            return true;
> +        }
> +        field_types = thunk_type_next(type_ptr);
> +    }
> +    return false;
> +}
> +
> +int thunk_type_size_with_src(const void *src, const argtype *type_ptr,
> +                             int is_host)
> +{
> +    switch (*type_ptr) {
> +    case TYPE_STRUCT: {
> +        int i;
> +        const StructEntry *se = struct_entries + type_ptr[1];
> +        const argtype *field_types;
> +        if (se->thunk_size[is_host] != NULL) {
> +            return (*se->thunk_size[is_host])(src);
> +        }
> +
> +        field_types = se->field_types;
> +        for (i = 0; i < se->nb_fields; i++) {
> +            if (*field_types == TYPE_FLEXIBLE_ARRAY) {
> +                uint32_t array_length = *(const uint32_t *)(
> +                    (const uint8_t *)src +
> +                    se->field_offsets[is_host][field_types[1]]);
> +                if (!is_host) {
> +                    array_length = tswap32(array_length);
> +                }
> +                return se->size[is_host] +
> +                    array_length *
> +                    thunk_type_size(field_types + 2, is_host);
> +            }
> +            field_types = thunk_type_next(type_ptr);
> +        }
> +        return se->size[is_host];
> +    }
> +    default:
> +        return thunk_type_size(type_ptr, is_host);
> +    }
> +}
> +
>  /* from em86 */
>
>  /* Utility function: Table-driven functions to translate bitmasks
> --
> 2.28.0.220.ged08abb693-goog
>
>
Shu-Chun Weng Dec. 18, 2020, 4:03 a.m. UTC | #2
Ping -- any comments on
https://patchew.org/QEMU/cover.1597129029.git.scw@google.com/e0754f52180aee6418eae8b3b8aa5981fcac12fd.1597129029.git.scw@google.com/

On Tue, Aug 11, 2020 at 2:39 PM Shu-Chun Weng <scw@google.com> wrote:

> Forgot to +riku.voipio@iki.fi when generating v2.
>
> On Tue, Aug 11, 2020 at 12:10 AM Shu-Chun Weng <scw@google.com> wrote:
>
>> Flexible arrays may appear in the last field of a struct and are heavily
>> used in the ioctl(SIOCETHTOOL) system call on Linux. E.g.
>>
>>   struct ethtool_regs {
>>       __u32   cmd;
>>       __u32   version; /* driver-specific, indicates different chips/revs
>> */
>>       __u32   len; /* bytes */
>>       __u8    data[0];
>>   };
>>
>> where number of elements in `data` is specified in `len`. It is translated
>> into:
>>
>>   STRUCT(ethtool_regs,
>>          TYPE_INT, /* cmd */
>>          TYPE_INT, /* version */
>>          TYPE_INT, /* len */
>>          MK_FLEXIBLE_ARRAY(TYPE_CHAR, 2)) /* data[0]: len */
>>
>> where the "2" passed to `MK_FLEXIBLE_ARRAY` means the number of element
>> is specified by field number 2 (0-index).
>>
>> Signed-off-by: Shu-Chun Weng <scw@google.com>
>> ---
>> v1 -> v2:
>>   Fix style problems.
>>
>>  include/exec/user/thunk.h |  24 ++++++
>>  thunk.c                   | 152 +++++++++++++++++++++++++++++++++++++-
>>  2 files changed, 174 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h
>> index 7992475c9f..d0d7c83f1f 100644
>> --- a/include/exec/user/thunk.h
>> +++ b/include/exec/user/thunk.h
>> @@ -39,12 +39,21 @@ typedef enum argtype {
>>      TYPE_ARRAY,
>>      TYPE_STRUCT,
>>      TYPE_OLDDEVT,
>> +    TYPE_FLEXIBLE_ARRAY,
>>  } argtype;
>>
>>  #define MK_PTR(type) TYPE_PTR, type
>>  #define MK_ARRAY(type, size) TYPE_ARRAY, size, type
>>  #define MK_STRUCT(id) TYPE_STRUCT, id
>>
>> +/*
>> + * Should only appear as the last element of a TYPE_STRUCT.
>> `len_field_idx` is
>> + * the index into the fields in the enclosing struct that specify the
>> length of
>> + * the flexibly array. The length field MUST be a TYPE_INT field.
>> + */
>> +#define MK_FLEXIBLE_ARRAY(type, len_field_idx) \
>> +    TYPE_FLEXIBLE_ARRAY, (len_field_idx), type
>> +
>>  #define THUNK_TARGET 0
>>  #define THUNK_HOST   1
>>
>> @@ -55,6 +64,8 @@ typedef struct {
>>      int *field_offsets[2];
>>      /* special handling */
>>      void (*convert[2])(void *dst, const void *src);
>> +    int (*thunk_size[2])(const void *src);
>> +
>>      int size[2];
>>      int align[2];
>>      const char *name;
>> @@ -75,6 +86,11 @@ const argtype *thunk_convert(void *dst, const void
>> *src,
>>                               const argtype *type_ptr, int to_host);
>>  const argtype *thunk_print(void *arg, const argtype *type_ptr);
>>
>> +bool thunk_type_has_flexible_array(const argtype *type_ptr);
>> +/* thunk_type_size but can handle TYPE_FLEXIBLE_ARRAY */
>> +int thunk_type_size_with_src(const void *src, const argtype *type_ptr,
>> +                             int is_host);
>> +
>>  extern StructEntry *struct_entries;
>>
>>  int thunk_type_size_array(const argtype *type_ptr, int is_host);
>> @@ -137,6 +153,12 @@ static inline int thunk_type_size(const argtype
>> *type_ptr, int is_host)
>>      case TYPE_STRUCT:
>>          se = struct_entries + type_ptr[1];
>>          return se->size[is_host];
>> +    case TYPE_FLEXIBLE_ARRAY:
>> +        /*
>> +         * Flexible arrays do not count toward sizeof(). Users of
>> structures
>> +         * containing them need to calculate it themselves.
>> +         */
>> +        return 0;
>>      default:
>>          g_assert_not_reached();
>>      }
>> @@ -187,6 +209,8 @@ static inline int thunk_type_align(const argtype
>> *type_ptr, int is_host)
>>      case TYPE_STRUCT:
>>          se = struct_entries + type_ptr[1];
>>          return se->align[is_host];
>> +    case TYPE_FLEXIBLE_ARRAY:
>> +        return thunk_type_align_array(type_ptr + 2, is_host);
>>      default:
>>          g_assert_not_reached();
>>      }
>> diff --git a/thunk.c b/thunk.c
>> index c5d9719747..d9c6cba3bd 100644
>> --- a/thunk.c
>> +++ b/thunk.c
>> @@ -50,6 +50,8 @@ static inline const argtype *thunk_type_next(const
>> argtype *type_ptr)
>>          return thunk_type_next_ptr(type_ptr + 1);
>>      case TYPE_STRUCT:
>>          return type_ptr + 1;
>> +    case TYPE_FLEXIBLE_ARRAY:
>> +        return thunk_type_next_ptr(type_ptr + 1);
>>      default:
>>          return NULL;
>>      }
>> @@ -122,6 +124,34 @@ void thunk_register_struct_direct(int id, const char
>> *name,
>>      se->name = name;
>>  }
>>
>> +static const argtype *
>> +thunk_convert_flexible_array(void *dst, const void *src,
>> +                             const uint8_t *dst_struct,
>> +                             const uint8_t *src_struct, const argtype
>> *type_ptr,
>> +                             const StructEntry *se, int to_host) {
>> +    int len_field_idx, dst_size, src_size, i;
>> +    uint32_t array_length;
>> +    uint8_t *d;
>> +    const uint8_t *s;
>> +
>> +    assert(*type_ptr == TYPE_FLEXIBLE_ARRAY);
>> +    type_ptr++;
>> +    len_field_idx = *type_ptr++;
>> +    array_length =
>> +        *(const uint32_t *)(to_host ?
>> +                            dst_struct +
>> se->field_offsets[1][len_field_idx] :
>> +                            src_struct +
>> se->field_offsets[0][len_field_idx]);
>> +    dst_size = thunk_type_size(type_ptr, to_host);
>> +    src_size = thunk_type_size(type_ptr, to_host);
>> +    d = dst;
>> +    s = src;
>> +    for (i = 0; i < array_length; i++) {
>> +        thunk_convert(d, s, type_ptr, to_host);
>> +        d += dst_size;
>> +        s += src_size;
>> +    }
>> +    return thunk_type_next(type_ptr);
>> +}
>>
>>  /* now we can define the main conversion functions */
>>  const argtype *thunk_convert(void *dst, const void *src,
>> @@ -246,7 +276,7 @@ const argtype *thunk_convert(void *dst, const void
>> *src,
>>
>>              assert(*type_ptr < max_struct_entries);
>>              se = struct_entries + *type_ptr++;
>> -            if (se->convert[0] != NULL) {
>> +            if (se->convert[to_host] != NULL) {
>>                  /* specific conversion is needed */
>>                  (*se->convert[to_host])(dst, src);
>>              } else {
>> @@ -256,7 +286,18 @@ const argtype *thunk_convert(void *dst, const void
>> *src,
>>                  src_offsets = se->field_offsets[1 - to_host];
>>                  d = dst;
>>                  s = src;
>> -                for(i = 0;i < se->nb_fields; i++) {
>> +                for (i = 0; i < se->nb_fields; i++) {
>> +                    if (*field_types == TYPE_FLEXIBLE_ARRAY) {
>> +                        field_types = thunk_convert_flexible_array(
>> +                            d + dst_offsets[i],
>> +                            s + src_offsets[i],
>> +                            d,
>> +                            s,
>> +                            field_types,
>> +                            se,
>> +                            to_host);
>> +                        continue;
>> +                    }
>>                      field_types = thunk_convert(d + dst_offsets[i],
>>                                                  s + src_offsets[i],
>>                                                  field_types, to_host);
>> @@ -264,6 +305,11 @@ const argtype *thunk_convert(void *dst, const void
>> *src,
>>              }
>>          }
>>          break;
>> +    case TYPE_FLEXIBLE_ARRAY:
>> +        fprintf(stderr,
>> +                "Invalid flexible array (type 0x%x) outside of a
>> structure\n",
>> +                type);
>> +        break;
>>      default:
>>          fprintf(stderr, "Invalid type 0x%x\n", type);
>>          break;
>> @@ -271,6 +317,45 @@ const argtype *thunk_convert(void *dst, const void
>> *src,
>>      return type_ptr;
>>  }
>>
>> +static const argtype *
>> +thunk_print_flexible_array(void *arg, const uint8_t *arg_struct,
>> +                           const argtype *type_ptr, const StructEntry
>> *se) {
>> +    int array_length, len_field_idx, arg_size, i;
>> +    uint8_t *a;
>> +    int is_string = 0;
>> +
>> +    assert(*type_ptr == TYPE_FLEXIBLE_ARRAY);
>> +    type_ptr++;
>> +    len_field_idx = *type_ptr++;
>> +
>> +    array_length = tswap32(
>> +        *(const uint32_t *)(arg_struct +
>> se->field_offsets[0][len_field_idx]));
>> +    arg_size = thunk_type_size(type_ptr, 0);
>> +    a = arg;
>> +
>> +    if (*type_ptr == TYPE_CHAR) {
>> +        qemu_log("\"");
>> +        is_string = 1;
>> +    } else {
>> +        qemu_log("[");
>> +    }
>> +
>> +    for (i = 0; i < array_length; i++) {
>> +        if (i > 0 && !is_string) {
>> +            qemu_log(",");
>> +        }
>> +        thunk_print(a, type_ptr);
>> +        a += arg_size;
>> +    }
>> +
>> +    if (is_string) {
>> +        qemu_log("\"");
>> +    } else {
>> +        qemu_log("]");
>> +    }
>> +    return thunk_type_next(type_ptr);
>> +}
>> +
>>  const argtype *thunk_print(void *arg, const argtype *type_ptr)
>>  {
>>      int type;
>> @@ -414,17 +499,80 @@ const argtype *thunk_print(void *arg, const argtype
>> *type_ptr)
>>                  if (i > 0) {
>>                      qemu_log(",");
>>                  }
>> +                if (*field_types == TYPE_FLEXIBLE_ARRAY) {
>> +                    field_types = thunk_print_flexible_array(
>> +                        a + arg_offsets[i], a, field_types, se);
>> +                    continue;
>> +                }
>>                  field_types = thunk_print(a + arg_offsets[i],
>> field_types);
>>              }
>>              qemu_log("}");
>>          }
>>          break;
>> +    case TYPE_FLEXIBLE_ARRAY:
>> +        fprintf(stderr,
>> +                "Invalid flexible array (type 0x%x) outside of a
>> structure\n",
>> +                type);
>> +        break;
>>      default:
>>          g_assert_not_reached();
>>      }
>>      return type_ptr;
>>  }
>>
>> +bool thunk_type_has_flexible_array(const argtype *type_ptr)
>> +{
>> +  int i;
>> +  const StructEntry *se;
>> +  const argtype *field_types;
>> +    if (*type_ptr != TYPE_STRUCT) {
>> +        return false;
>> +    }
>> +    se = struct_entries + type_ptr[1];
>> +    field_types = se->field_types;
>> +    for (i = 0; i < se->nb_fields; i++) {
>> +        if (*field_types == TYPE_FLEXIBLE_ARRAY) {
>> +            return true;
>> +        }
>> +        field_types = thunk_type_next(type_ptr);
>> +    }
>> +    return false;
>> +}
>> +
>> +int thunk_type_size_with_src(const void *src, const argtype *type_ptr,
>> +                             int is_host)
>> +{
>> +    switch (*type_ptr) {
>> +    case TYPE_STRUCT: {
>> +        int i;
>> +        const StructEntry *se = struct_entries + type_ptr[1];
>> +        const argtype *field_types;
>> +        if (se->thunk_size[is_host] != NULL) {
>> +            return (*se->thunk_size[is_host])(src);
>> +        }
>> +
>> +        field_types = se->field_types;
>> +        for (i = 0; i < se->nb_fields; i++) {
>> +            if (*field_types == TYPE_FLEXIBLE_ARRAY) {
>> +                uint32_t array_length = *(const uint32_t *)(
>> +                    (const uint8_t *)src +
>> +                    se->field_offsets[is_host][field_types[1]]);
>> +                if (!is_host) {
>> +                    array_length = tswap32(array_length);
>> +                }
>> +                return se->size[is_host] +
>> +                    array_length *
>> +                    thunk_type_size(field_types + 2, is_host);
>> +            }
>> +            field_types = thunk_type_next(type_ptr);
>> +        }
>> +        return se->size[is_host];
>> +    }
>> +    default:
>> +        return thunk_type_size(type_ptr, is_host);
>> +    }
>> +}
>> +
>>  /* from em86 */
>>
>>  /* Utility function: Table-driven functions to translate bitmasks
>> --
>> 2.28.0.220.ged08abb693-goog
>>
>>
diff mbox series

Patch

diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h
index 7992475c9f..d0d7c83f1f 100644
--- a/include/exec/user/thunk.h
+++ b/include/exec/user/thunk.h
@@ -39,12 +39,21 @@  typedef enum argtype {
     TYPE_ARRAY,
     TYPE_STRUCT,
     TYPE_OLDDEVT,
+    TYPE_FLEXIBLE_ARRAY,
 } argtype;
 
 #define MK_PTR(type) TYPE_PTR, type
 #define MK_ARRAY(type, size) TYPE_ARRAY, size, type
 #define MK_STRUCT(id) TYPE_STRUCT, id
 
+/*
+ * Should only appear as the last element of a TYPE_STRUCT. `len_field_idx` is
+ * the index into the fields in the enclosing struct that specify the length of
+ * the flexibly array. The length field MUST be a TYPE_INT field.
+ */
+#define MK_FLEXIBLE_ARRAY(type, len_field_idx) \
+    TYPE_FLEXIBLE_ARRAY, (len_field_idx), type
+
 #define THUNK_TARGET 0
 #define THUNK_HOST   1
 
@@ -55,6 +64,8 @@  typedef struct {
     int *field_offsets[2];
     /* special handling */
     void (*convert[2])(void *dst, const void *src);
+    int (*thunk_size[2])(const void *src);
+
     int size[2];
     int align[2];
     const char *name;
@@ -75,6 +86,11 @@  const argtype *thunk_convert(void *dst, const void *src,
                              const argtype *type_ptr, int to_host);
 const argtype *thunk_print(void *arg, const argtype *type_ptr);
 
+bool thunk_type_has_flexible_array(const argtype *type_ptr);
+/* thunk_type_size but can handle TYPE_FLEXIBLE_ARRAY */
+int thunk_type_size_with_src(const void *src, const argtype *type_ptr,
+                             int is_host);
+
 extern StructEntry *struct_entries;
 
 int thunk_type_size_array(const argtype *type_ptr, int is_host);
@@ -137,6 +153,12 @@  static inline int thunk_type_size(const argtype *type_ptr, int is_host)
     case TYPE_STRUCT:
         se = struct_entries + type_ptr[1];
         return se->size[is_host];
+    case TYPE_FLEXIBLE_ARRAY:
+        /*
+         * Flexible arrays do not count toward sizeof(). Users of structures
+         * containing them need to calculate it themselves.
+         */
+        return 0;
     default:
         g_assert_not_reached();
     }
@@ -187,6 +209,8 @@  static inline int thunk_type_align(const argtype *type_ptr, int is_host)
     case TYPE_STRUCT:
         se = struct_entries + type_ptr[1];
         return se->align[is_host];
+    case TYPE_FLEXIBLE_ARRAY:
+        return thunk_type_align_array(type_ptr + 2, is_host);
     default:
         g_assert_not_reached();
     }
diff --git a/thunk.c b/thunk.c
index c5d9719747..d9c6cba3bd 100644
--- a/thunk.c
+++ b/thunk.c
@@ -50,6 +50,8 @@  static inline const argtype *thunk_type_next(const argtype *type_ptr)
         return thunk_type_next_ptr(type_ptr + 1);
     case TYPE_STRUCT:
         return type_ptr + 1;
+    case TYPE_FLEXIBLE_ARRAY:
+        return thunk_type_next_ptr(type_ptr + 1);
     default:
         return NULL;
     }
@@ -122,6 +124,34 @@  void thunk_register_struct_direct(int id, const char *name,
     se->name = name;
 }
 
+static const argtype *
+thunk_convert_flexible_array(void *dst, const void *src,
+                             const uint8_t *dst_struct,
+                             const uint8_t *src_struct, const argtype *type_ptr,
+                             const StructEntry *se, int to_host) {
+    int len_field_idx, dst_size, src_size, i;
+    uint32_t array_length;
+    uint8_t *d;
+    const uint8_t *s;
+
+    assert(*type_ptr == TYPE_FLEXIBLE_ARRAY);
+    type_ptr++;
+    len_field_idx = *type_ptr++;
+    array_length =
+        *(const uint32_t *)(to_host ?
+                            dst_struct + se->field_offsets[1][len_field_idx] :
+                            src_struct + se->field_offsets[0][len_field_idx]);
+    dst_size = thunk_type_size(type_ptr, to_host);
+    src_size = thunk_type_size(type_ptr, to_host);
+    d = dst;
+    s = src;
+    for (i = 0; i < array_length; i++) {
+        thunk_convert(d, s, type_ptr, to_host);
+        d += dst_size;
+        s += src_size;
+    }
+    return thunk_type_next(type_ptr);
+}
 
 /* now we can define the main conversion functions */
 const argtype *thunk_convert(void *dst, const void *src,
@@ -246,7 +276,7 @@  const argtype *thunk_convert(void *dst, const void *src,
 
             assert(*type_ptr < max_struct_entries);
             se = struct_entries + *type_ptr++;
-            if (se->convert[0] != NULL) {
+            if (se->convert[to_host] != NULL) {
                 /* specific conversion is needed */
                 (*se->convert[to_host])(dst, src);
             } else {
@@ -256,7 +286,18 @@  const argtype *thunk_convert(void *dst, const void *src,
                 src_offsets = se->field_offsets[1 - to_host];
                 d = dst;
                 s = src;
-                for(i = 0;i < se->nb_fields; i++) {
+                for (i = 0; i < se->nb_fields; i++) {
+                    if (*field_types == TYPE_FLEXIBLE_ARRAY) {
+                        field_types = thunk_convert_flexible_array(
+                            d + dst_offsets[i],
+                            s + src_offsets[i],
+                            d,
+                            s,
+                            field_types,
+                            se,
+                            to_host);
+                        continue;
+                    }
                     field_types = thunk_convert(d + dst_offsets[i],
                                                 s + src_offsets[i],
                                                 field_types, to_host);
@@ -264,6 +305,11 @@  const argtype *thunk_convert(void *dst, const void *src,
             }
         }
         break;
+    case TYPE_FLEXIBLE_ARRAY:
+        fprintf(stderr,
+                "Invalid flexible array (type 0x%x) outside of a structure\n",
+                type);
+        break;
     default:
         fprintf(stderr, "Invalid type 0x%x\n", type);
         break;
@@ -271,6 +317,45 @@  const argtype *thunk_convert(void *dst, const void *src,
     return type_ptr;
 }
 
+static const argtype *
+thunk_print_flexible_array(void *arg, const uint8_t *arg_struct,
+                           const argtype *type_ptr, const StructEntry *se) {
+    int array_length, len_field_idx, arg_size, i;
+    uint8_t *a;
+    int is_string = 0;
+
+    assert(*type_ptr == TYPE_FLEXIBLE_ARRAY);
+    type_ptr++;
+    len_field_idx = *type_ptr++;
+
+    array_length = tswap32(
+        *(const uint32_t *)(arg_struct + se->field_offsets[0][len_field_idx]));
+    arg_size = thunk_type_size(type_ptr, 0);
+    a = arg;
+
+    if (*type_ptr == TYPE_CHAR) {
+        qemu_log("\"");
+        is_string = 1;
+    } else {
+        qemu_log("[");
+    }
+
+    for (i = 0; i < array_length; i++) {
+        if (i > 0 && !is_string) {
+            qemu_log(",");
+        }
+        thunk_print(a, type_ptr);
+        a += arg_size;
+    }
+
+    if (is_string) {
+        qemu_log("\"");
+    } else {
+        qemu_log("]");
+    }
+    return thunk_type_next(type_ptr);
+}
+
 const argtype *thunk_print(void *arg, const argtype *type_ptr)
 {
     int type;
@@ -414,17 +499,80 @@  const argtype *thunk_print(void *arg, const argtype *type_ptr)
                 if (i > 0) {
                     qemu_log(",");
                 }
+                if (*field_types == TYPE_FLEXIBLE_ARRAY) {
+                    field_types = thunk_print_flexible_array(
+                        a + arg_offsets[i], a, field_types, se);
+                    continue;
+                }
                 field_types = thunk_print(a + arg_offsets[i], field_types);
             }
             qemu_log("}");
         }
         break;
+    case TYPE_FLEXIBLE_ARRAY:
+        fprintf(stderr,
+                "Invalid flexible array (type 0x%x) outside of a structure\n",
+                type);
+        break;
     default:
         g_assert_not_reached();
     }
     return type_ptr;
 }
 
+bool thunk_type_has_flexible_array(const argtype *type_ptr)
+{
+  int i;
+  const StructEntry *se;
+  const argtype *field_types;
+    if (*type_ptr != TYPE_STRUCT) {
+        return false;
+    }
+    se = struct_entries + type_ptr[1];
+    field_types = se->field_types;
+    for (i = 0; i < se->nb_fields; i++) {
+        if (*field_types == TYPE_FLEXIBLE_ARRAY) {
+            return true;
+        }
+        field_types = thunk_type_next(type_ptr);
+    }
+    return false;
+}
+
+int thunk_type_size_with_src(const void *src, const argtype *type_ptr,
+                             int is_host)
+{
+    switch (*type_ptr) {
+    case TYPE_STRUCT: {
+        int i;
+        const StructEntry *se = struct_entries + type_ptr[1];
+        const argtype *field_types;
+        if (se->thunk_size[is_host] != NULL) {
+            return (*se->thunk_size[is_host])(src);
+        }
+
+        field_types = se->field_types;
+        for (i = 0; i < se->nb_fields; i++) {
+            if (*field_types == TYPE_FLEXIBLE_ARRAY) {
+                uint32_t array_length = *(const uint32_t *)(
+                    (const uint8_t *)src +
+                    se->field_offsets[is_host][field_types[1]]);
+                if (!is_host) {
+                    array_length = tswap32(array_length);
+                }
+                return se->size[is_host] +
+                    array_length *
+                    thunk_type_size(field_types + 2, is_host);
+            }
+            field_types = thunk_type_next(type_ptr);
+        }
+        return se->size[is_host];
+    }
+    default:
+        return thunk_type_size(type_ptr, is_host);
+    }
+}
+
 /* from em86 */
 
 /* Utility function: Table-driven functions to translate bitmasks