Got dpa_app working
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user