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

s390/cio: use generic driver_override infrastructure

When a driver is probed through __driver_attach(), the bus' match()
callback is called without the device lock held, thus accessing the
driver_override field without a lock, which can cause a UAF.

Fix this by using the driver-core driver_override infrastructure taking
care of proper locking internally.

Note that calling match() from __driver_attach() without the device lock
held is intentional. [1]

Link: https://lore.kernel.org/driver-core/DGRGTIRHA62X.3RY09D9SOK77P@kernel.org/ [1]
Reported-by: Gui-Dong Han <hanguidong02@gmail.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789
Fixes: ebc3d1791503 ("s390/cio: introduce driver_override on the css bus")
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Link: https://patch.msgid.link/20260324005919.2408620-10-dakr@kernel.org
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

+4 -35
-5
drivers/s390/cio/cio.h
··· 103 103 struct work_struct todo_work; 104 104 struct schib_config config; 105 105 u64 dma_mask; 106 - /* 107 - * Driver name to force a match. Do not set directly, because core 108 - * frees it. Use driver_set_override() to set or clear it. 109 - */ 110 - const char *driver_override; 111 106 } __attribute__ ((aligned(8))); 112 107 113 108 DECLARE_PER_CPU_ALIGNED(struct irb, cio_irb);
+4 -30
drivers/s390/cio/css.c
··· 159 159 160 160 sch->config.intparm = 0; 161 161 cio_commit_config(sch); 162 - kfree(sch->driver_override); 163 162 kfree(sch); 164 163 } 165 164 ··· 322 323 323 324 static DEVICE_ATTR_RO(modalias); 324 325 325 - static ssize_t driver_override_store(struct device *dev, 326 - struct device_attribute *attr, 327 - const char *buf, size_t count) 328 - { 329 - struct subchannel *sch = to_subchannel(dev); 330 - int ret; 331 - 332 - ret = driver_set_override(dev, &sch->driver_override, buf, count); 333 - if (ret) 334 - return ret; 335 - 336 - return count; 337 - } 338 - 339 - static ssize_t driver_override_show(struct device *dev, 340 - struct device_attribute *attr, char *buf) 341 - { 342 - struct subchannel *sch = to_subchannel(dev); 343 - ssize_t len; 344 - 345 - device_lock(dev); 346 - len = sysfs_emit(buf, "%s\n", sch->driver_override); 347 - device_unlock(dev); 348 - return len; 349 - } 350 - static DEVICE_ATTR_RW(driver_override); 351 - 352 326 static struct attribute *subch_attrs[] = { 353 327 &dev_attr_type.attr, 354 328 &dev_attr_modalias.attr, 355 - &dev_attr_driver_override.attr, 356 329 NULL, 357 330 }; 358 331 ··· 1327 1356 struct subchannel *sch = to_subchannel(dev); 1328 1357 const struct css_driver *driver = to_cssdriver(drv); 1329 1358 struct css_device_id *id; 1359 + int ret; 1330 1360 1331 1361 /* When driver_override is set, only bind to the matching driver */ 1332 - if (sch->driver_override && strcmp(sch->driver_override, drv->name)) 1362 + ret = device_match_driver_override(dev, drv); 1363 + if (ret == 0) 1333 1364 return 0; 1334 1365 1335 1366 for (id = driver->subchannel_type; id->match_flags; id++) { ··· 1388 1415 1389 1416 static const struct bus_type css_bus_type = { 1390 1417 .name = "css", 1418 + .driver_override = true, 1391 1419 .match = css_bus_match, 1392 1420 .probe = css_probe, 1393 1421 .remove = css_remove,