From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: monok8s Date: Sun, 10 May 2026 00:00:00 +0000 Subject: [PATCH 4/4] cdx: stash userspace fman nested pointers before cleanup cdx_ioc_set_dpa_params() copies struct cdx_fman_info from userspace. The nested portinfo and tbl_info members are userspace pointers at that point, but release_cfg_info() treats non-NULL nested pointers as kernel-owned allocations. If setup fails before get_port_info() or get_cctbl_info() replaces those members with kernel allocations, the error path can kfree a userspace address and panic in kfree()/virt_to_folio(). Stash the userspace pointers in temporary arrays, clear the nested members in fman_info immediately, and pass the stashed pointers into the copy helpers explicitly. Also route early setup failures through the same cleanup path so partial state is released consistently. --- diff --git a/cdx/dpa_cfg.c b/cdx/dpa_cfg.c index c678d5d..55b910d 100644 --- a/cdx/dpa_cfg.c +++ b/cdx/dpa_cfg.c @@ -267,10 +267,9 @@ static void *get_dist_info_by_fman_params(struct cdx_fman_info *finfo, uint32_t #endif //CDX_RTP_RELAY //allocate and copy port releated info from uspace -static int get_port_info(struct cdx_fman_info *finfo) +static int get_port_info(struct cdx_fman_info *finfo, void *uspace_info) { struct cdx_port_info *port_info; - void *uspace_info; void **uspace_dist_info; uint32_t mem_size; uint32_t ii; @@ -298,7 +297,6 @@ static int get_port_info(struct cdx_fman_info *finfo) 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", @@ -355,7 +353,7 @@ static int get_port_info(struct cdx_fman_info *finfo) } port_info = finfo->portinfo; for (ii = 0; ii < finfo->max_ports; ii++) { - int retval; + int retval = 0; //get dist info for this port retval = get_dist_info(port_info, uspace_dist_info[ii]); if (retval) { @@ -369,11 +367,10 @@ static int get_port_info(struct cdx_fman_info *finfo) } //allocate and copy cc table infor from uspace -static int get_cctbl_info(struct cdx_fman_info *finfo) +static int get_cctbl_info(struct cdx_fman_info *finfo, void *uspace_info) { struct table_info *tbl_info; uint32_t mem_size; - void *uspace_info; //allocate table information area mem_size = (sizeof(struct table_info) * finfo->num_tables); @@ -384,7 +381,6 @@ static int get_cctbl_info(struct cdx_fman_info *finfo) return -ENOMEM; } memset(tbl_info, 0, mem_size); - uspace_info = finfo->tbl_info; finfo->tbl_info = tbl_info; //copy table related info from user space if (copy_from_user(tbl_info, (void *)uspace_info, mem_size)) { @@ -625,9 +621,11 @@ int cdx_ioc_set_dpa_params(unsigned long args) { struct cdx_ctrl_set_dpa_params params; struct cdx_fman_info *finfo; + void **uspace_port_info = NULL; + void **uspace_tbl_info = NULL; uint32_t ii; uint32_t mem_size; - int retval; + int retval = 0; if (copy_from_user(¶ms, (void *)args, sizeof(struct cdx_ctrl_set_dpa_params))) { @@ -655,6 +653,35 @@ int cdx_ioc_set_dpa_params(unsigned long args) retval = -EIO; goto err_ret; } + uspace_port_info = kcalloc(num_fmans, sizeof(*uspace_port_info), + GFP_KERNEL); + uspace_tbl_info = kcalloc(num_fmans, sizeof(*uspace_tbl_info), + GFP_KERNEL); + if (!uspace_port_info || !uspace_tbl_info) { + DPA_ERROR("%s::unable to allocate user pointer stash\n", + __func__); + for (ii = 0; ii < num_fmans; ii++) { + fman_info[ii].portinfo = NULL; + fman_info[ii].tbl_info = NULL; + } + retval = -ENOMEM; + goto err_ret; + } + + /* + * fman_info is copied from userspace. Its nested portinfo and + * tbl_info members are userspace pointers until get_port_info() and + * get_cctbl_info() replace them with kernel allocations. Never leave + * raw userspace pointers in fman_info, because release_cfg_info() owns + * and frees non-NULL nested pointers on error paths. + */ + for (ii = 0; ii < num_fmans; ii++) { + uspace_port_info[ii] = fman_info[ii].portinfo; + uspace_tbl_info[ii] = fman_info[ii].tbl_info; + fman_info[ii].portinfo = NULL; + fman_info[ii].tbl_info = NULL; + } + if (copy_from_user(&ipr_info, (void *)params.ipr_info, sizeof(struct cdx_ipr_info))) { DPA_ERROR("%s::Read iprv_info failed\n", @@ -665,22 +688,26 @@ int cdx_ioc_set_dpa_params(unsigned long args) //init the fman handles finfo = fman_info; for (ii = 0; ii < num_fmans; ii++) { - if (cdxdrv_get_fman_handles(finfo)) - return -1; + if (cdxdrv_get_fman_handles(finfo)) { + retval = -EIO; + goto err_ret; + } finfo++; } finfo = fman_info; //init interface stats module - if (cdxdrv_init_stats(finfo->muram_handle)) - return -1; + if (cdxdrv_init_stats(finfo->muram_handle)) { + retval = -EIO; + goto err_ret; + } for (ii = 0; ii < num_fmans; ii++) { //get port info - retval = get_port_info(finfo); + retval = get_port_info(finfo, uspace_port_info[ii]); if (retval) goto err_ret; //get cc table info - retval = get_cctbl_info(finfo); + retval = get_cctbl_info(finfo, uspace_tbl_info[ii]); if (retval) goto err_ret; finfo++; @@ -727,29 +754,43 @@ int cdx_ioc_set_dpa_params(unsigned long args) finfo++; } - if (cdx_create_port_fqs()) - return -1; + if (cdx_create_port_fqs()) { + retval = -EIO; + goto err_ret; + } //create cp rate limit policier profiles if (cdxdrv_create_missaction_policer_profiles(fman_info)) { + retval = -EIO; goto err_ret; } #ifdef ENABLE_INGRESS_QOS if (cdxdrv_create_ingress_qos_policer_profiles(fman_info)) { + retval = -EIO; goto err_ret; } #endif #ifdef ENABLE_EGRESS_QOS - if(ceetm_init_cq_plcr()) + if(ceetm_init_cq_plcr()) { + retval = -EIO; goto err_ret; + } #endif //init the fman and its ports for (ii = 0; ii < num_fmans; ii++) { - if (cdxdrv_set_miss_action(ii)) + if (cdxdrv_set_miss_action(ii)) { + retval = -EIO; goto err_ret; + } } display_dpa_cfg(); + kfree(uspace_port_info); + kfree(uspace_tbl_info); return 0; err_ret: + DPA_ERROR("%s::error path retval %d, releasing partial DPA cfg\n", + __func__, retval); + kfree(uspace_port_info); + kfree(uspace_tbl_info); release_cfg_info(); return retval; } -- 2.39.5