Got dpa_app working

This commit is contained in:
2026-05-10 22:25:46 +08:00
parent 8bc9737032
commit 9ccd41bc54
9 changed files with 571 additions and 23 deletions

View File

@@ -0,0 +1,120 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: monok8s <monok8s@example.invalid>
Date: Sun, 10 May 2026 00:00:00 +0000
Subject: [PATCH 3/4] cdx: avoid kfree of userspace dist_info pointers
get_port_info() copies an array of cdx_port_info from userspace. At that
point each cdx_port_info.dist_info field is still a userspace pointer.
However release_cfg_info() treats any non-NULL dist_info as a kernel
allocation and kfree()s it on error paths.
If a later step fails before get_dist_info() has replaced every dist_info
with a kernel allocation, release_cfg_info() can kfree a raw userspace
pointer and oops in kfree()/virt_to_folio().
Stash the userspace dist_info pointers in a temporary array, clear the
kernel-side cdx_port_info.dist_info fields immediately after copy_from_user(),
and pass the saved userspace pointer explicitly to get_dist_info(). This
keeps release_cfg_info() safe on partial-initialization failures.
---
cdx/dpa_cfg.c | 47 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 37 insertions(+), 10 deletions(-)
diff -urN a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c
--- a/cdx/dpa_cfg.c 2026-05-10 00:46:34.295813594 +0000
+++ b/cdx/dpa_cfg.c 2026-05-10 00:46:35.558487337 +0000
@@ -169,11 +169,10 @@
}
//allocate and copy distribution info from uspace
-static int get_dist_info(struct cdx_port_info *port_info)
+static int get_dist_info(struct cdx_port_info *port_info, void *uspace_info)
{
uint32_t mem_size;
struct cdx_dist_info *dist_info;
- void *uspace_info;
#ifdef DPA_CFG_DEBUG
DPA_INFO("%s::port %s dist %d\n", __func__,
@@ -187,7 +186,6 @@
return -ENOMEM;
}
memset(dist_info, 0, mem_size);
- uspace_info = port_info->dist_info;
port_info->dist_info = dist_info;
if (copy_from_user(dist_info, uspace_info,
mem_size)) {
@@ -273,6 +271,7 @@
{
struct cdx_port_info *port_info;
void *uspace_info;
+ void **uspace_dist_info;
uint32_t mem_size;
uint32_t ii;
@@ -289,13 +288,40 @@
return -ENOMEM;
}
memset(port_info, 0, mem_size);
+
+ uspace_dist_info = kcalloc(finfo->max_ports, sizeof(*uspace_dist_info),
+ GFP_KERNEL);
+ if (!uspace_dist_info) {
+ DPA_ERROR("%s::memalloc for uspace_dist_info failed\n",
+ __func__);
+ kfree(port_info);
+ return -ENOMEM;
+ }
+
uspace_info = finfo->portinfo;
finfo->portinfo = port_info;
if (copy_from_user(port_info, uspace_info, mem_size)) {
DPA_ERROR("%s::Read port_info failed\n",
__func__);
+ finfo->portinfo = NULL;
+ kfree(uspace_dist_info);
+ kfree(port_info);
return -EIO;
}
+
+ /*
+ * port_info has just been copied from userspace, so each dist_info
+ * member is still a userspace pointer. release_cfg_info() kfree()s
+ * non-NULL dist_info members, therefore keeping those raw userspace
+ * pointers in the kernel copy turns any later error path into an
+ * invalid kfree(). Stash the userspace pointers separately and clear
+ * the struct fields until get_dist_info() replaces them with real
+ * kernel allocations.
+ */
+ for (ii = 0; ii < finfo->max_ports; ii++) {
+ uspace_dist_info[ii] = port_info[ii].dist_info;
+ port_info[ii].dist_info = NULL;
+ }
//put the linux name for the port
for (ii = 0; ii < finfo->max_ports; ii++) {
struct net_device *dev;
@@ -311,6 +337,7 @@
__func__, port_info->name,
port_info->fm_index, port_info->index,
port_info->portid, port_info->type);
+ kfree(uspace_dist_info);
return -ENODEV;
}
DPA_INFO("%s::mapped user port %s to netdev %s\n",
@@ -330,11 +357,14 @@
for (ii = 0; ii < finfo->max_ports; ii++) {
int retval;
//get dist info for this port
- retval = get_dist_info(port_info);
- if (retval)
+ retval = get_dist_info(port_info, uspace_dist_info[ii]);
+ if (retval) {
+ kfree(uspace_dist_info);
return retval;
+ }
port_info++;
}
+ kfree(uspace_dist_info);
return 0;
}