All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] efivar: Disable get_next_variable when firmware is broken
@ 2013-03-19 21:17 Seiji Aguchi
       [not found] ` <A5ED84D3BB3A384992CBB9C77DEDA4D41AF6FBF6-ohthHghroY0jroPwUH3sq+6wyyQG6/Uh@public.gmane.org>
  0 siblings, 1 reply; 4+ messages in thread
From: Seiji Aguchi @ 2013-03-19 21:17 UTC (permalink / raw)
  To: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Matt Fleming (matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org),
	Andre Heider (a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org),
	Lingzhu Xiang (lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org),
	mikew-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org
  Cc: dle-develop-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
	Satoru Moriya, Tomoki Sekiyama

[Problem]
Some firmware implementations return the same variable name on multiple calls to
get_next_variable().
In this case, an efivar driver gets stuck in a infinite loop at initializing time, 
register_efivars().
It is hard for users to fix the broken firmware.

Here is an actual result of the case above.
http://article.gmane.org/gmane.linux.kernel.efi/835

[Solution]
This patch terminates the loop immediately and disables get_next_variable()
at the initializing time when the same variable name is found on multiple 
calls to get_next_variable().

Also, to avoid stucking in the infinite loop,
update_sysfs_entries returns without calling get_next_variable if the case above happens.

Reported-by: Andre Heider <a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Signed-off-by: Seiji Aguchi <seiji.aguchi-7rDLJAbr9SE@public.gmane.org>
---
 drivers/firmware/efivars.c     |   34 ++++++++++++++++++++++++++++++++--
 drivers/firmware/google/gsmi.c |    4 +++-
 include/linux/efi.h            |    3 ++-
 3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index fe62aa3..fa601c6 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -1725,6 +1725,14 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
 	efi_status_t status = EFI_NOT_FOUND;
 	bool found;
 
+	spin_lock_irq(&efivars->lock);
+	if (!efivars->ops->get_next_variable) {
+			spin_unlock_irq(&efivars->lock);
+			pr_warn("efivars: Skip updating sysfs entries\n");
+			return;
+	}
+	spin_unlock_irq(&efivars->lock);
+
 	/* Add new sysfs entries */
 	while (1) {
 		variable_name = kzalloc(variable_name_size, GFP_KERNEL);
@@ -1960,7 +1968,8 @@ EXPORT_SYMBOL_GPL(unregister_efivars);
 
 int register_efivars(struct efivars *efivars,
 		     const struct efivar_operations *ops,
-		     struct kobject *parent_kobj)
+		     struct kobject *parent_kobj,
+		     bool *get_next_variable_cannot_call)
 {
 	efi_status_t status = EFI_NOT_FOUND;
 	efi_guid_t vendor_guid;
@@ -2006,6 +2015,21 @@ int register_efivars(struct efivars *efivars,
 						&vendor_guid);
 		switch (status) {
 		case EFI_SUCCESS:
+			/*
+			 * Some firmware implementations return the
+			 * same variable name on multiple calls to
+			 * get_next_variable(). Terminate the loop
+			 * immediately as there is no guarantee that
+			 * we'll ever see a different variable name,
+			 * and may end up looping here forever.
+			 */
+			if (variable_is_present(variable_name, &vendor_guid)) {
+				pr_warn("efivars: duplicate variable name.\n");
+				*get_next_variable_cannot_call = true;
+				status = EFI_NOT_FOUND;
+				break;
+			}
+
 			efivar_create_sysfs_entry(efivars,
 						  variable_name_size,
 						  variable_name,
@@ -2056,6 +2080,7 @@ static int __init
 efivars_init(void)
 {
 	int error = 0;
+	bool get_next_variable_cannot_call = false;
 
 	printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
 	       EFIVARS_DATE);
@@ -2075,7 +2100,12 @@ efivars_init(void)
 	ops.get_next_variable = efi.get_next_variable;
 	ops.query_variable_info = efi.query_variable_info;
 
-	error = register_efivars(&__efivars, &ops, efi_kobj);
+	error = register_efivars(&__efivars, &ops, efi_kobj,
+				 &get_next_variable_cannot_call);
+	if (get_next_variable_cannot_call) {
+		pr_warn("efivars: Disable get_next_variable service\n");
+		ops.get_next_variable = NULL;
+	}
 	if (error)
 		goto err_put;
 
diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c
index 91ddf0f..0c205c8 100644
--- a/drivers/firmware/google/gsmi.c
+++ b/drivers/firmware/google/gsmi.c
@@ -778,6 +778,7 @@ static __init int gsmi_init(void)
 {
 	unsigned long flags;
 	int ret;
+	bool get_next_variable_cannot_call = false;
 
 	ret = gsmi_system_valid();
 	if (ret)
@@ -893,7 +894,8 @@ static __init int gsmi_init(void)
 		goto out_remove_bin_file;
 	}
 
-	ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj);
+	ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj,
+			       &get_next_variable_cannot_call);
 	if (ret) {
 		printk(KERN_INFO "gsmi: Failed to register efivars\n");
 		goto out_remove_sysfs_files;
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 9bf2f1f..9db51b1 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -756,7 +756,8 @@ struct efivars {
 
 int register_efivars(struct efivars *efivars,
 		     const struct efivar_operations *ops,
-		     struct kobject *parent_kobj);
+		     struct kobject *parent_kobj,
+		     bool *get_next_variable_cannot_call);
 void unregister_efivars(struct efivars *efivars);
 
 #endif /* CONFIG_EFI_VARS */
-- 1.7.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] efivar: Disable get_next_variable when firmware is broken
       [not found] ` <A5ED84D3BB3A384992CBB9C77DEDA4D41AF6FBF6-ohthHghroY0jroPwUH3sq+6wyyQG6/Uh@public.gmane.org>
