[PATCH 1/8] cxl/features: Validate the fwctl RPC input length
Guixin Liu <[email protected]>
| Newsgroups | org.kernel.vger.linux-cxl |
|---|---|
| Message-ID | <[email protected]> |
cxlctl_fw_rpc() ignores @in_len, the length of the buffer that the fwctl
core copied in from userspace, and blindly dereferences the input as a
'struct fwctl_rpc_cxl'.
Userspace fully controls that length via fwctl_rpc.in_len, which the core
only bounds from above (MAX_RPC_LEN) before doing
kvzalloc(cmd->in_len)/copy_from_user(). An in_len of 0 yields a
ZERO_SIZE_PTR allocation, so the read of rpc_in->opcode at the top of
cxlctl_fw_rpc() faults, and any in_len smaller than the header reads past
the allocation.
The @op_size field of the header is equally unchecked. It is a u32 that
describes how much payload trails the header, and it is used as such:
cxlctl_set_feature() passes 'op_size - sizeof(feat_in->hdr)' to
cxl_set_feature() as the length of feat_in->feat_data, and
cxlctl_validate_set_features() reads the UUID out of the payload once
op_size claims to be large enough. Since op_size is never compared against
the size of the buffer that was actually copied in, a caller passing a
small in_len together with a large op_size makes the driver read up to
~4GB past the end of the input allocation.
Require the input to be at least header sized, and require the declared
payload to fit in what was copied in. This matches the documented
userspace calling convention (Documentation/userspace-api/fwctl/
fwctl-cxl.rst), which sizes the input buffer as
'sizeof(struct fwctl_rpc_cxl) + sizeof(*payload)' while setting op_size to
just the payload size.
Fixes: 4d1c09cef2c2 ("cxl: Add support for fwctl RPC command to enable CXL feature commands")
Signed-off-by: Guixin Liu <[email protected]>
---
drivers/cxl/core/features.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c
index 85185af46b72..0ab1a8547b7e 100644
--- a/drivers/cxl/core/features.c
+++ b/drivers/cxl/core/features.c
@@ -649,7 +649,16 @@ static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev);
struct cxl_features_state *cxlfs = to_cxlfs(cxlmd->cxlds);
const struct fwctl_rpc_cxl *rpc_in = in;
- u16 opcode = rpc_in->opcode;
+ u16 opcode;
+
+ if (in_len < sizeof(rpc_in->hdr))
+ return ERR_PTR(-EINVAL);
+
+ /* @op_size describes the input payload that trails the header */
+ if (rpc_in->op_size > in_len - sizeof(rpc_in->hdr))
+ return ERR_PTR(-EINVAL);
+
+ opcode = rpc_in->opcode;
if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode))
return ERR_PTR(-EINVAL);
--
2.43.7