Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

qcom: soc: llcc-slice: Clear the global drv_data pointer on error

Currently the data structure for llc-slice is devm allocated and
stored as a global but never cleared if the probe function fails.
This is a problem because devm managed memory gets freed on probe
failure the API functions could access the pointer after it has been
freed.

Initialize the drv_data pointer to an error and reset it to an error
on probe failure or device destroy and add protection to the API
functions to make sure the memory doesn't get accessed.

Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
Signed-off-by: Andy Gross <andy.gross@linaro.org>

authored by

Jordan Crouse and committed by
Andy Gross
72d1cd03 4e2256d3

+66 -17
+6
drivers/soc/qcom/llcc-sdm845.c
··· 71 71 SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), 72 72 }; 73 73 74 + static int sdm845_qcom_llcc_remove(struct platform_device *pdev) 75 + { 76 + return qcom_llcc_remove(pdev); 77 + } 78 + 74 79 static int sdm845_qcom_llcc_probe(struct platform_device *pdev) 75 80 { 76 81 return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); ··· 92 87 .of_match_table = sdm845_qcom_llcc_of_match, 93 88 }, 94 89 .probe = sdm845_qcom_llcc_probe, 90 + .remove = sdm845_qcom_llcc_remove, 95 91 }; 96 92 module_platform_driver(sdm845_qcom_llcc_driver); 97 93
+54 -17
drivers/soc/qcom/llcc-slice.c
··· 46 46 47 47 #define BANK_OFFSET_STRIDE 0x80000 48 48 49 - static struct llcc_drv_data *drv_data; 49 + static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; 50 50 51 51 static const struct regmap_config llcc_regmap_config = { 52 52 .reg_bits = 32, ··· 67 67 const struct llcc_slice_config *cfg; 68 68 struct llcc_slice_desc *desc; 69 69 u32 sz, count; 70 + 71 + if (IS_ERR(drv_data)) 72 + return ERR_CAST(drv_data); 70 73 71 74 cfg = drv_data->cfg; 72 75 sz = drv_data->cfg_size; ··· 111 108 u32 slice_status; 112 109 int ret; 113 110 111 + if (IS_ERR(drv_data)) 112 + return PTR_ERR(drv_data); 113 + 114 114 act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid); 115 115 status_reg = LLCC_TRP_STATUSn(sid); 116 116 ··· 148 142 { 149 143 int ret; 150 144 u32 act_ctrl_val; 145 + 146 + If (IS_ERR(drv_data)) 147 + return PTR_ERR(drv_data); 151 148 152 149 if (IS_ERR_OR_NULL(desc)) 153 150 return -EINVAL; ··· 188 179 { 189 180 u32 act_ctrl_val; 190 181 int ret; 182 + 183 + If (IS_ERR(drv_data)) 184 + return PTR_ERR(drv_data); 191 185 192 186 if (IS_ERR_OR_NULL(desc)) 193 187 return -EINVAL; ··· 301 289 return ret; 302 290 } 303 291 292 + int qcom_llcc_remove(struct platform_device *pdev) 293 + { 294 + /* Set the global pointer to a error code to avoid referencing it */ 295 + drv_data = ERR_PTR(-ENODEV); 296 + return 0; 297 + } 298 + EXPORT_SYMBOL_GPL(qcom_llcc_remove); 299 + 304 300 int qcom_llcc_probe(struct platform_device *pdev, 305 301 const struct llcc_slice_config *llcc_cfg, u32 sz) 306 302 { ··· 320 300 struct platform_device *llcc_edac; 321 301 322 302 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); 323 - if (!drv_data) 324 - return -ENOMEM; 303 + if (!drv_data) { 304 + ret = -ENOMEM; 305 + goto err; 306 + } 325 307 326 308 llcc_banks_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 327 309 "llcc_base"); 328 310 llcc_banks_base = devm_ioremap_resource(&pdev->dev, llcc_banks_res); 329 - if (IS_ERR(llcc_banks_base)) 330 - return PTR_ERR(llcc_banks_base); 311 + if (IS_ERR(llcc_banks_base)) { 312 + ret = PTR_ERR(llcc_banks_base); 313 + goto err; 314 + } 331 315 332 316 drv_data->regmap = devm_regmap_init_mmio(dev, llcc_banks_base, 333 317 &llcc_regmap_config); 334 - if (IS_ERR(drv_data->regmap)) 335 - return PTR_ERR(drv_data->regmap); 318 + if (IS_ERR(drv_data->regmap)) { 319 + ret = PTR_ERR(drv_data->regmap); 320 + goto err; 321 + } 336 322 337 323 llcc_bcast_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 338 324 "llcc_broadcast_base"); 339 325 llcc_bcast_base = devm_ioremap_resource(&pdev->dev, llcc_bcast_res); 340 - if (IS_ERR(llcc_bcast_base)) 341 - return PTR_ERR(llcc_bcast_base); 326 + if (IS_ERR(llcc_bcast_base)) { 327 + ret = PTR_ERR(llcc_bcast_base); 328 + goto err; 329 + } 342 330 343 331 drv_data->bcast_regmap = devm_regmap_init_mmio(dev, llcc_bcast_base, 344 332 &llcc_regmap_config); 345 - if (IS_ERR(drv_data->bcast_regmap)) 346 - return PTR_ERR(drv_data->bcast_regmap); 333 + if (IS_ERR(drv_data->bcast_regmap)) { 334 + ret = PTR_ERR(drv_data->bcast_regmap); 335 + goto err; 336 + } 347 337 348 338 ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0, 349 339 &num_banks); 350 340 if (ret) 351 - return ret; 341 + goto err; 352 342 353 343 num_banks &= LLCC_LB_CNT_MASK; 354 344 num_banks >>= LLCC_LB_CNT_SHIFT; ··· 370 340 371 341 drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), 372 342 GFP_KERNEL); 373 - if (!drv_data->offsets) 374 - return -ENOMEM; 343 + if (!drv_data->offsets) { 344 + ret = -ENOMEM; 345 + goto err; 346 + } 375 347 376 348 for (i = 0; i < num_banks; i++) 377 349 drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; ··· 381 349 drv_data->bitmap = devm_kcalloc(dev, 382 350 BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long), 383 351 GFP_KERNEL); 384 - if (!drv_data->bitmap) 385 - return -ENOMEM; 352 + if (!drv_data->bitmap) { 353 + ret = -ENOMEM; 354 + goto err; 355 + } 386 356 387 357 drv_data->cfg = llcc_cfg; 388 358 drv_data->cfg_size = sz; ··· 393 359 394 360 ret = qcom_llcc_cfg_program(pdev); 395 361 if (ret) 396 - return ret; 362 + goto err; 397 363 398 364 drv_data->ecc_irq = platform_get_irq(pdev, 0); 399 365 if (drv_data->ecc_irq >= 0) { ··· 404 370 dev_err(dev, "Failed to register llcc edac driver\n"); 405 371 } 406 372 373 + return 0; 374 + err: 375 + drv_data = ERR_PTR(-ENODEV); 407 376 return ret; 408 377 } 409 378 EXPORT_SYMBOL_GPL(qcom_llcc_probe);
+6
include/linux/soc/qcom/llcc-qcom.h
··· 162 162 */ 163 163 int qcom_llcc_probe(struct platform_device *pdev, 164 164 const struct llcc_slice_config *table, u32 sz); 165 + 166 + /** 167 + * qcom_llcc_remove - remove the sct table 168 + * @pdev: Platform device pointer 169 + */ 170 + int qcom_llcc_remove(struct platform_device *pdev); 165 171 #else 166 172 static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid) 167 173 {