@ 2013-03-19 21:23   ` Mike Waychison
  2013-03-19 22:15   ` Matt Fleming
  1 sibling, 0 replies; 4+ messages in thread
From: Mike Waychison @ 2013-03-19 21:23 UTC (permalink / raw)
  To: Seiji Aguchi
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Matt Fleming (matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org),
	Andre Heider (a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org),
	Lingzhu Xiang (lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org),
	dle-develop-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
	Satoru Moriya, Tomoki Sekiyama

On Tue, Mar 19, 2013 at 2:17 PM, Seiji Aguchi <seiji.aguchi-7rDLJAbr9SE@public.gmane.org> wrote:
>
> [Problem]
> Some firmware implementations return the same variable name on multiple calls to
> get_next_variable().
> In this case, an efivar driver gets stuck in a infinite loop at initializing time,
> register_efivars().
> It is hard for users to fix the broken firmware.
>
> Here is an actual result of the case above.
> http://article.gmane.org/gmane.linux.kernel.efi/835
>
> [Solution]
> This patch terminates the loop immediately and disables get_next_variable()
> at the initializing time when the same variable name is found on multiple
> calls to get_next_variable().
>
> Also, to avoid stucking in the infinite loop,
> update_sysfs_entries returns without calling get_next_variable if the case above happens.
>
> Reported-by: Andre Heider <a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Seiji Aguchi <seiji.aguchi-7rDLJAbr9SE@public.gmane.org>
> ---
>  drivers/firmware/efivars.c     |   34 ++++++++++++++++++++++++++++++++--
>  drivers/firmware/google/gsmi.c |    4 +++-
>  include/linux/efi.h            |    3 ++-
>  3 files changed, 37 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
> index fe62aa3..fa601c6 100644
> --- a/drivers/firmware/efivars.c
> +++ b/drivers/firmware/efivars.c
> @@ -1725,6 +1725,14 @@ static void efivar_update_sysfs_entries(struct work_struct *work)
>         efi_status_t status = EFI_NOT_FOUND;
>         bool found;
>
> +       spin_lock_irq(&efivars->lock);
> +       if (!efivars->ops->get_next_variable) {
> +                       spin_unlock_irq(&efivars->lock);
> +                       pr_warn("efivars: Skip updating sysfs entries\n");
> +                       return;
> +       }
> +       spin_unlock_irq(&efivars->lock);
> +
>         /* Add new sysfs entries */
>         while (1) {
>                 variable_name = kzalloc(variable_name_size, GFP_KERNEL);
> @@ -1960,7 +1968,8 @@ EXPORT_SYMBOL_GPL(unregister_efivars);
>
>  int register_efivars(struct efivars *efivars,
>                      const struct efivar_operations *ops,
> -                    struct kobject *parent_kobj)
> +                    struct kobject *parent_kobj,
> +                    bool *get_next_variable_cannot_call)
>  {
>         efi_status_t status = EFI_NOT_FOUND;
>         efi_guid_t vendor_guid;
> @@ -2006,6 +2015,21 @@ int register_efivars(struct efivars *efivars,
>                                                 &vendor_guid);
>                 switch (status) {
>                 case EFI_SUCCESS:
> +                       /*
> +                        * Some firmware implementations return the
> +                        * same variable name on multiple calls to
> +                        * get_next_variable(). Terminate the loop
> +                        * immediately as there is no guarantee that
> +                        * we'll ever see a different variable name,
> +                        * and may end up looping here forever.
> +                        */
> +                       if (variable_is_present(variable_name, &vendor_guid)) {
> +                               pr_warn("efivars: duplicate variable name.\n");
> +                               *get_next_variable_cannot_call = true;
> +                               status = EFI_NOT_FOUND;
> +                               break;
> +                       }
> +
>                         efivar_create_sysfs_entry(efivars,
>                                                   variable_name_size,
>                                                   variable_name,
> @@ -2056,6 +2080,7 @@ static int __init
>  efivars_init(void)
>  {
>         int error = 0;
> +       bool get_next_variable_cannot_call = false;
>
>         printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION,
>                EFIVARS_DATE);
> @@ -2075,7 +2100,12 @@ efivars_init(void)
>         ops.get_next_variable = efi.get_next_variable;
>         ops.query_variable_info = efi.query_variable_info;
>
> -       error = register_efivars(&__efivars, &ops, efi_kobj);
> +       error = register_efivars(&__efivars, &ops, efi_kobj,
> +                                &get_next_variable_cannot_call);
> +       if (get_next_variable_cannot_call) {
> +               pr_warn("efivars: Disable get_next_variable service\n");
> +               ops.get_next_variable = NULL;

I don't like this pattern of clearing out ops fields.  Can you instead
just add a flag in efivars that is checked before calling
ops->get_next_variable?

> +       }
>         if (error)
>                 goto err_put;
>
> diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c
> index 91ddf0f..0c205c8 100644
> --- a/drivers/firmware/google/gsmi.c
> +++ b/drivers/firmware/google/gsmi.c
> @@ -778,6 +778,7 @@ static __init int gsmi_init(void)
>  {
>         unsigned long flags;
>         int ret;
> +       bool get_next_variable_cannot_call = false;
>
>         ret = gsmi_system_valid();
>         if (ret)
> @@ -893,7 +894,8 @@ static __init int gsmi_init(void)
>                 goto out_remove_bin_file;
>         }
>
> -       ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj);
> +       ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj,
> +                              &get_next_variable_cannot_call);
>         if (ret) {
>                 printk(KERN_INFO "gsmi: Failed to register efivars\n");
>                 goto out_remove_sysfs_files;
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 9bf2f1f..9db51b1 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -756,7 +756,8 @@ struct efivars {
>
>  int register_efivars(struct efivars *efivars,
>                      const struct efivar_operations *ops,
> -                    struct kobject *parent_kobj);
> +                    struct kobject *parent_kobj,
> +                    bool *get_next_variable_cannot_call);
>  void unregister_efivars(struct efivars *efivars);
>
>  #endif /* CONFIG_EFI_VARS */
> -- 1.7.1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] efivar: Disable get_next_variable when firmware is broken
       [not found] ` <A5ED84D3BB3A384992CBB9C77DEDA4D41AF6FBF6-ohthHghroY0jroPwUH3sq+6wyyQG6/Uh@public.gmane.org>
  2013-03-19 21:23   ` Mike Waychison
@ 2013-03-19 22:15   ` Matt Fleming
       [not found]     ` <5148E38D.5010109-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  1 sibling, 1 reply; 4+ messages in thread
From: Matt Fleming @ 2013-03-19 22:15 UTC (permalink / raw)
  To: Seiji Aguchi
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Andre Heider (a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org),
	Lingzhu Xiang (lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org),
	mikew-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org,
	dle-develop-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
	Satoru Moriya, Tomoki Sekiyama

On 03/19/2013 09:17 PM, Seiji Aguchi wrote:
> [Problem]
> Some firmware implementations return the same variable name on multiple calls to
> get_next_variable().
> In this case, an efivar driver gets stuck in a infinite loop at initializing time, 
> register_efivars().
> It is hard for users to fix the broken firmware.
> 
> Here is an actual result of the case above.
> http://article.gmane.org/gmane.linux.kernel.efi/835
> 
> [Solution]
> This patch terminates the loop immediately and disables get_next_variable()
> at the initializing time when the same variable name is found on multiple 
> calls to get_next_variable().
> 
> Also, to avoid stucking in the infinite loop,
> update_sysfs_entries returns without calling get_next_variable if the case above happens.
> 
> Reported-by: Andre Heider <a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Seiji Aguchi <seiji.aguchi-7rDLJAbr9SE@public.gmane.org>
> ---
>  drivers/firmware/efivars.c     |   34 ++++++++++++++++++++++++++++++++--
>  drivers/firmware/google/gsmi.c |    4 +++-
>  include/linux/efi.h            |    3 ++-
>  3 files changed, 37 insertions(+), 4 deletions(-)

I don't see how this solution is an improvement to the patch I posted here.

	http://article.gmane.org/gmane.linux.kernel.efi/905

You unconditionally call efivar_update_sysfs_entries(), even when the
algorithm isn't going to succeed. Your patch also spreads this firmware
brain damage into the gsmi.c code, which really doesn't need to concern
itself with these problems.

-- 
Matt Fleming, Intel Open Source Technology Center

^ permalink raw reply	[flat|nested] 4+ messages in thread

* RE: [PATCH] efivar: Disable get_next_variable when firmware is broken
       [not found]     ` <5148E38D.5010109-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
@ 2013-03-20  2:10       ` Seiji Aguchi
  0 siblings, 0 replies; 4+ messages in thread
From: Seiji Aguchi @ 2013-03-20  2:10 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Andre Heider (a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org),
	Lingzhu Xiang (lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org),
	mikew-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org,
	dle-develop-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org,
	Satoru Moriya, Tomoki Sekiyama



> -----Original Message-----
> From: linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-efi-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Matt Fleming
> Sent: Tuesday, March 19, 2013 6:16 PM
> To: Seiji Aguchi
> Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; Andre Heider (a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org); Lingzhu Xiang (lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org); mikew-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org; dle-
> develop-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org; Satoru Moriya; Tomoki Sekiyama
> Subject: Re: [PATCH] efivar: Disable get_next_variable when firmware is broken
> 
> On 03/19/2013 09:17 PM, Seiji Aguchi wrote:
> > [Problem]
> > Some firmware implementations return the same variable name on
> > multiple calls to get_next_variable().
> > In this case, an efivar driver gets stuck in a infinite loop at
> > initializing time, register_efivars().
> > It is hard for users to fix the broken firmware.
> >
> > Here is an actual result of the case above.
> > http://article.gmane.org/gmane.linux.kernel.efi/835
> >
> > [Solution]
> > This patch terminates the loop immediately and disables
> > get_next_variable() at the initializing time when the same variable
> > name is found on multiple calls to get_next_variable().
> >
> > Also, to avoid stucking in the infinite loop, update_sysfs_entries
> > returns without calling get_next_variable if the case above happens.
> >
> > Reported-by: Andre Heider <a.heider-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> > Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> > Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> > Signed-off-by: Seiji Aguchi <seiji.aguchi-7rDLJAbr9SE@public.gmane.org>
> > ---
> >  drivers/firmware/efivars.c     |   34 ++++++++++++++++++++++++++++++++--
> >  drivers/firmware/google/gsmi.c |    4 +++-
> >  include/linux/efi.h            |    3 ++-
> >  3 files changed, 37 insertions(+), 4 deletions(-)
> 
> I don't see how this solution is an improvement to the patch I posted here.
> 
> 	http://article.gmane.org/gmane.linux.kernel.efi/905

Ah, I just missed your patch above.
It is reasonable to me.

> 
> You unconditionally call efivar_update_sysfs_entries(), even when the algorithm isn't going to succeed. Your patch also spreads this
> firmware brain damage into the gsmi.c code, which really doesn't need to concern itself with these problems.
> 

I agree. Thank you for giving me the comment.

Seiji

> --
> Matt Fleming, Intel Open Source Technology Center
> --
> To unsubscribe from this list: send the line "unsubscribe linux-efi" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More
> majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-03-20  2:10 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-03-19 21:17 [PATCH] efivar: Disable get_next_variable when firmware is broken Seiji Aguchi
     [not found] ` <A5ED84D3BB3A384992CBB9C77DEDA4D41AF6FBF6-ohthHghroY0jroPwUH3sq+6wyyQG6/Uh@public.gmane.org>
2013-03-19 21:23   ` Mike Waychison
2013-03-19 22:15   ` Matt Fleming
     [not found]     ` <5148E38D.5010109-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2013-03-20  2:10       ` Seiji Aguchi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.