Skip to content

Commit

Permalink
json: Improve tolerance to uncovered C members during JSON encoding
Browse files Browse the repository at this point in the history
This enhancement fix encoding of JSON objects for which descriptors do
not covers all members of the C structure represented.

Introduce SIZEOF_FIELD helper macro

Fixes #50976

Signed-off-by: Lucas Dietrich <[email protected]>
  • Loading branch information
lucasdietrich committed Apr 3, 2023
1 parent 4256cd4 commit de6b69c
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 14 deletions.
14 changes: 10 additions & 4 deletions include/zephyr/data/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ struct json_obj_descr {
union {
struct {
const struct json_obj_descr *sub_descr;
size_t sub_descr_len;
uint16_t sub_descr_len;
uint16_t sub_struct_size;
} object;
struct {
const struct json_obj_descr *element_descr;
Expand Down Expand Up @@ -201,6 +202,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len,
.object = { \
.sub_descr = sub_descr_, \
.sub_descr_len = ARRAY_SIZE(sub_descr_), \
.sub_struct_size = sizeof(struct_), \
}, \
}, \
}
Expand Down Expand Up @@ -245,11 +247,12 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len,
* @param elem_descr_ Element descriptor, pointer to a descriptor array
* @param elem_descr_len_ Number of elements in elem_descr_
*/
#define Z_JSON_DESCR_OBJ(elem_descr_, elem_descr_len_) \
#define Z_JSON_DESCR_OBJ(elem_descr_, elem_descr_len_, struct_size_) \
{ \
.object = { \
.sub_descr = elem_descr_, \
.sub_descr_len = elem_descr_len_, \
.sub_struct_size = struct_size_, \
}, \
}

Expand Down Expand Up @@ -338,7 +341,8 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len,
.array = { \
.element_descr = Z_JSON_ELEMENT_DESCR(struct_, len_field_, \
JSON_TOK_OBJECT_START, \
Z_JSON_DESCR_OBJ(elem_descr_, elem_descr_len_)), \
Z_JSON_DESCR_OBJ(elem_descr_, elem_descr_len_, \
SIZEOF_FIELD(struct_, field_name_[0]))), \
.n_elements = (max_len_), \
}, \
}, \
Expand Down Expand Up @@ -456,6 +460,7 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len,
.object = { \
.sub_descr = sub_descr_, \
.sub_descr_len = ARRAY_SIZE(sub_descr_), \
.sub_struct_size = sizeof(struct_), \
}, \
}, \
}
Expand Down Expand Up @@ -548,7 +553,8 @@ typedef int (*json_append_bytes_t)(const char *bytes, size_t len,
.array = { \
.element_descr = Z_JSON_ELEMENT_DESCR(struct_, len_field_, \
JSON_TOK_OBJECT_START, \
Z_JSON_DESCR_OBJ(elem_descr_, elem_descr_len_)), \
Z_JSON_DESCR_OBJ(elem_descr_, elem_descr_len_, \
SIZEOF_FIELD(struct_, field_name_[0])), \
.n_elements = (max_len_), \
}, \
}, \
Expand Down
20 changes: 20 additions & 0 deletions include/zephyr/sys/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,26 @@ extern "C" {
#define CONTAINER_OF(ptr, type, field) \
((type *)(((char *)(ptr)) - offsetof(type, field)))

/**
* @brief Get the size of a field in a structure type
*
* Example:
*
* struct foo {
* int bar;
* char baz;
* };
*
* size_t size_of_baz = SIZEOF_FIELD(struct foo, baz);
*
* Above, @p size_of_baz is equal to sizeof(char).
*
* @param type the type of the structure containing the field
* @param member the name of the field in the structure
* @return the size of the field in bytes
*/
#define SIZEOF_FIELD(type, member) sizeof(((type *)0)->member)

/**
* @brief Value of @p x rounded up to the next multiple of @p align,
* which must be a power of 2.
Expand Down
13 changes: 3 additions & 10 deletions lib/os/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -522,16 +522,7 @@ static ptrdiff_t get_elem_size(const struct json_obj_descr *descr)
case JSON_TOK_ARRAY_START:
return descr->array.n_elements * get_elem_size(descr->array.element_descr);
case JSON_TOK_OBJECT_START: {
ptrdiff_t total = 0;
size_t i;

for (i = 0; i < descr->object.sub_descr_len; i++) {
ptrdiff_t s = get_elem_size(&descr->object.sub_descr[i]);

total += ROUND_UP(s, 1 << descr->align_shift);
}

return total;
return descr->object.sub_struct_size;
}
default:
return -EINVAL;
Expand All @@ -549,6 +540,8 @@ static int arr_parse(struct json_obj *obj,

if (val) {
elements = (size_t *)((char *)val + elem_descr->offset);
} else {
field = (char *)field + elem_descr->offset;
}

__ASSERT_NO_MSG(elem_size > 0);
Expand Down
9 changes: 9 additions & 0 deletions tests/lib/json/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

struct test_nested {
int nested_int;
uint32_t _unused_member;
bool nested_bool;
const char *nested_string;
};
Expand All @@ -19,6 +20,7 @@ struct test_struct {
const char *some_string;
int some_int;
bool some_bool;
uint32_t _unused_member;
struct test_nested some_nested_struct;
int some_array[16];
size_t some_array_len;
Expand All @@ -30,17 +32,20 @@ struct test_struct {
};

struct elt {
uint32_t _unused_member;
const char *name;
int height;
};

struct obj_array {
struct elt elements[10];
uint32_t _unused_member;
size_t num_elements;
};

struct test_int_limits {
int int_max;
uint32_t _unused_member;
int int_cero;
int int_min;
};
Expand Down Expand Up @@ -88,11 +93,15 @@ static const struct json_obj_descr obj_limits_descr[] = {
};

struct array {
uint32_t _unused_member1;
uint32_t _unused_member2;
struct elt objects;
};

struct obj_array_array {
uint32_t _unused_member1;
struct array objects_array[4];
uint32_t _unused_member2;
size_t objects_array_len;
};

Expand Down

0 comments on commit de6b69c

Please sign in to comment.