In preparation to limiting the scope of a list iterator to the list
traversal loop, use a dedicated pointer to iterate through the list [1].
In the current state the list_for_each_entry() is guaranteed to
hit a break or goto in order to work properly. If the list iterator
executes completely or the list is empty the iterator variable contains
a type confused bogus value infered from the head of the list.
With this patch the variable used past the list iterator is only set
if the list exists early and is NULL otherwise. It should therefore
be safe to just set *prev = NULL (as it was before).
Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/
Signed-off-by: Jakob Koschel <[email protected]>
---
drivers/firmware/efi/vars.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index cae590bd08f2..3994aad38661 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -1081,14 +1081,16 @@ int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data,
struct efivar_entry **prev)
{
- struct efivar_entry *entry, *n;
+ struct efivar_entry *entry = NULL, *iter, *n;
int err = 0;
if (!prev || !*prev) {
- list_for_each_entry_safe(entry, n, head, list) {
- err = func(entry, data);
- if (err)
+ list_for_each_entry_safe(iter, n, head, list) {
+ err = func(iter, data);
+ if (err) {
+ entry = iter;
break;
+ }
}
if (prev)
base-commit: d888c83fcec75194a8a48ccd283953bdba7b2550
--
2.25.1
To move the list iterator variable into the list_for_each_entry_*()
macro in the future it should be avoided to use the list iterator
variable after the loop body.
To *never* use the list iterator variable after the loop it was
concluded to use a separate iterator variable instead of a
found boolean [1].
This removes the need to use a found variable and simply checking if
the variable was set, can determine if the break/goto was hit.
Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/
Signed-off-by: Jakob Koschel <[email protected]>
---
drivers/firmware/efi/vars.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 3994aad38661..e4e1cc593441 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -809,22 +809,21 @@ EXPORT_SYMBOL_GPL(efivar_entry_set_safe);
struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
struct list_head *head, bool remove)
{
- struct efivar_entry *entry, *n;
+ struct efivar_entry *entry = NULL, *iter, *n;
int strsize1, strsize2;
- bool found = false;
- list_for_each_entry_safe(entry, n, head, list) {
+ list_for_each_entry_safe(iter, n, head, list) {
strsize1 = ucs2_strsize(name, 1024);
- strsize2 = ucs2_strsize(entry->var.VariableName, 1024);
+ strsize2 = ucs2_strsize(iter->var.VariableName, 1024);
if (strsize1 == strsize2 &&
- !memcmp(name, &(entry->var.VariableName), strsize1) &&
- !efi_guidcmp(guid, entry->var.VendorGuid)) {
- found = true;
+ !memcmp(name, &(iter->var.VariableName), strsize1) &&
+ !efi_guidcmp(guid, iter->var.VendorGuid)) {
+ entry = iter;
break;
}
}
- if (!found)
+ if (!entry)
return NULL;
if (remove) {
--
2.25.1
> On 13. Apr 2022, at 19:05, Ard Biesheuvel <[email protected]> wrote:
>
> On Fri, 1 Apr 2022 at 00:11, Jakob Koschel <[email protected]> wrote:
>>
>> In preparation to limiting the scope of a list iterator to the list
>> traversal loop, use a dedicated pointer to iterate through the list [1].
>>
>> In the current state the list_for_each_entry() is guaranteed to
>> hit a break or goto in order to work properly. If the list iterator
>> executes completely or the list is empty the iterator variable contains
>> a type confused bogus value infered from the head of the list.
>>
>> With this patch the variable used past the list iterator is only set
>> if the list exists early and is NULL otherwise. It should therefore
>> be safe to just set *prev = NULL (as it was before).
>>
>
> This generic boilerplate is fine to include, but it would help if you
> could point out why repainting the current logic with your new brush
> is appropriate here.
This makes sense, I can see that the commit message should be improved here.
>
> In this particular case, I wonder whether updating *prev makes sense
> to begin with if we are returning an error, and if we fix that, the
> issue disappears as well.
Actually I'm rethinking this now. The only use of 'prev' that I can see is
in efi_pstore_erase_name(). It only uses it if found != 0
which would mean err != 0 in __efivar_entry_iter().
This would allow massively simplifying the entire function.
The valid case is updating *prev when there is an "error" as far as I can tell.
I've sketched up a rewritten function that should hopefully be more clear and
archive the same goal, I'm curious what you think:
int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
struct list_head *head, void *data,
struct efivar_entry **prev)
{
struct efivar_entry *entry, *n;
int err = 0;
/* If prev is set and *prev != NULL start iterating from there */
if (prev)
entry = list_prepare_entry(*prev, head, list);
/* Otherwise start at the beginning */
else
entry = list_entry(head, typeof(*entry), list);
list_for_each_entry_safe_continue(entry, n, head, list) {
err = func(entry, data);
if (err && prev)
*prev = entry;
if (err)
return err;
}
return 0;
}
On Fri, 1 Apr 2022 at 00:11, Jakob Koschel <[email protected]> wrote:
>
> In preparation to limiting the scope of a list iterator to the list
> traversal loop, use a dedicated pointer to iterate through the list [1].
>
> In the current state the list_for_each_entry() is guaranteed to
> hit a break or goto in order to work properly. If the list iterator
> executes completely or the list is empty the iterator variable contains
> a type confused bogus value infered from the head of the list.
>
> With this patch the variable used past the list iterator is only set
> if the list exists early and is NULL otherwise. It should therefore
> be safe to just set *prev = NULL (as it was before).
>
This generic boilerplate is fine to include, but it would help if you
could point out why repainting the current logic with your new brush
is appropriate here.
In this particular case, I wonder whether updating *prev makes sense
to begin with if we are returning an error, and if we fix that, the
issue disappears as well.
> Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/
> Signed-off-by: Jakob Koschel <[email protected]>
> ---
> drivers/firmware/efi/vars.c | 10 ++++++----
> 1 file changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
> index cae590bd08f2..3994aad38661 100644
> --- a/drivers/firmware/efi/vars.c
> +++ b/drivers/firmware/efi/vars.c
> @@ -1081,14 +1081,16 @@ int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
> struct list_head *head, void *data,
> struct efivar_entry **prev)
> {
> - struct efivar_entry *entry, *n;
> + struct efivar_entry *entry = NULL, *iter, *n;
> int err = 0;
>
> if (!prev || !*prev) {
> - list_for_each_entry_safe(entry, n, head, list) {
> - err = func(entry, data);
> - if (err)
> + list_for_each_entry_safe(iter, n, head, list) {
> + err = func(iter, data);
> + if (err) {
> + entry = iter;
> break;
> + }
> }
>
> if (prev)
>
> base-commit: d888c83fcec75194a8a48ccd283953bdba7b2550
> --
> 2.25.1
>
On Fri, 1 Apr 2022 at 00:11, Jakob Koschel <[email protected]> wrote:
>
> To move the list iterator variable into the list_for_each_entry_*()
> macro in the future it should be avoided to use the list iterator
> variable after the loop body.
>
> To *never* use the list iterator variable after the loop it was
> concluded to use a separate iterator variable instead of a
> found boolean [1].
>
> This removes the need to use a found variable and simply checking if
> the variable was set, can determine if the break/goto was hit.
>
> Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/
> Signed-off-by: Jakob Koschel <[email protected]>
Acked-by: Ard Biesheuvel <[email protected]>
I'll queue this up once we converge on a solution for the other patch.
> ---
> drivers/firmware/efi/vars.c | 15 +++++++--------
> 1 file changed, 7 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
> index 3994aad38661..e4e1cc593441 100644
> --- a/drivers/firmware/efi/vars.c
> +++ b/drivers/firmware/efi/vars.c
> @@ -809,22 +809,21 @@ EXPORT_SYMBOL_GPL(efivar_entry_set_safe);
> struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
> struct list_head *head, bool remove)
> {
> - struct efivar_entry *entry, *n;
> + struct efivar_entry *entry = NULL, *iter, *n;
> int strsize1, strsize2;
> - bool found = false;
>
> - list_for_each_entry_safe(entry, n, head, list) {
> + list_for_each_entry_safe(iter, n, head, list) {
> strsize1 = ucs2_strsize(name, 1024);
> - strsize2 = ucs2_strsize(entry->var.VariableName, 1024);
> + strsize2 = ucs2_strsize(iter->var.VariableName, 1024);
> if (strsize1 == strsize2 &&
> - !memcmp(name, &(entry->var.VariableName), strsize1) &&
> - !efi_guidcmp(guid, entry->var.VendorGuid)) {
> - found = true;
> + !memcmp(name, &(iter->var.VariableName), strsize1) &&
> + !efi_guidcmp(guid, iter->var.VendorGuid)) {
> + entry = iter;
> break;
> }
> }
>
> - if (!found)
> + if (!entry)
> return NULL;
>
> if (remove) {
> --
> 2.25.1
>
(cc Kees for pstore)
On Wed, 13 Apr 2022 at 20:08, Jakob Koschel <[email protected]> wrote:
>
>
>
> > On 13. Apr 2022, at 19:05, Ard Biesheuvel <[email protected]> wrote:
> >
> > On Fri, 1 Apr 2022 at 00:11, Jakob Koschel <[email protected]> wrote:
> >>
> >> In preparation to limiting the scope of a list iterator to the list
> >> traversal loop, use a dedicated pointer to iterate through the list [1].
> >>
> >> In the current state the list_for_each_entry() is guaranteed to
> >> hit a break or goto in order to work properly. If the list iterator
> >> executes completely or the list is empty the iterator variable contains
> >> a type confused bogus value infered from the head of the list.
> >>
> >> With this patch the variable used past the list iterator is only set
> >> if the list exists early and is NULL otherwise. It should therefore
> >> be safe to just set *prev = NULL (as it was before).
> >>
> >
> > This generic boilerplate is fine to include, but it would help if you
> > could point out why repainting the current logic with your new brush
> > is appropriate here.
>
> This makes sense, I can see that the commit message should be improved here.
>
> >
> > In this particular case, I wonder whether updating *prev makes sense
> > to begin with if we are returning an error, and if we fix that, the
> > issue disappears as well.
>
> Actually I'm rethinking this now. The only use of 'prev' that I can see is
> in efi_pstore_erase_name(). It only uses it if found != 0
> which would mean err != 0 in __efivar_entry_iter().
>
> This would allow massively simplifying the entire function.
> The valid case is updating *prev when there is an "error" as far as I can tell.
>
OK, so in summary, the only user of that code that bothers to pass a
value for prev abuses it to implement its own version of
efivar_entry_find(), and so if we fix that caller, we can drop the
'prev' argument from this function altogether.
> I've sketched up a rewritten function that should hopefully be more clear and
> archive the same goal, I'm curious what you think:
>
>
> int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
> struct list_head *head, void *data,
> struct efivar_entry **prev)
> {
> struct efivar_entry *entry, *n;
> int err = 0;
>
> /* If prev is set and *prev != NULL start iterating from there */
> if (prev)
> entry = list_prepare_entry(*prev, head, list);
> /* Otherwise start at the beginning */
> else
> entry = list_entry(head, typeof(*entry), list);
> list_for_each_entry_safe_continue(entry, n, head, list) {
> err = func(entry, data);
> if (err && prev)
> *prev = entry;
> if (err)
> return err;
> }
>
> return 0;
> }
>
Thanks for this. I'll have a stab myself at fixing the EFI pstore
code, and hopefully we can clean up __efivar_entry_iter() as I
suggested.
On Thu, 14 Apr 2022 at 00:09, Ard Biesheuvel <[email protected]> wrote:
>
> (cc Kees for pstore)
>
> On Wed, 13 Apr 2022 at 20:08, Jakob Koschel <[email protected]> wrote:
> >
> >
> >
> > > On 13. Apr 2022, at 19:05, Ard Biesheuvel <[email protected]> wrote:
> > >
> > > On Fri, 1 Apr 2022 at 00:11, Jakob Koschel <[email protected]> wrote:
> > >>
> > >> In preparation to limiting the scope of a list iterator to the list
> > >> traversal loop, use a dedicated pointer to iterate through the list [1].
> > >>
> > >> In the current state the list_for_each_entry() is guaranteed to
> > >> hit a break or goto in order to work properly. If the list iterator
> > >> executes completely or the list is empty the iterator variable contains
> > >> a type confused bogus value infered from the head of the list.
> > >>
> > >> With this patch the variable used past the list iterator is only set
> > >> if the list exists early and is NULL otherwise. It should therefore
> > >> be safe to just set *prev = NULL (as it was before).
> > >>
> > >
> > > This generic boilerplate is fine to include, but it would help if you
> > > could point out why repainting the current logic with your new brush
> > > is appropriate here.
> >
> > This makes sense, I can see that the commit message should be improved here.
> >
> > >
> > > In this particular case, I wonder whether updating *prev makes sense
> > > to begin with if we are returning an error, and if we fix that, the
> > > issue disappears as well.
> >
> > Actually I'm rethinking this now. The only use of 'prev' that I can see is
> > in efi_pstore_erase_name(). It only uses it if found != 0
> > which would mean err != 0 in __efivar_entry_iter().
> >
> > This would allow massively simplifying the entire function.
> > The valid case is updating *prev when there is an "error" as far as I can tell.
> >
>
> OK, so in summary, the only user of that code that bothers to pass a
> value for prev abuses it to implement its own version of
> efivar_entry_find(), and so if we fix that caller, we can drop the
> 'prev' argument from this function altogether.
>
>
> > I've sketched up a rewritten function that should hopefully be more clear and
> > archive the same goal, I'm curious what you think:
> >
> >
> > int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
> > struct list_head *head, void *data,
> > struct efivar_entry **prev)
> > {
> > struct efivar_entry *entry, *n;
> > int err = 0;
> >
> > /* If prev is set and *prev != NULL start iterating from there */
> > if (prev)
> > entry = list_prepare_entry(*prev, head, list);
> > /* Otherwise start at the beginning */
> > else
> > entry = list_entry(head, typeof(*entry), list);
> > list_for_each_entry_safe_continue(entry, n, head, list) {
> > err = func(entry, data);
> > if (err && prev)
> > *prev = entry;
> > if (err)
> > return err;
> > }
> >
> > return 0;
> > }
> >
>
> Thanks for this. I'll have a stab myself at fixing the EFI pstore
> code, and hopefully we can clean up __efivar_entry_iter() as I
> suggested.
This is now queued up in the EFI tree: the pstore driver no longer use
the efivar_entry API at all, and the remaining user of
efivar_entry_iter() does not use the value of the iterator variable in
the same way.