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

coresight: Fix support for sparsely populated ports

On some systems the firmware may not describe all the ports
connected to a component (e.g, for security reasons). This
could be especially problematic for "funnels" where we could
end up in modifying memory beyond the allocated space for
refcounts.

e.g, for a funnel with input ports listed 0, 3, 5, nr_inport = 3.
However the we could access refcnts[5] while checking for
references, like :

[ 526.110401] ==================================================================
[ 526.117988] BUG: KASAN: slab-out-of-bounds in funnel_enable+0x54/0x1b0
[ 526.124706] Read of size 4 at addr ffffff8135f9549c by task bash/1114
[ 526.131324]
[ 526.132886] CPU: 3 PID: 1114 Comm: bash Tainted: G S 5.4.25 #232
[ 526.140397] Hardware name: Qualcomm Technologies, Inc. SC7180 IDP (DT)
[ 526.147113] Call trace:
[ 526.149653] dump_backtrace+0x0/0x188
[ 526.153431] show_stack+0x20/0x2c
[ 526.156852] dump_stack+0xdc/0x144
[ 526.160370] print_address_description+0x3c/0x494
[ 526.165211] __kasan_report+0x144/0x168
[ 526.169170] kasan_report+0x10/0x18
[ 526.172769] check_memory_region+0x1a4/0x1b4
[ 526.177164] __kasan_check_read+0x18/0x24
[ 526.181292] funnel_enable+0x54/0x1b0
[ 526.185072] coresight_enable_path+0x104/0x198
[ 526.189649] coresight_enable+0x118/0x26c

...

[ 526.237782] Allocated by task 280:
[ 526.241298] __kasan_kmalloc+0xf0/0x1ac
[ 526.245249] kasan_kmalloc+0xc/0x14
[ 526.248849] __kmalloc+0x28c/0x3b4
[ 526.252361] coresight_register+0x88/0x250
[ 526.256587] funnel_probe+0x15c/0x228
[ 526.260365] dynamic_funnel_probe+0x20/0x2c
[ 526.264679] amba_probe+0xbc/0x158
[ 526.268193] really_probe+0x144/0x408
[ 526.271970] driver_probe_device+0x70/0x140

...

[ 526.316810]
[ 526.318364] Freed by task 0:
[ 526.321344] (stack is not available)
[ 526.325024]
[ 526.326580] The buggy address belongs to the object at ffffff8135f95480
[ 526.326580] which belongs to the cache kmalloc-128 of size 128
[ 526.339439] The buggy address is located 28 bytes inside of
[ 526.339439] 128-byte region [ffffff8135f95480, ffffff8135f95500)
[ 526.351399] The buggy address belongs to the page:
[ 526.356342] page:ffffffff04b7e500 refcount:1 mapcount:0 mapping:ffffff814b00c380 index:0x0 compound_mapcount: 0
[ 526.366711] flags: 0x4000000000010200(slab|head)
[ 526.371475] raw: 4000000000010200 ffffffff05034008 ffffffff0501eb08 ffffff814b00c380
[ 526.379435] raw: 0000000000000000 0000000000190019 00000001ffffffff 0000000000000000
[ 526.387393] page dumped because: kasan: bad access detected
[ 526.393128]
[ 526.394681] Memory state around the buggy address:
[ 526.399619] ffffff8135f95380: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 526.407046] ffffff8135f95400: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 526.414473] >ffffff8135f95480: 04 fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 526.421900] ^
[ 526.426029] ffffff8135f95500: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 526.433456] ffffff8135f95580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 526.440883] ==================================================================

To keep the code simple, we now track the maximum number of
possible input/output connections to/from this component
@ nr_inport and nr_outport in platform_data, respectively.
Thus the output connections could be sparse and code is
adjusted to skip the unspecified connections.

Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Cc: Mike Leach <mike.leach@linaro.org>
Reported-by: Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org>
Tested-by: Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org>
Tested-by: Stephen Boyd <swboyd@chromium.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: https://lore.kernel.org/r/20200518180242.7916-13-mathieu.poirier@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Suzuki K Poulose and committed by
Greg Kroah-Hartman
d375b356 1c33c65c

