lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <2053a9ea-a647-89f4-497c-1141ac3e0fa7@metafoo.de>
Date:   Mon, 27 Dec 2021 13:16:56 +0100
From:   Lars-Peter Clausen <lars@...afoo.de>
To:     Uwe Kleine-König <u.kleine-koenig@...gutronix.de>,
        William Breathitt Gray <vilhelm.gray@...il.com>
Cc:     kernel@...gutronix.de,
        Jonathan Cameron <Jonathan.Cameron@...wei.com>,
        linux-iio@...r.kernel.org,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        linux-kernel@...r.kernel.org
Subject: Re: [PATCH v2 13/23] counter: Provide alternative counter
 registration functions

On 12/27/21 10:45 AM, Uwe Kleine-König wrote:
> The current implementation gets device lifetime tracking wrong. The
> problem is that allocation of struct counter_device is controlled by the
> individual drivers but this structure contains a struct device that
> might have to live longer than a driver is bound. As a result a command
> sequence like:
>
> 	{ sleep 5; echo bang; } > /dev/counter0 &
> 	sleep 1;
> 	echo 40000000.timer:counter > /sys/bus/platform/drivers/stm32-timer-counter/unbind
>
> can keep a reference to the struct device and unbinding results in
> freeing the memory occupied by this device resulting in an oops.
>
> This commit provides two new functions (plus some helpers):
>   - counter_alloc() to allocate a struct counter_device that is
>     automatically freed once the embedded struct device is released
>   - counter_add() to register such a device.
>
> Note that this commit doesn't fix any issues, all drivers have to be
> converted to these new functions to correct the lifetime problems.
Nice work!


> [...]
> @@ -24,6 +25,11 @@
>   /* Provides a unique ID for each counter device */
>   static DEFINE_IDA(counter_ida);
>   
> +struct counter_device_allochelper {
> +	struct counter_device counter;
> +	unsigned long privdata[];

The normal k*alloc functions guarantee that the allocation is cacheline 
aligned. Without that for example the ____cacheline_aligned attribute 
will not work correctly. And while it is unlikely that a counter driver 
will need for example DMA memory I think it is still good practice to 
guarantee this here to avoid bad surprises. There are two ways of doing 
this.

Make privdata ____cacheline_aligned like in the memstick framework[1]. 
The downside is that this will reserve padding for the allocation, even 
if no private data is allocated.

The alternative is to do something similar to IIO[2] which only 
allocates the padding if there actually is any private data requested. 
The disadvantage of that is that the code is a bit more cumbersome. And 
most drivers will want to have some private data anyway so it might not 
be worth it.

[1] 
https://elixir.bootlin.com/linux/v5.16-rc7/source/include/linux/memstick.h#L292
[2] 
https://elixir.bootlin.com/linux/v5.16-rc7/source/drivers/iio/industrialio-core.c#L1638

> [...]
> +struct counter_device *counter_alloc(size_t sizeof_priv)
I'd add a parent parameter here since with the devm_ variant we have to 
pass it anyway and this allows to slightly reduce the boilerplate code 
in the driver where the parent field of the counter has to be initialized.
> +{
> +	struct counter_device_allochelper *ch;
> +	struct counter_device *counter;
> +	struct device *dev;
> +	int id, err;
> +
> +	ch = kzalloc(sizeof(*ch) + sizeof_priv, GFP_KERNEL);
> +	if (!ch) {
> +		err = -ENOMEM;
> +		goto err_alloc_ch;
> +	}
> +
> +	counter = &ch->counter;
> +	dev = &counter->dev;
> +
> +	/* Acquire unique ID */
> +	err = ida_alloc(&counter_ida, GFP_KERNEL);
> +	if (err < 0) {
> +		goto err_ida_alloc;
> +	}
There is a inconsistency whether braces are used for single statement 
`if`s in this patch.
> +	dev->id = err;
> +
> +	err = counter_chrdev_add(counter);
> +	if (err < 0)
> +		goto err_chrdev_add;
> +
> +	device_initialize(dev);
> +	/* Configure device structure for Counter */
> +	dev->type = &counter_device_type;
> +	dev->bus = &counter_bus_type;
> +	dev->devt = MKDEV(MAJOR(counter_devt), id);
> +
> +	mutex_init(&counter->ops_exist_lock);
> +
> +	return counter;
> +
> +err_chrdev_add:
> +
> +	ida_free(&counter_ida, dev->id);
> +err_ida_alloc:
> +
> +	kfree(ch);
> +err_alloc_ch:
> +
> +	return ERR_PTR(err);
> +}
> +EXPORT_SYMBOL_GPL(counter_alloc);
> +
> +void counter_put(struct counter_device *counter)
> +{
> +	put_device(&counter->dev);
> +}
> +
> +/**
> + * counter_add - complete registration of a counter
> + * @counter: the counter to add
> + *
> + * This is part two of counter registration.
> + *
> + * If this succeeds, call counter_unregister() to get rid of the counter_device again.
> + */
> +int counter_add(struct counter_device *counter)
> +{
> +	int err;
> +	struct device *dev = &counter->dev;
> +
> +	get_device(&counter->dev);

This get_device() is not needed. device_add() will also add a reference, 
so you increment the reference count by 2 when calling counter_add().

It is only needed to balance the put_device() in counter_unregister(). 
I'd add a `counter->legacy_device` around that put_device() and then 
remove it in the last patch.

The extra get_device() here is also leaked on the error paths, so 
removing it will allow to keep the errors paths as they are.

> +
> +	if (counter->parent) {
> +		dev->parent = counter->parent;
> +		dev->of_node = counter->parent->of_node;
> +	}
> +
> +	err = counter_sysfs_add(counter);
> +	if (err < 0)
> +		return err;
> +
> +	/* implies device_add(dev) */
> +	err = cdev_device_add(&counter->chrdev, dev);
> +
> +	return err;
return cdev_device_add(...).
> +struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv)
API Documentation?
> +{
> +	struct counter_device *counter;
> +	int err;
> +
> +	counter = counter_alloc(sizeof_priv);
> +	if (IS_ERR(counter))
> +		return counter;
> +
> +	err = devm_add_action_or_reset(dev, devm_counter_put, counter);
> +	if (err < 0)
> +		return ERR_PTR(err);
> +
> +	return counter;
> +}
> +EXPORT_SYMBOL_GPL(devm_counter_alloc);
> +
> +int devm_counter_add(struct device *dev,
> +		     struct counter_device *const counter)
> +{
API Documentation?
> +	int err;
> +
> +	err = counter_add(counter);
> +	if (err < 0)
> +		return err;
> +
> +	return devm_add_action_or_reset(dev, devm_counter_release, counter);
> +}
> +EXPORT_SYMBOL_GPL(devm_counter_add);
> +
>   #define COUNTER_DEV_MAX 256
>   

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