|
From: | Shu-Chun Weng |
Subject: | Re: [PATCH v2 7/8] thunk: supports flexible arrays |
Date: | Tue, 11 Aug 2020 14:39:10 -0700 |
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
smime.p7s
Description: S/MIME Cryptographic Signature
[Prev in Thread] | Current Thread | [Next in Thread] |