+72 -30
+60 -25
drivers/hwtracing/coresight/coresight-platform.c
··· 87 87 int *nr_inport, int *nr_outport) 88 88 { 89 89 struct device_node *ep = NULL; 90 + struct of_endpoint endpoint; 90 91 int in = 0, out = 0; 91 92 92 93 do { ··· 95 94 if (!ep) 96 95 break; 97 96 98 - if (of_coresight_legacy_ep_is_input(ep)) 99 - in++; 100 - else 101 - out++; 97 + if (of_graph_parse_endpoint(ep, &endpoint)) 98 + continue; 99 + 100 + if (of_coresight_legacy_ep_is_input(ep)) { 101 + in = (endpoint.port + 1 > in) ? 102 + endpoint.port + 1 : in; 103 + } else { 104 + out = (endpoint.port + 1) > out ? 105 + endpoint.port + 1 : out; 106 + } 102 107 103 108 } while (ep); 104 109 ··· 144 137 { 145 138 int i = 0; 146 139 struct device_node *ep = NULL; 140 + struct of_endpoint endpoint; 147 141 148 - while ((ep = of_graph_get_next_endpoint(port_parent, ep))) 149 - i++; 142 + while ((ep = of_graph_get_next_endpoint(port_parent, ep))) { 143 + /* Defer error handling to parsing */ 144 + if (of_graph_parse_endpoint(ep, &endpoint)) 145 + continue; 146 + if (endpoint.port + 1 > i) 147 + i = endpoint.port + 1; 148 + } 149 + 150 150 return i; 151 151 } 152 152 ··· 205 191 * Parses the local port, remote device name and the remote port. 206 192 * 207 193 * Returns : 208 - * 1 - If the parsing is successful and a connection record 209 - * was created for an output connection. 210 194 * 0 - If the parsing completed without any fatal errors. 211 195 * -Errno - Fatal error, abort the scanning. 212 196 */ 213 197 static int of_coresight_parse_endpoint(struct device *dev, 214 198 struct device_node *ep, 215 - struct coresight_connection *conn) 199 + struct coresight_platform_data *pdata) 216 200 { 217 201 int ret = 0; 218 202 struct of_endpoint endpoint, rendpoint; ··· 218 206 struct device_node *rep = NULL; 219 207 struct device *rdev = NULL; 220 208 struct fwnode_handle *rdev_fwnode; 209 + struct coresight_connection *conn; 221 210 222 211 do { 223 212 /* Parse the local port details */ ··· 245 232 break; 246 233 } 247 234 235 + conn = &pdata->conns[endpoint.port]; 236 + if (conn->child_fwnode) { 237 + dev_warn(dev, "Duplicate output port %d\n", 238 + endpoint.port); 239 + ret = -EINVAL; 240 + break; 241 + } 248 242 conn->outport = endpoint.port; 249 243 /* 250 244 * Hold the refcount to the target device. This could be ··· 264 244 conn->child_fwnode = fwnode_handle_get(rdev_fwnode); 265 245 conn->child_port = rendpoint.port; 266 246 /* Connection record updated */ 267 - ret = 1; 268 247 } while (0); 269 248 270 249 of_node_put(rparent); ··· 277 258 struct coresight_platform_data *pdata) 278 259 { 279 260 int ret = 0; 280 - struct coresight_connection *conn; 281 261 struct device_node *ep = NULL; 282 262 const struct device_node *parent = NULL; 283 263 bool legacy_binding = false; ··· 305 287 dev_warn_once(dev, "Uses obsolete Coresight DT bindings\n"); 306 288 } 307 289 308 - conn = pdata->conns; 309 - 310 290 /* Iterate through each output port to discover topology */ 311 291 while ((ep = of_graph_get_next_endpoint(parent, ep))) { 312 292 /* ··· 316 300 if (legacy_binding && of_coresight_legacy_ep_is_input(ep)) 317 301 continue; 318 302 319 - ret = of_coresight_parse_endpoint(dev, ep, conn); 320 - switch (ret) { 321 - case 1: 322 - conn++; /* Fall through */ 323 - case 0: 324 - break; 325 - default: 303 + ret = of_coresight_parse_endpoint(dev, ep, pdata); 304 + if (ret) 326 305 return ret; 327 - } 328 306 } 329 307 330 308 return 0; ··· 657 647 * coresight_remove_match(). 658 648 */ 659 649 conn->child_fwnode = fwnode_handle_get(&r_adev->fwnode); 650 + } else if (dir == ACPI_CORESIGHT_LINK_SLAVE) { 651 + /* 652 + * We are only interested in the port number 653 + * for the input ports at this component. 654 + * Store the port number in child_port. 655 + */ 656 + conn->child_port = fields[0].integer.value; 657 + } else { 658 + /* Invalid direction */ 659 + return -EINVAL; 660 660 } 661 661 662 662 return dir; ··· 712 692 return dir; 713 693 714 694 if (dir == ACPI_CORESIGHT_LINK_MASTER) { 715 - pdata->nr_outport++; 695 + if (ptr->outport > pdata->nr_outport) 696 + pdata->nr_outport = ptr->outport; 716 697 ptr++; 717 698 } else { 718 - pdata->nr_inport++; 699 + WARN_ON(pdata->nr_inport == ptr->child_port); 700 + /* 701 + * We do not track input port connections for a device. 702 + * However we need the highest port number described, 703 + * which can be recorded now and reuse this connection 704 + * record for an output connection. Hence, do not move 705 + * the ptr for input connections 706 + */ 707 + if (ptr->child_port > pdata->nr_inport) 708 + pdata->nr_inport = ptr->child_port; 719 709 } 720 710 } 721 711 ··· 734 704 return rc; 735 705 736 706 /* Copy the connection information to the final location */ 737 - for (i = 0; i < pdata->nr_outport; i++) 738 - pdata->conns[i] = conns[i]; 707 + for (i = 0; conns + i < ptr; i++) { 708 + int port = conns[i].outport; 709 + 710 + /* Duplicate output port */ 711 + WARN_ON(pdata->conns[port].child_fwnode); 712 + pdata->conns[port] = conns[i]; 713 + } 739 714 740 715 devm_kfree(&adev->dev, conns); 741 716 return 0;
+6 -1
drivers/hwtracing/coresight/coresight.c
··· 1053 1053 for (i = 0; i < i_csdev->pdata->nr_outport; i++) { 1054 1054 conn = &i_csdev->pdata->conns[i]; 1055 1055 1056 + /* Skip the port if FW doesn't describe it */ 1057 + if (!conn->child_fwnode) 1058 + continue; 1056 1059 /* We have found at least one orphan connection */ 1057 1060 if (conn->child_dev == NULL) { 1058 1061 /* Does it match this newly added device? */ ··· 1094 1091 for (i = 0; i < csdev->pdata->nr_outport; i++) { 1095 1092 struct coresight_connection *conn = &csdev->pdata->conns[i]; 1096 1093 1094 + if (!conn->child_fwnode) 1095 + continue; 1097 1096 conn->child_dev = 1098 1097 coresight_find_csdev_by_fwnode(conn->child_fwnode); 1099 1098 if (conn->child_dev) { ··· 1131 1126 for (i = 0; i < iterator->pdata->nr_outport; i++) { 1132 1127 conn = &iterator->pdata->conns[i]; 1133 1128 1134 - if (conn->child_dev == NULL) 1129 + if (conn->child_dev == NULL || conn->child_fwnode == NULL) 1135 1130 continue; 1136 1131 1137 1132 if (csdev->dev.fwnode == conn->child_fwnode) {
+6 -4
include/linux/coresight.h
··· 100 100 }; 101 101 102 102 /** 103 - * struct coresight_platform_data - data harvested from the DT specification 104 - * @nr_inport: number of input ports for this component. 105 - * @nr_outport: number of output ports for this component. 106 - * @conns: Array of nr_outport connections from this component 103 + * struct coresight_platform_data - data harvested from the firmware 104 + * specification. 105 + * 106 + * @nr_inport: Number of elements for the input connections. 107 + * @nr_outport: Number of elements for the output connections. 108 + * @conns: Sparse array of nr_outport connections from this component. 107 109 */ 108 110 struct coresight_platform_data { 109 111 int nr_inport;