From: Jiri Pirko Add parsing and VA checks for blob descriptors shared by schema validation and runtime acquisition. Keep CONFIG_CTLV unwired until dispatch completes the module. Assisted-by: Cursor: GPT-5.6 Sol Assisted-by: Cursor: Claude Opus 5.5 Signed-off-by: Jiri Pirko --- RFC->RFCv2: - returned EOVERFLOW for a blob range that wraps - said TLV, type, record and field instead of attr - said record instead of nest, the root being one - numbered every enum member - took the UAPI headers from include/uapi/ctlv/ - moved to lib/ctlv/ --- lib/ctlv/Makefile | 2 +- lib/ctlv/blob_desc.c | 101 +++++++++++++++++++++++++++++++++++++++++++ lib/ctlv/blob_desc.h | 29 +++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 lib/ctlv/blob_desc.c create mode 100644 lib/ctlv/blob_desc.h diff --git a/lib/ctlv/Makefile b/lib/ctlv/Makefile index 4f0fe41bc9e1..5edc0b4b973f 100644 --- a/lib/ctlv/Makefile +++ b/lib/ctlv/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CTLV) += ctlv.o -ctlv-y += message.o +ctlv-y += blob_desc.o message.o diff --git a/lib/ctlv/blob_desc.c b/lib/ctlv/blob_desc.c new file mode 100644 index 000000000000..7c68ef5702fd --- /dev/null +++ b/lib/ctlv/blob_desc.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES + * + * Blob descriptor parsing and validation. + */ +#include +#include +#include +#include +#include + +#include +#include + +#include "blob_desc.h" +#include "message.h" + +static int ctlv_blob_member(u64 type) +{ + switch (type) { + case CTLV_T_BLOB_TYPE: + return CTLV_BLOB_MEMBER_TYPE; + case CTLV_T_BLOB_ADDRESS: + return CTLV_BLOB_MEMBER_ADDRESS; + case CTLV_T_BLOB_FD: + return CTLV_BLOB_MEMBER_FD; + case CTLV_T_BLOB_OFFSET: + return CTLV_BLOB_MEMBER_OFFSET; + case CTLV_T_BLOB_LENGTH: + return CTLV_BLOB_MEMBER_LENGTH; + default: + return -1; + } +} + +u32 ctlv_blob_desc_read(const struct ctlv_hdr *blob, struct ctlv_blob_desc *desc, + const struct ctlv_hdr **bad) +{ + const struct ctlv_hdr *child = NULL; + enum ctlv_message_step step; + + *desc = (struct ctlv_blob_desc){}; + while ((step = ctlv_message_next(blob, &child)) == CTLV_MESSAGE_STEP_TLV) { + int member = ctlv_blob_member(child->type); + + *bad = child; + if (child->flags & ~CTLV_HDR_F_RECORD) + return CTLV_VALIDATION_REASON_UNKNOWN_FLAG; + if (child->flags & CTLV_HDR_F_RECORD) + return CTLV_VALIDATION_REASON_MALFORMED_MESSAGE; + if (member < 0) + return CTLV_VALIDATION_REASON_UNKNOWN_TYPE; + if (ctlv_message_hdr_payload_len(child) != sizeof(u64)) + return CTLV_VALIDATION_REASON_BAD_LENGTH; + if (desc->present & BIT(member)) + return CTLV_VALIDATION_REASON_BAD_BLOB_DESCRIPTOR; + desc->present |= BIT(member); + desc->member[member] = *(const u64 *)ctlv_message_hdr_payload(child); + } + *bad = NULL; + if (step == CTLV_MESSAGE_STEP_BAD) + return CTLV_VALIDATION_REASON_MALFORMED_MESSAGE; + return 0; +} + +static bool ctlv_blob_variant_ok(const struct ctlv_blob_desc *desc) +{ + u32 required = BIT(CTLV_BLOB_MEMBER_TYPE) | BIT(CTLV_BLOB_MEMBER_LENGTH); + + switch (desc->member[CTLV_BLOB_MEMBER_TYPE]) { + case CTLV_BLOB_TYPE_VA: + required |= BIT(CTLV_BLOB_MEMBER_ADDRESS); + break; + default: + return false; + } + return desc->present == required; +} + +/* Every failure is a bad descriptor; only a range that wraps is -EOVERFLOW. */ +int ctlv_blob_desc_check(const struct ctlv_blob_desc *desc, const struct ctlv_node_schema *node) +{ + u64 start, length, end; + + if (!ctlv_blob_variant_ok(desc) || + !(node->blob.backings & BIT(desc->member[CTLV_BLOB_MEMBER_TYPE]))) + return -EINVAL; + + length = desc->member[CTLV_BLOB_MEMBER_LENGTH]; + if (!length || length > node->blob.max_len) + return -EINVAL; + start = desc->member[desc->present & BIT(CTLV_BLOB_MEMBER_ADDRESS) ? + CTLV_BLOB_MEMBER_ADDRESS : + CTLV_BLOB_MEMBER_OFFSET]; + if (!IS_ALIGNED(start, node->blob.alignment)) + return -EINVAL; + if (check_add_overflow(start, length, &end)) + return -EOVERFLOW; + return 0; +} diff --git a/lib/ctlv/blob_desc.h b/lib/ctlv/blob_desc.h new file mode 100644 index 000000000000..e88693cbee4b --- /dev/null +++ b/lib/ctlv/blob_desc.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _CTLV_BLOB_DESC_H +#define _CTLV_BLOB_DESC_H + +#include +#include + +#include + +/* The members of a descriptor, in the order the root family declares them. */ +enum { + CTLV_BLOB_MEMBER_TYPE = 0, + CTLV_BLOB_MEMBER_ADDRESS = 1, + CTLV_BLOB_MEMBER_FD = 2, + CTLV_BLOB_MEMBER_OFFSET = 3, + CTLV_BLOB_MEMBER_LENGTH = 4, + CTLV_BLOB_MEMBERS = 5, +}; + +struct ctlv_blob_desc { + u64 member[CTLV_BLOB_MEMBERS]; + u32 present; +}; + +u32 ctlv_blob_desc_read(const struct ctlv_hdr *blob, struct ctlv_blob_desc *desc, + const struct ctlv_hdr **bad); +int ctlv_blob_desc_check(const struct ctlv_blob_desc *desc, const struct ctlv_node_schema *node); + +#endif /* _CTLV_BLOB_DESC_H */ -- 2.56.0