Hi Daniel and Lukasz, On Tue, Feb 27, 2024 at 04:37:36PM +0100, Daniel Lezcano wrote: > On 27/02/2024 12:09, Rafael J. Wysocki wrote: > > On Tue, Feb 27, 2024 at 11:14 AM Daniel Lezcano > > <daniel.lezc...@linaro.org> wrote: > > > > > > On 27/02/2024 01:54, Nathan Chancellor wrote: > > > > When booting a CONFIG_FORTIFY_SOURCE=y kernel compiled with a toolchain > > > > that supports __counted_by() (such as clang-18 and newer), there is a > > > > panic on boot: > > > > > > > > [ 2.913770] memcpy: detected buffer overflow: 72 byte write of > > > > buffer size 0 > > > > [ 2.920834] WARNING: CPU: 2 PID: 1 at lib/string_helpers.c:1027 > > > > __fortify_report+0x5c/0x74 > > > > ... > > > > [ 3.039208] Call trace: > > > > [ 3.041643] __fortify_report+0x5c/0x74 > > > > [ 3.045469] __fortify_panic+0x18/0x20 > > > > [ 3.049209] thermal_zone_device_register_with_trips+0x4c8/0x4f8 > > > > > > > > This panic occurs because trips is counted by num_trips but num_trips is > > > > assigned after the call to memcpy(), so the fortify checks think the > > > > buffer size is zero because tz was allocated with kzalloc(). > > > > > > > > Move the num_trips assignment before the memcpy() to resolve the panic > > > > and ensure that the fortify checks work properly. > > > > > > > > Fixes: 9b0a62758665 ("thermal: core: Store zone trips table in struct > > > > thermal_zone_device") > > > > Signed-off-by: Nathan Chancellor <nat...@kernel.org> > > > > --- > > > > drivers/thermal/thermal_core.c | 2 +- > > > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > > > > > diff --git a/drivers/thermal/thermal_core.c > > > > b/drivers/thermal/thermal_core.c > > > > index bb21f78b4bfa..1eabc8ebe27d 100644 > > > > --- a/drivers/thermal/thermal_core.c > > > > +++ b/drivers/thermal/thermal_core.c > > > > @@ -1354,8 +1354,8 @@ thermal_zone_device_register_with_trips(const > > > > char *type, > > > > > > > > tz->device.class = thermal_class; > > > > tz->devdata = devdata; > > > > - memcpy(tz->trips, trips, num_trips * sizeof(*trips)); > > > > tz->num_trips = num_trips; > > > > + memcpy(tz->trips, trips, num_trips * sizeof(*trips)); > > > > > > IIUC, clang-18 is used and supports __counted_by(). > > > > > > Is it possible sizeof(*trips) returns already the real trips array size > > > and we are multiplying it again by num_trips ? > > > > > > While with an older compiler, __counted_by() does nothing and we have to > > > multiply by num_trips ? > > > > > > IOW, the array size arithmetic is different depending if we have > > > _counted_by supported or not ? > > > > IIUC it is just the instrumentation using the current value of > > tz->num_trips (which is 0 before the initialization). > > Right, but I am wondering if > > memcpy(tz->trips, trips, num_trips * sizeof(*trips)); > > is still correct with __counted_by because: > > (1) if the compiler supports it: > > sizeof(*trips) == 24 bytes * num_trips
No, this is incorrect. sizeof(*trips) == sizeof(struct thermal_trip), which is never affected by __counted_by. As far as I am aware, sizeof() in general is never affected by __counted_by because calling sizeof() on a flexible array member (which are the only things currently allowed to have __counted_by) is invalid because a FAM is by definition an unknown size at compile time. > then: > > memcpy(tz->trips, trips, num_trips * sizeof(*trips)); > > memcpy(tz->trips, trips, num_trips * 24 * num_trips); > > ==> memory size = 24 * num_trips^2 > > (2) if the compiler does not support it: > > sizeof(*trips) == 24 bytes > > then: > > memcpy(tz->trips, trips, num_trips * sizeof(*trips)); > > memcpy(tz->trips, trips, num_trips * 24); > > ==> memory size = 24 * num_trips > > Or did I misunderstand __counted_by ? I see there has been some confusion around this change from both yourself and Lukasz, so I will try to make things as clear and concise as I can. The fortify routines in the kernel rely on __builtin_dynamic_object_size to get the size of flexible array members (see the macro soup around memcpy() in include/linux/fortify-string.h). Before __counted_by, __builtin_dynamic_object_size() on a FAM would return -1. With __counted_by, __builtin_dynamic_object_size() will be able to return the correct size of the FAM by looking at the counted by member. In this case, memcpy() was called with tz->num_trips (the counted by member) == 0 (meaning that __bdos() will return 0) while trying to copy 'sizeof(*trips) * num_trips' bytes into it, triggering the fortify panic routine. Moving the tz->num_trips assignment before the memcpy() call ensures that __bdos() returns the correct amount for checking in the fortify routines. Hope that helps clear things up, I will make sure to write a clearer commit message next time. Cheers, Nathan