The bug is here:
dev = new_dev->dev;
The list iterator 'new_dev' will point to a bogus position containing
HEAD if the list is empty or no element is found. This case must
be checked before any use of the iterator, otherwise it will lead
to a invalid memory access.
To fix this bug, add an check. Use a new variable 'iter' as the
list iterator, while use the old variable 'new_dev' as a dedicated
pointer to point to the found element.
Cc: [email protected]
Fixes: deaa51465105a ("PM / OPP: Add debugfs support")
Signed-off-by: Xiaomeng Tong <[email protected]>
---
drivers/opp/debugfs.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c
index 596c185b5dda..a4476985e4ce 100644
--- a/drivers/opp/debugfs.c
+++ b/drivers/opp/debugfs.c
@@ -187,14 +187,19 @@ void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
static void opp_migrate_dentry(struct opp_device *opp_dev,
struct opp_table *opp_table)
{
- struct opp_device *new_dev;
+ struct opp_device *new_dev = NULL, *iter;
const struct device *dev;
struct dentry *dentry;
/* Look for next opp-dev */
- list_for_each_entry(new_dev, &opp_table->dev_list, node)
- if (new_dev != opp_dev)
+ list_for_each_entry(iter, &opp_table->dev_list, node)
+ if (iter != opp_dev) {
+ new_dev = iter;
break;
+ }
+
+ if (!new_dev)
+ return;
/* new_dev is guaranteed to be valid here */
dev = new_dev->dev;
--
2.17.1
On 27-03-22, 13:39, Xiaomeng Tong wrote:
> The bug is here:
> dev = new_dev->dev;
>
> The list iterator 'new_dev' will point to a bogus position containing
> HEAD if the list is empty or no element is found. This case must
> be checked before any use of the iterator, otherwise it will lead
> to a invalid memory access.
>
> To fix this bug, add an check. Use a new variable 'iter' as the
> list iterator, while use the old variable 'new_dev' as a dedicated
> pointer to point to the found element.
>
> Cc: [email protected]
> Fixes: deaa51465105a ("PM / OPP: Add debugfs support")
> Signed-off-by: Xiaomeng Tong <[email protected]>
> ---
> drivers/opp/debugfs.c | 11 ++++++++---
> 1 file changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c
> index 596c185b5dda..a4476985e4ce 100644
> --- a/drivers/opp/debugfs.c
> +++ b/drivers/opp/debugfs.c
> @@ -187,14 +187,19 @@ void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
> static void opp_migrate_dentry(struct opp_device *opp_dev,
> struct opp_table *opp_table)
> {
> - struct opp_device *new_dev;
> + struct opp_device *new_dev = NULL, *iter;
> const struct device *dev;
> struct dentry *dentry;
>
> /* Look for next opp-dev */
> - list_for_each_entry(new_dev, &opp_table->dev_list, node)
> - if (new_dev != opp_dev)
> + list_for_each_entry(iter, &opp_table->dev_list, node)
> + if (iter != opp_dev) {
> + new_dev = iter;
> break;
> + }
> +
> + if (!new_dev)
> + return;
I think you missed this check in the parent function ?
if (!list_is_singular(&opp_table->dev_list)) {
i.e. this bug can never happen.
--
viresh
On Mon, 28 Mar 2022 14:20:57 +0530, Viresh Kumar wrote:
> On 28-03-22, 15:43, Xiaomeng Tong wrote:
> > No. the conditon to call opp_migrate_dentry(opp_dev, opp_table); is:
> > if (!list_is_singular(&opp_table->dev_list)),
> >
> > while list_is_singlular is: !list_empty(head) && (head->next == head->prev);
> >
> > so the condition is: list_empty(head) || (head->next != head->prev)
> >
> > if the list is empty, the bug can be triggered.
>
> List can't be empty here by design. It will be a huge bug in that
> case, which should lead to crash somewhere.
>
There is anther condition to trigger this bug: the list is not empty and
no element found (if (iter != opp_dev)).
--
Xiaomeng Tong
On Mon, 28 Mar 2022 08:47:39 +0530, Viresh Kumar wrote:
> > diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c
> > index 596c185b5dda..a4476985e4ce 100644
> > --- a/drivers/opp/debugfs.c
> > +++ b/drivers/opp/debugfs.c
> > @@ -187,14 +187,19 @@ void opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
> > static void opp_migrate_dentry(struct opp_device *opp_dev,
> > struct opp_table *opp_table)
> > {
> > - struct opp_device *new_dev;
> > + struct opp_device *new_dev = NULL, *iter;
> > const struct device *dev;
> > struct dentry *dentry;
> >
> > /* Look for next opp-dev */
> > - list_for_each_entry(new_dev, &opp_table->dev_list, node)
> > - if (new_dev != opp_dev)
> > + list_for_each_entry(iter, &opp_table->dev_list, node)
> > + if (iter != opp_dev) {
> > + new_dev = iter;
> > break;
> > + }
> > +
> > + if (!new_dev)
> > + return;
>
> I think you missed this check in the parent function ?
>
> if (!list_is_singular(&opp_table->dev_list)) {
>
>
> i.e. this bug can never happen.
>
No. the conditon to call opp_migrate_dentry(opp_dev, opp_table); is:
if (!list_is_singular(&opp_table->dev_list)),
while list_is_singlular is: !list_empty(head) && (head->next == head->prev);
so the condition is: list_empty(head) || (head->next != head->prev)
if the list is empty, the bug can be triggered.
--
Xiaomeng Tong
On 28-03-22, 17:13, Xiaomeng Tong wrote:
> On Mon, 28 Mar 2022 14:20:57 +0530, Viresh Kumar wrote:
> > On 28-03-22, 15:43, Xiaomeng Tong wrote:
> > > No. the conditon to call opp_migrate_dentry(opp_dev, opp_table); is:
> > > if (!list_is_singular(&opp_table->dev_list)),
> > >
> > > while list_is_singlular is: !list_empty(head) && (head->next == head->prev);
> > >
> > > so the condition is: list_empty(head) || (head->next != head->prev)
> > >
> > > if the list is empty, the bug can be triggered.
> >
> > List can't be empty here by design. It will be a huge bug in that
> > case, which should lead to crash somewhere.
> >
>
> There is anther condition to trigger this bug: the list is not empty and
> no element found (if (iter != opp_dev)).
I suggest reading the code again, considering opp_debug_unregister()
as well.
What's happening here is this:
- Several devices share the OPP table.
- One of them (devX) is going away and opp_debug_unregister() is called for this device.
- If devX is the last device for this OPP table, then we don't migrate
and just release all resources.
- Otherwise, we migrate it to the next element in the list. i.e. any
device which != devX.
Please tell based on this where do you see a possibility of a bug.
Surely there can be one, but I fail to see it at the moment and need
more detail of the same.
Thanks Xiaomeng.
--
viresh
On 28-03-22, 15:43, Xiaomeng Tong wrote:
> No. the conditon to call opp_migrate_dentry(opp_dev, opp_table); is:
> if (!list_is_singular(&opp_table->dev_list)),
>
> while list_is_singlular is: !list_empty(head) && (head->next == head->prev);
>
> so the condition is: list_empty(head) || (head->next != head->prev)
>
> if the list is empty, the bug can be triggered.
List can't be empty here by design. It will be a huge bug in that
case, which should lead to crash somewhere.
--
viresh
On Mon, 28 Mar 2022 15:09:33 +0530, Viresh Kumar wrote:
> On 28-03-22, 17:13, Xiaomeng Tong wrote:
> > On Mon, 28 Mar 2022 14:20:57 +0530, Viresh Kumar wrote:
> > > On 28-03-22, 15:43, Xiaomeng Tong wrote:
> > > > No. the conditon to call opp_migrate_dentry(opp_dev, opp_table); is:
> > > > if (!list_is_singular(&opp_table->dev_list)),
> > > >
> > > > while list_is_singlular is: !list_empty(head) && (head->next == head->prev);
> > > >
> > > > so the condition is: list_empty(head) || (head->next != head->prev)
> > > >
> > > > if the list is empty, the bug can be triggered.
> > >
> > > List can't be empty here by design. It will be a huge bug in that
> > > case, which should lead to crash somewhere.
> > >
> >
> > There is anther condition to trigger this bug: the list is not empty and
> > no element found (if (iter != opp_dev)).
>
> I suggest reading the code again, considering opp_debug_unregister()
> as well.
>
> What's happening here is this:
>
> - Several devices share the OPP table.
> - One of them (devX) is going away and opp_debug_unregister() is called for this device.
> - If devX is the last device for this OPP table, then we don't migrate
> and just release all resources.
> - Otherwise, we migrate it to the next element in the list. i.e. any
> device which != devX.
>
> Please tell based on this where do you see a possibility of a bug.
> Surely there can be one, but I fail to see it at the moment and need
> more detail of the same.
>
Perhaps you are right. Anyway, It is a good choise to use list iterator
only inside the loop as linus suggested [1], to avoid potential risk.
I have also repost another patch with changed commit message. Please
check it, thank you.
[1]:https://lore.kernel.org/lkml/[email protected]/
--
Xiaomeng Tong