static int c_variant_write_one(CVariant *cv, char basic, const void *arg, size_t n_arg) { CVariantLevel *level; CVariantType info; void *front; int r; assert(n_arg > 0); level = cv->state->levels + cv->state->i_levels; if (_unlikely_(level->n_type < 1)) return c_variant_poison(cv, -EBADRQC); r = c_variant_signature_next(level->type, level->n_type, &info); assert(r == 1); r = c_variant_append(cv, basic, &info, 0, n_arg, &front, 0, NULL); if (r < 0) return r; memcpy(front, arg, n_arg); return 0; }
static int c_variant_insert_one(CVariant *cv, const char *type, const struct iovec *vecs, size_t n_vecs, size_t size) { CVariantLevel *level; CVariantType info; size_t n_type, i, idx; struct iovec *v; uint64_t frame; int r; level = cv->state->levels + cv->state->i_levels; if (_unlikely_(level->n_type < 1)) return c_variant_poison(cv, -EBADRQC); r = c_variant_signature_next(level->type, level->n_type, &info); assert(r == 1); n_type = strlen(type); if (_unlikely_(n_type != info.n_type || strncmp(type, info.type, n_type))) return c_variant_poison(cv, -EBADRQC); if (_unlikely_(info.size > 0 && size != info.size)) return c_variant_poison(cv, -EBADMSG); r = c_variant_append(cv, *type, &info, n_vecs + 1, 0, NULL, 0, NULL); if (r < 0) return r; /* make sure there are at least 'n_vecs + 1' unused vectors */ assert(cv->n_vecs - level->v_front - level->v_tail - 2U >= n_vecs + 1U); /* * Clip the current front and prepare the next vector with the * remaining buffer space. Then insert the requested vectors in between * both and verify alignment restrictions. */ v = cv->vecs + level->v_front; v[n_vecs + 1].iov_base = (char *)v->iov_base + level->i_front; v[n_vecs + 1].iov_len = v->iov_len - level->i_front; v->iov_len = level->i_front; for (i = 0; i < n_vecs; ++i) { idx = level->v_front + i + 1; if (((char *)(cv->vecs + cv->n_vecs))[idx]) { ((char *)(cv->vecs + cv->n_vecs))[idx] = false; free((cv->vecs + idx)->iov_base); } cv->vecs[idx] = vecs[i]; } level->v_front += n_vecs + 1; level->i_front = 0; level->offset += size; /* see c_variant_end_one(); we have to update the framing offset */ if (info.size < 1) { switch (level->enclosing) { case C_VARIANT_TUPLE_OPEN: case C_VARIANT_PAIR_OPEN: /* last element never stores framing offsets */ if (level->n_type < 1) break; /* fallthrough */ case C_VARIANT_ARRAY: assert(level->i_tail >= 8); assert(!(level->i_tail & 7)); v = cv->vecs + cv->n_vecs - level->v_tail - 1; frame = level->offset; memcpy((char *)v->iov_base + level->i_tail - 8, &frame, 8); break; } } return 0; }
static int c_variant_begin_one(CVariant *cv, char container, const char *variant) { CVariantLevel *next, *level; CVariantType info; size_t n_tail; void *tail; int r; r = c_variant_ensure_level(cv); if (r < 0) return r; if (container == C_VARIANT_VARIANT) n_tail = strlen(variant); else n_tail = 0; level = cv->state->levels + cv->state->i_levels; if (_unlikely_(level->n_type < 1)) return c_variant_poison(cv, -EBADRQC); r = c_variant_signature_next(level->type, level->n_type, &info); assert(r == 1); r = c_variant_append(cv, container, &info, 0, 0, NULL, n_tail, &tail); if (r < 0) return r; c_variant_push_level(cv); next = cv->state->levels + cv->state->i_levels; next->size = info.size; next->i_tail = level->i_tail; next->v_tail = level->v_tail; /* wordsize is unused */ next->enclosing = container; next->v_front = level->v_front; next->i_front = level->i_front; next->index = 0; next->offset = 0; switch (container) { case C_VARIANT_VARIANT: memcpy(tail, variant, n_tail); next->i_tail += n_tail; next->n_type = n_tail; next->index = n_tail; next->type = tail; break; case C_VARIANT_MAYBE: case C_VARIANT_ARRAY: next->n_type = info.n_type - 1; next->type = info.type + 1; break; case C_VARIANT_TUPLE_OPEN: case C_VARIANT_PAIR_OPEN: next->n_type = info.n_type - 2; next->type = info.type + 1; break; default: assert(0); break; } return 0; }
static void test_signature_api(void) { CVariantType t; int r; /* * Verify that we never read more data from the signature than * requested by the caller. Hence, this call should never see the * invalid '$' element. */ r = c_variant_signature_next("$foobar", 0, &t); assert(r == 0); assert(t.alignment == 0); assert(t.size == 0); assert(t.bound_size == 0); assert(t.n_levels == 0); assert(t.n_type == 0); /* * Verify that we never look ahead of a parsed type. That is, parsing * the valid element 'b' should return before looking at the invalid * element '$' ahead. * But if we then strip it (that is, we continue after the parsed * element), it should immediately fail but leave 't' untouched. */ r = c_variant_signature_next("b$foobar", 8, &t); assert(r == 1); assert(t.alignment == 0); assert(t.size == 1); assert(t.bound_size == 0); assert(t.n_levels == 0); assert(t.n_type == 1); assert(!strncmp(t.type, "b", t.n_type)); r = c_variant_signature_next(t.type + t.n_type, 8 - t.n_type, &t); assert(r < 0); assert(r == -EMEDIUMTYPE); /* @t must not have been touched! */ assert(t.alignment == 0); assert(t.size == 1); assert(t.bound_size == 0); assert(t.n_levels == 0); assert(t.n_type == 1); assert(!strncmp(t.type, "b", t.n_type)); /* * Make sure an inappropriately sized signature will result in an * immediate error without the data being looked at. * This is not explicitly part of the *ABI*, but reasonable enough to * test for. */ r = c_variant_signature_next(NULL, SIZE_MAX, &t); assert(r < 0); assert(r == -EMSGSIZE); /* * Make sure API-types are rejected in type-strings. This includes * anything that is not a valid element. */ r = c_variant_signature_next("r", 1, &t); assert(r < 0); assert(r == -EMEDIUMTYPE); r = c_variant_signature_next("e", 1, &t); assert(r < 0); assert(r == -EMEDIUMTYPE); r = c_variant_signature_next("?", 1, &t); assert(r < 0); assert(r == -EMEDIUMTYPE); r = c_variant_signature_next("*", 1, &t); assert(r < 0); assert(r == -EMEDIUMTYPE); }