All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] efivarfs patch queue
@ 2013-02-11 14:28 Matt Fleming
       [not found] ` <1360592935-26026-1-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  0 siblings, 1 reply; 15+ messages in thread
From: Matt Fleming @ 2013-02-11 14:28 UTC (permalink / raw)
  To: linux-efi-u79uwXL29TY76Z2rM5mHXA
  Cc: Lingzhu Xiang, Matthew Garrett, Jeremy Kerr, Al Viro, Matt Fleming

From: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

I've queued up the following fixes in the 'next' branch of the EFI
tree. Both bugs were reported by Lingzhu Xiang and are caused by a
lack of filename validation in efivarfs.

Matt Fleming (2):
  efivarfs: Validate filenames much more aggressively
  efivarfs: guid part of filenames are case-insensitive

 drivers/firmware/efivars.c | 156 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 150 insertions(+), 6 deletions(-)

-- 
1.7.11.7

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

* [PATCH 1/2] efivarfs: Validate filenames much more aggressively
       [not found] ` <1360592935-26026-1-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
@ 2013-02-11 14:28   ` Matt Fleming
       [not found]     ` <1360592935-26026-2-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  2013-02-11 14:28   ` [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive Matt Fleming
  1 sibling, 1 reply; 15+ messages in thread
From: Matt Fleming @ 2013-02-11 14:28 UTC (permalink / raw)
  To: linux-efi-u79uwXL29TY76Z2rM5mHXA
  Cc: Lingzhu Xiang, Matthew Garrett, Jeremy Kerr, Al Viro, Matt Fleming

From: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

The only thing that efivarfs does to enforce a valid filename is
ensure that the name isn't too short. We need to strongly sanitise any
filenames, not least because variable creation is delayed until
efivarfs_file_write(), which means we can't rely on the firmware to
inform us of an invalid name, because if the file is never written to
we'll never know it's invalid.

Perform a couple of steps before agreeing to create a new file,

  * hex_to_bin() returns a value indicating whether or not it was able
    to convert its arguments to a binary representation - we should
    check it.

  * Ensure that the GUID portion of the filename is the correct length
    and format.

  * The variable name portion of the filename needs to be at least one
    character in size.

Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: Matthew Garrett <mjg59-1xO5oi07KQx4cg9Nei1l7Q@public.gmane.org>
Cc: Jeremy Kerr <jeremy.kerr-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/firmware/efivars.c | 55 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 50 insertions(+), 5 deletions(-)

diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 371c441..a4fa409 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -900,6 +900,55 @@ static struct inode *efivarfs_get_inode(struct super_block *sb,
 	return inode;
 }
 
+/*
+ * Return 1 if 'str' is a valid efivarfs filename of the form,
+ *
+ *	VariableName-12345678-1234-1234-1234-1234567891bc
+ */
+static int efivarfs_valid_name(const char *str, int len)
+{
+	const char *s;
+	int i, j;
+	int ranges[2][5] = {
+		{ 0, 9, 14, 19, 24 },
+		{ 8, 13, 18, 23, 36 }
+	};
+
+	/*
+	 * We need a GUID, plus at least one letter for the variable name,
+	 * plus the '-' separator
+	 */
+	if (len < GUID_LEN + 2)
+		return 0;
+
+	s = strchr(str, '-');
+	if (!s)
+		return 0;
+
+	s++;			/* Skip '-' */
+
+	/* Ensure we have enough characters for a GUID */
+	if (len - (s - str) != GUID_LEN)
+		return 0;
+
+	/*
+	 * Validate that 's' is of the correct format, e.g.
+	 *
+	 *	12345678-1234-1234-1234-123456789abc
+	 */
+	for (i = 0; i < 5; i++) {
+		for (j = ranges[0][i]; j < ranges[1][i]; j++) {
+			if (hex_to_bin(s[j]) < 0)
+				return 0;
+		}
+
+		if (j < GUID_LEN && s[j] != '-')
+			return 0;
+	}
+
+	return 1;
+}
+
 static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)
 {
 	guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]);
@@ -928,11 +977,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
 	struct efivar_entry *var;
 	int namelen, i = 0, err = 0;
 
-	/*
-	 * We need a GUID, plus at least one letter for the variable name,
-	 * plus the '-' separator
-	 */
-	if (dentry->d_name.len < GUID_LEN + 2)
+	if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
 		return -EINVAL;
 
 	inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
-- 
1.7.11.7

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

* [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive
       [not found] ` <1360592935-26026-1-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  2013-02-11 14:28   ` [PATCH 1/2] efivarfs: Validate filenames much more aggressively Matt Fleming
@ 2013-02-11 14:28   ` Matt Fleming
       [not found]     ` <1360592935-26026-3-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  1 sibling, 1 reply; 15+ messages in thread
From: Matt Fleming @ 2013-02-11 14:28 UTC (permalink / raw)
  To: linux-efi-u79uwXL29TY76Z2rM5mHXA
  Cc: Lingzhu Xiang, Matthew Garrett, Jeremy Kerr, Al Viro, Matt Fleming

From: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>

It makes no sense to treat the following filenames as unique,

	VarName-abcdefab-abcd-abcd-abcd-abcdefabcdef
	VarName-ABCDEFAB-ABCD-ABCD-ABCD-ABCDEFABCDEF
	VarName-ABcDEfAB-ABcD-ABcD-ABcD-ABcDEfABcDEf
	VarName-aBcDEfAB-aBcD-aBcD-aBcD-aBcDEfaBcDEf
	... etc ...

since the guid will be converted into a binary representation, which
has no case.

Roll our own dentry operations so that we can treat the variable name
part of filenames ("VarName" in the above example) as case-sensitive,
but the guid portion as case-insensitive. That way, efivarfs will
refuse to create the above files if any one already exists.

Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: Matthew Garrett <mjg59-1xO5oi07KQx4cg9Nei1l7Q@public.gmane.org>
Cc: Jeremy Kerr <jeremy.kerr-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/firmware/efivars.c | 101 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 100 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index a4fa409..10088fd 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -79,6 +79,7 @@
 #include <linux/device.h>
 #include <linux/slab.h>
 #include <linux/pstore.h>
+#include <linux/ctype.h>
 
 #include <linux/fs.h>
 #include <linux/ramfs.h>
@@ -1049,6 +1050,91 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
 	return -EINVAL;
 };
 
+/*
+ * Compare two efivarfs file names.
+ *
+ * An efivarfs filename is composed of two parts,
+ *
+ *	1. A case-sensitive variable name
+ *	2. A case-insensitive GUID
+ *
+ * So we need to perform a case-sensitive match on part 1 and a
+ * case-insensitive match on part 2.
+ */
+static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
+			      const struct dentry *dentry, const struct inode *inode,
+			      unsigned int len, const char *str,
+			      const struct qstr *name)
+{
+	const char *q;
+	int guid;
+
+	/*
+	 * If the string we're being asked to compare doesn't match
+	 * the expected format return "no match".
+	 */
+	if (!efivarfs_valid_name(str, len))
+		return 1;
+
+	if (!(q = strchr(name->name, '-')))
+		return 1;
+
+	/* Find part 1, the variable name. */
+	guid = q - (const char *)name->name;
+
+	/* Case-sensitive compare for the variable name */
+	if (memcmp(str, name->name, guid))
+		return 1;
+
+	/* Case-insensitive compare for the GUID */
+	return strcasecmp(&name->name[guid], &str[guid]);
+}
+
+static int efivarfs_d_hash(const struct dentry *dentry,
+			   const struct inode *inode, struct qstr *qstr)
+{
+	const unsigned char *us;
+	char lower[NAME_MAX];
+	int guid;
+
+	if (!efivarfs_valid_name(qstr->name, qstr->len))
+		return -EINVAL;
+
+	if (qstr->len > NAME_MAX)
+		return -ENAMETOOLONG;
+
+	us = strchr(qstr->name, '-');
+	if (!us)
+		return -EINVAL;
+
+	/* The variable name part is case-sensitive */
+	guid = us - qstr->name;
+	memcpy(lower, qstr->name, guid);
+
+	/* GUID is case-insensitive. */
+	for (us = &qstr->name[guid]; guid < qstr->len; guid++)
+		lower[guid] = tolower(*us++);
+	lower[guid] = '\0';
+
+	qstr->hash = full_name_hash(lower, qstr->len);
+	return 0;
+}
+
+/*
+ * Retaining negative dentries for an in-memory filesystem just wastes
+ * memory and lookup time: arrange for them to be deleted immediately.
+ */
+static int efivarfs_delete_dentry(const struct dentry *dentry)
+{
+	return 1;
+}
+
+static struct dentry_operations efivarfs_d_ops = {
+	.d_compare = efivarfs_d_compare,
+	.d_hash = efivarfs_d_hash,
+	.d_delete = efivarfs_delete_dentry,
+};
+
 static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 {
 	struct inode *inode = NULL;
@@ -1064,6 +1150,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 	sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
 	sb->s_magic             = EFIVARFS_MAGIC;
 	sb->s_op                = &efivarfs_ops;
+	sb->s_d_op		= &efivarfs_d_ops;
 	sb->s_time_gran         = 1;
 
 	inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
@@ -1154,8 +1241,20 @@ static struct file_system_type efivarfs_type = {
 	.kill_sb = efivarfs_kill_sb,
 };
 
+/*
+ * Handle negative dentry.
+ */
+static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry,
+				      unsigned int flags)
+{
+	if (dentry->d_name.len > NAME_MAX)
+		return ERR_PTR(-ENAMETOOLONG);
+	d_add(dentry, NULL);
+	return NULL;
+}
+
 static const struct inode_operations efivarfs_dir_inode_operations = {
-	.lookup = simple_lookup,
+	.lookup = efivarfs_lookup,
 	.unlink = efivarfs_unlink,
 	.create = efivarfs_create,
 };
-- 
1.7.11.7

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

* Re: [PATCH 1/2] efivarfs: Validate filenames much more aggressively
       [not found]     ` <1360592935-26026-2-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
@ 2013-02-11 15:01       ` Al Viro
       [not found]         ` <20130211150109.GK4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
  2013-02-12 12:36       ` [PATCH 1/2 v2] " Matt Fleming
  1 sibling, 1 reply; 15+ messages in thread
From: Al Viro @ 2013-02-11 15:01 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Mon, Feb 11, 2013 at 02:28:54PM +0000, Matt Fleming wrote:
> + * Return 1 if 'str' is a valid efivarfs filename of the form,
> + *
> + *	VariableName-12345678-1234-1234-1234-1234567891bc
> + */
> +static int efivarfs_valid_name(const char *str, int len)
> +{
> +	const char *s;
> +	int i, j;
> +	int ranges[2][5] = {
> +		{ 0, 9, 14, 19, 24 },
> +		{ 8, 13, 18, 23, 36 }
> +	};
> +
> +	/*
> +	 * We need a GUID, plus at least one letter for the variable name,
> +	 * plus the '-' separator
> +	 */
> +	if (len < GUID_LEN + 2)
> +		return 0;
> +
> +	s = strchr(str, '-');
> +	if (!s)
> +		return 0;
> +
> +	s++;			/* Skip '-' */
> +
> +	/* Ensure we have enough characters for a GUID */
> +	if (len - (s - str) != GUID_LEN)
> +		return 0;
> +
> +	/*
> +	 * Validate that 's' is of the correct format, e.g.
> +	 *
> +	 *	12345678-1234-1234-1234-123456789abc
> +	 */
> +	for (i = 0; i < 5; i++) {
> +		for (j = ranges[0][i]; j < ranges[1][i]; j++) {
> +			if (hex_to_bin(s[j]) < 0)
> +				return 0;
> +		}
> +
> +		if (j < GUID_LEN && s[j] != '-')
> +			return 0;
> +	}
> +
> +	return 1;

Yecchhh...  How about
	static const char dashes[GUID_LEN] = {
		[8] = 1, [13] = 1, [18] = 1, [23] = 1
	};
	const char *s = str + len - GUID_LEN;
	int i;
	/*
	 * We need a GUID, plus at least one letter for the variable name,
	 * plus the '-' separator
	 */
	if (len < GUID_LEN + 2)
		return 0;

	/* GUID should be right after the first '-' */
	if (s - 1 != strchr(str, '-'))
		return 0;

	/*
	 * Validate that 's' is of the correct format, e.g.
	 *
	 *	12345678-1234-1234-1234-123456789abc
	 */
	for (i = 0; i < GUID_LEN; i++) {
		if (dashes[i]) {
			if (*s++ != '-')
				return 0;
		} else {
			if (!isxdigit(*s++))
				return 0;
		}
	}
	return 1;

instead?

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

* Re: [PATCH 1/2] efivarfs: Validate filenames much more aggressively
       [not found]         ` <20130211150109.GK4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
@ 2013-02-11 15:12           ` Matt Fleming
  0 siblings, 0 replies; 15+ messages in thread
From: Matt Fleming @ 2013-02-11 15:12 UTC (permalink / raw)
  To: Al Viro
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Mon, 11 Feb, at 03:01:09PM, Al Viro wrote:
> 
> Yecchhh...  How about
> 	static const char dashes[GUID_LEN] = {
> 		[8] = 1, [13] = 1, [18] = 1, [23] = 1
> 	};
> 	const char *s = str + len - GUID_LEN;
> 	int i;
> 	/*
> 	 * We need a GUID, plus at least one letter for the variable name,
> 	 * plus the '-' separator
> 	 */
> 	if (len < GUID_LEN + 2)
> 		return 0;
> 
> 	/* GUID should be right after the first '-' */
> 	if (s - 1 != strchr(str, '-'))
> 		return 0;
> 
> 	/*
> 	 * Validate that 's' is of the correct format, e.g.
> 	 *
> 	 *	12345678-1234-1234-1234-123456789abc
> 	 */
> 	for (i = 0; i < GUID_LEN; i++) {
> 		if (dashes[i]) {
> 			if (*s++ != '-')
> 				return 0;
> 		} else {
> 			if (!isxdigit(*s++))
> 				return 0;
> 		}
> 	}
> 	return 1;
> 
> instead?

Sure, that's a nice improvement.

Also, I did have a version of this patch that returned a boolean, but
that seems to have been lost in one of my topic branches.

I'll respind this, thanks Al.

-- 
Matt Fleming, Intel Open Source Technology Center

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

* Re: [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive
       [not found]     ` <1360592935-26026-3-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
@ 2013-02-11 15:22       ` Al Viro
       [not found]         ` <20130211152221.GL4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
  2013-02-12 12:39       ` [PATCH 2/2 v2] " Matt Fleming
  1 sibling, 1 reply; 15+ messages in thread
From: Al Viro @ 2013-02-11 15:22 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Mon, Feb 11, 2013 at 02:28:55PM +0000, Matt Fleming wrote:
> From: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> 
> It makes no sense to treat the following filenames as unique,
> 
> 	VarName-abcdefab-abcd-abcd-abcd-abcdefabcdef
> 	VarName-ABCDEFAB-ABCD-ABCD-ABCD-ABCDEFABCDEF
> 	VarName-ABcDEfAB-ABcD-ABcD-ABcD-ABcDEfABcDEf
> 	VarName-aBcDEfAB-aBcD-aBcD-aBcD-aBcDEfaBcDEf
> 	... etc ...
> 
> since the guid will be converted into a binary representation, which
> has no case.
> 
> Roll our own dentry operations so that we can treat the variable name
> part of filenames ("VarName" in the above example) as case-sensitive,
> but the guid portion as case-insensitive. That way, efivarfs will
> refuse to create the above files if any one already exists.
> 
> Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
> Cc: Matthew Garrett <mjg59-1xO5oi07KQx4cg9Nei1l7Q@public.gmane.org>
> Cc: Jeremy Kerr <jeremy.kerr-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
> Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
> Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/firmware/efivars.c | 101 ++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 100 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
> index a4fa409..10088fd 100644
> --- a/drivers/firmware/efivars.c
> +++ b/drivers/firmware/efivars.c
> @@ -79,6 +79,7 @@
>  #include <linux/device.h>
>  #include <linux/slab.h>
>  #include <linux/pstore.h>
> +#include <linux/ctype.h>
>  
>  #include <linux/fs.h>
>  #include <linux/ramfs.h>
> @@ -1049,6 +1050,91 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
>  	return -EINVAL;
>  };
>  
> +/*
> + * Compare two efivarfs file names.
> + *
> + * An efivarfs filename is composed of two parts,
> + *
> + *	1. A case-sensitive variable name
> + *	2. A case-insensitive GUID
> + *
> + * So we need to perform a case-sensitive match on part 1 and a
> + * case-insensitive match on part 2.
> + */
> +static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
> +			      const struct dentry *dentry, const struct inode *inode,
> +			      unsigned int len, const char *str,
> +			      const struct qstr *name)
> +{
> +	const char *q;
> +	int guid;
> +
> +	/*
> +	 * If the string we're being asked to compare doesn't match
> +	 * the expected format return "no match".
> +	 */
> +	if (!efivarfs_valid_name(str, len))
> +		return 1;
> +	if (!(q = strchr(name->name, '-')))
> +		return 1;

No.  Why check that again, when we'd already called ->d_hash() on the
incoming name *and* candidate dentry?  And buggered off on any potential
errors.

> +
> +	/* Find part 1, the variable name. */
> +	guid = q - (const char *)name->name;

No need to do strchr() for that - you know that name passes
efivarfs_valid_name(), so you know how far from the end will GUID part begin.

> +	/* Case-sensitive compare for the variable name */
> +	if (memcmp(str, name->name, guid))
> +		return 1;
> +
> +	/* Case-insensitive compare for the GUID */
> +	return strcasecmp(&name->name[guid], &str[guid]);
> +}

> +static int efivarfs_d_hash(const struct dentry *dentry,
> +			   const struct inode *inode, struct qstr *qstr)
> +{
Egads, man...
[snip the horror with copying the name]

        unsigned long hash = init_name_hash();
	const unsigned char *s = qstr->name;
	int len = qstr->len;

	if (!efivarfs_valid_name(s, len))
		return -EINVAL;
        while (len-- > GUID_LEN)
		hash = partial_name_hash(*s++, hash);
	/* GUID is case-insensitive. */
	while (len--)
		hash = partial_name_hash(tolower(*s++), hash);
        return end_name_hash(hash);

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

* Re: [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive
       [not found]         ` <20130211152221.GL4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
@ 2013-02-11 15:37           ` Al Viro
  2013-02-11 16:05           ` Matt Fleming
  1 sibling, 0 replies; 15+ messages in thread
From: Al Viro @ 2013-02-11 15:37 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Mon, Feb 11, 2013 at 03:22:21PM +0000, Al Viro wrote:
> > +static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
> > +			      const struct dentry *dentry, const struct inode *inode,
> > +			      unsigned int len, const char *str,
> > +			      const struct qstr *name)
> > +{
> > +	const char *q;
> > +	int guid;
> > +
> > +	/*
> > +	 * If the string we're being asked to compare doesn't match
> > +	 * the expected format return "no match".
> > +	 */
> > +	if (!efivarfs_valid_name(str, len))
> > +		return 1;
> > +	if (!(q = strchr(name->name, '-')))
> > +		return 1;
> 
> No.  Why check that again, when we'd already called ->d_hash() on the
> incoming name *and* candidate dentry?  And buggered off on any potential
> errors.
> 
> > +
> > +	/* Find part 1, the variable name. */
> > +	guid = q - (const char *)name->name;
> 
> No need to do strchr() for that - you know that name passes
> efivarfs_valid_name(), so you know how far from the end will GUID part begin.
> 
> > +	/* Case-sensitive compare for the variable name */
> > +	if (memcmp(str, name->name, guid))
> > +		return 1;

... and by the way, you need to compare lengths first, or that memcmp()
risks running out of mapped page.  Sure, it's NUL-terminated, but memcmp()
is *not* required to compare left-to-right; it's arch-dependent and the
very first memory access have every right to be at str + guid - 1.

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

* Re: [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive
       [not found]         ` <20130211152221.GL4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
  2013-02-11 15:37           ` Al Viro
@ 2013-02-11 16:05           ` Matt Fleming
       [not found]             ` <20130211160557.GB26681-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  1 sibling, 1 reply; 15+ messages in thread
From: Matt Fleming @ 2013-02-11 16:05 UTC (permalink / raw)
  To: Al Viro
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Mon, 11 Feb, at 03:22:21PM, Al Viro wrote:
> > +	/*
> > +	 * If the string we're being asked to compare doesn't match
> > +	 * the expected format return "no match".
> > +	 */
> > +	if (!efivarfs_valid_name(str, len))
> > +		return 1;
> > +	if (!(q = strchr(name->name, '-')))
> > +		return 1;
> 
> No.  Why check that again, when we'd already called ->d_hash() on the
> incoming name *and* candidate dentry?  And buggered off on any potential
> errors.

Good point.

> > +
> > +	/* Find part 1, the variable name. */
> > +	guid = q - (const char *)name->name;
> 
> No need to do strchr() for that - you know that name passes
> efivarfs_valid_name(), so you know how far from the end will GUID part begin.

Right.

> > +	/* Case-sensitive compare for the variable name */
> > +	if (memcmp(str, name->name, guid))
> > +		return 1;
> > +
> > +	/* Case-insensitive compare for the GUID */
> > +	return strcasecmp(&name->name[guid], &str[guid]);
> > +}
> 
> > +static int efivarfs_d_hash(const struct dentry *dentry,
> > +			   const struct inode *inode, struct qstr *qstr)
> > +{
> Egads, man...
> [snip the horror with copying the name]
> 
>         unsigned long hash = init_name_hash();
> 	const unsigned char *s = qstr->name;
> 	int len = qstr->len;
> 
> 	if (!efivarfs_valid_name(s, len))
> 		return -EINVAL;
>         while (len-- > GUID_LEN)
> 		hash = partial_name_hash(*s++, hash);
> 	/* GUID is case-insensitive. */
> 	while (len--)
> 		hash = partial_name_hash(tolower(*s++), hash);
>         return end_name_hash(hash);

Oh, wow, yes that is much nicer. I didn't realise we could build the
hash incrementally like that. Very cool.

-- 
Matt Fleming, Intel Open Source Technology Center

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

* Re: [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive
       [not found]             ` <20130211160557.GB26681-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
@ 2013-02-11 17:30               ` Al Viro
       [not found]                 ` <20130211173057.GM4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
  0 siblings, 1 reply; 15+ messages in thread
From: Al Viro @ 2013-02-11 17:30 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Mon, Feb 11, 2013 at 04:05:57PM +0000, Matt Fleming wrote:
> > Egads, man...
> > [snip the horror with copying the name]
> > 
> >         unsigned long hash = init_name_hash();
> > 	const unsigned char *s = qstr->name;
> > 	int len = qstr->len;
> > 
> > 	if (!efivarfs_valid_name(s, len))
> > 		return -EINVAL;
> >         while (len-- > GUID_LEN)
> > 		hash = partial_name_hash(*s++, hash);
> > 	/* GUID is case-insensitive. */
> > 	while (len--)
> > 		hash = partial_name_hash(tolower(*s++), hash);
> >         return end_name_hash(hash);
> 
> Oh, wow, yes that is much nicer. I didn't realise we could build the
> hash incrementally like that. Very cool.

... except that it should end with qstr->hash = end_name_hash(hash); return 0;
instead.  My apologies.

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

* Re: [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive
       [not found]                 ` <20130211173057.GM4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
@ 2013-02-12 12:31                   ` Matt Fleming
  0 siblings, 0 replies; 15+ messages in thread
From: Matt Fleming @ 2013-02-12 12:31 UTC (permalink / raw)
  To: Al Viro
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Mon, 11 Feb, at 05:30:57PM, Al Viro wrote:
> On Mon, Feb 11, 2013 at 04:05:57PM +0000, Matt Fleming wrote:
> > > Egads, man...
> > > [snip the horror with copying the name]
> > > 
> > >         unsigned long hash = init_name_hash();
> > > 	const unsigned char *s = qstr->name;
> > > 	int len = qstr->len;
> > > 
> > > 	if (!efivarfs_valid_name(s, len))
> > > 		return -EINVAL;
> > >         while (len-- > GUID_LEN)
> > > 		hash = partial_name_hash(*s++, hash);
> > > 	/* GUID is case-insensitive. */
> > > 	while (len--)
> > > 		hash = partial_name_hash(tolower(*s++), hash);
> > >         return end_name_hash(hash);
> > 
> > Oh, wow, yes that is much nicer. I didn't realise we could build the
> > hash incrementally like that. Very cool.
> 
> ... except that it should end with qstr->hash = end_name_hash(hash); return 0;
> instead.  My apologies.

Oh, efivarfs also needs to stop using d_alloc_name(), which calls
full_name_hash(), because we now need to use our fs-specific hash
function.

Updated versions of both patches coming shortly.

-- 
Matt Fleming, Intel Open Source Technology Center

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

* [PATCH 1/2 v2] efivarfs: Validate filenames much more aggressively
       [not found]     ` <1360592935-26026-2-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  2013-02-11 15:01       ` Al Viro
@ 2013-02-12 12:36       ` Matt Fleming
  1 sibling, 0 replies; 15+ messages in thread
From: Matt Fleming @ 2013-02-12 12:36 UTC (permalink / raw)
  To: linux-efi-u79uwXL29TY76Z2rM5mHXA
  Cc: Lingzhu Xiang, Matthew Garrett, Jeremy Kerr, Al Viro, Matt Fleming

The only thing that efivarfs does to enforce a valid filename is
ensure that the name isn't too short. We need to strongly sanitise any
filenames, not least because variable creation is delayed until
efivarfs_file_write(), which means we can't rely on the firmware to
inform us of an invalid name, because if the file is never written to
we'll never know it's invalid.

Perform a couple of steps before agreeing to create a new file,

  * Ensure the GUID only contains hexadecimal digits and dashes.

  * Ensure that the GUID portion of the filename is the correct length
    and format.

  * The variable name portion of the filename needs to be at least one
    character in size.

Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: Matthew Garrett <mjg59-1xO5oi07KQx4cg9Nei1l7Q@public.gmane.org>
Cc: Jeremy Kerr <jeremy.kerr-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---

v2: Use Al's much improve filename parsing code and make
    efivarfs_valid_name() return the more natural boolean type.

 drivers/firmware/efivars.c | 49 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 44 insertions(+), 5 deletions(-)

diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 371c441..868cea5 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -79,6 +79,7 @@
 #include <linux/device.h>
 #include <linux/slab.h>
 #include <linux/pstore.h>
+#include <linux/ctype.h>
 
 #include <linux/fs.h>
 #include <linux/ramfs.h>
@@ -900,6 +901,48 @@ static struct inode *efivarfs_get_inode(struct super_block *sb,
 	return inode;
 }
 
+/*
+ * Return true if 'str' is a valid efivarfs filename of the form,
+ *
+ *	VariableName-12345678-1234-1234-1234-1234567891bc
+ */
+static bool efivarfs_valid_name(const char *str, int len)
+{
+	static const char dashes[GUID_LEN] = {
+		[8] = 1, [13] = 1, [18] = 1, [23] = 1
+	};
+	const char *s = str + len - GUID_LEN;
+	int i;
+
+	/*
+	 * We need a GUID, plus at least one letter for the variable name,
+	 * plus the '-' separator
+	 */
+	if (len < GUID_LEN + 2)
+		return false;
+
+	/* GUID should be right after the first '-' */
+	if (s - 1 != strchr(str, '-'))
+		return false;
+
+	/*
+	 * Validate that 's' is of the correct format, e.g.
+	 *
+	 *	12345678-1234-1234-1234-123456789abc
+	 */
+	for (i = 0; i < GUID_LEN; i++) {
+		if (dashes[i]) {
+			if (*s++ != '-')
+				return false;
+		} else {
+			if (!isxdigit(*s++))
+				return false;
+		}
+	}
+
+	return true;
+}
+
 static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)
 {
 	guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]);
@@ -928,11 +971,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
 	struct efivar_entry *var;
 	int namelen, i = 0, err = 0;
 
-	/*
-	 * We need a GUID, plus at least one letter for the variable name,
-	 * plus the '-' separator
-	 */
-	if (dentry->d_name.len < GUID_LEN + 2)
+	if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
 		return -EINVAL;
 
 	inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0);
-- 
1.7.11.7

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

* [PATCH 2/2 v2] efivarfs: guid part of filenames are case-insensitive
       [not found]     ` <1360592935-26026-3-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  2013-02-11 15:22       ` Al Viro
@ 2013-02-12 12:39       ` Matt Fleming
       [not found]         ` <20130212123934.GC14790-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
  1 sibling, 1 reply; 15+ messages in thread
From: Matt Fleming @ 2013-02-12 12:39 UTC (permalink / raw)
  To: linux-efi-u79uwXL29TY76Z2rM5mHXA
  Cc: Lingzhu Xiang, Matthew Garrett, Jeremy Kerr, Al Viro, Matt Fleming

It makes no sense to treat the following filenames as unique,

	VarName-abcdefab-abcd-abcd-abcd-abcdefabcdef
	VarName-ABCDEFAB-ABCD-ABCD-ABCD-ABCDEFABCDEF
	VarName-ABcDEfAB-ABcD-ABcD-ABcD-ABcDEfABcDEf
	VarName-aBcDEfAB-aBcD-aBcD-aBcD-aBcDEfaBcDEf
	... etc ...

since the guid will be converted into a binary representation, which
has no case.

Roll our own dentry operations so that we can treat the variable name
part of filenames ("VarName" in the above example) as case-sensitive,
but the guid portion as case-insensitive. That way, efivarfs will
refuse to create the above files if any one already exists.

Reported-by: Lingzhu Xiang <lxiang-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cc: Matthew Garrett <mjg59-1xO5oi07KQx4cg9Nei1l7Q@public.gmane.org>
Cc: Jeremy Kerr <jeremy.kerr-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
Cc: Al Viro <viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
Signed-off-by: Matt Fleming <matt.fleming-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---

v2: Use Al's non-eye-watering code for calculating the hash. Optimise
    efivarfs_d_compare() slightly by firstly checking whether the
    string lens match, this also avoids a possible out-of-bounds
    condition when performing the memcmp().

 drivers/firmware/efivars.c | 95 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 93 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 868cea5..5d055dc 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -1043,6 +1043,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry)
 	return -EINVAL;
 };
 
+/*
+ * Compare two efivarfs file names.
+ *
+ * An efivarfs filename is composed of two parts,
+ *
+ *	1. A case-sensitive variable name
+ *	2. A case-insensitive GUID
+ *
+ * So we need to perform a case-sensitive match on part 1 and a
+ * case-insensitive match on part 2.
+ */
+static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode,
+			      const struct dentry *dentry, const struct inode *inode,
+			      unsigned int len, const char *str,
+			      const struct qstr *name)
+{
+	int guid = len - GUID_LEN;
+
+	if (name->len != len)
+		return 1;
+
+	/* Case-sensitive compare for the variable name */
+	if (memcmp(str, name->name, guid))
+		return 1;
+
+	/* Case-insensitive compare for the GUID */
+	return strcasecmp(name->name + guid, str + guid);
+}
+
+static int efivarfs_d_hash(const struct dentry *dentry,
+			   const struct inode *inode, struct qstr *qstr)
+{
+	unsigned long hash = init_name_hash();
+	const unsigned char *s = qstr->name;
+	unsigned int len = qstr->len;
+
+	if (!efivarfs_valid_name(s, len))
+		return -EINVAL;
+
+	while (len-- > GUID_LEN)
+		hash = partial_name_hash(*s++, hash);
+
+	/* GUID is case-insensitive. */
+	while (len--)
+		hash = partial_name_hash(tolower(*s++), hash);
+
+	qstr->hash = end_name_hash(hash);
+	return 0;
+}
+
+/*
+ * Retaining negative dentries for an in-memory filesystem just wastes
+ * memory and lookup time: arrange for them to be deleted immediately.
+ */
+static int efivarfs_delete_dentry(const struct dentry *dentry)
+{
+	return 1;
+}
+
+static struct dentry_operations efivarfs_d_ops = {
+	.d_compare = efivarfs_d_compare,
+	.d_hash = efivarfs_d_hash,
+	.d_delete = efivarfs_delete_dentry,
+};
+
+static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
+{
+	struct qstr q;
+
+	q.name = name;
+	q.len = strlen(name);
+
+	if (efivarfs_d_hash(NULL, NULL, &q))
+		return NULL;
+
+	return d_alloc(parent, &q);
+}
+
 static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 {
 	struct inode *inode = NULL;
@@ -1058,6 +1136,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 	sb->s_blocksize_bits    = PAGE_CACHE_SHIFT;
 	sb->s_magic             = EFIVARFS_MAGIC;
 	sb->s_op                = &efivarfs_ops;
+	sb->s_d_op		= &efivarfs_d_ops;
 	sb->s_time_gran         = 1;
 
 	inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0);
@@ -1098,7 +1177,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
 		if (!inode)
 			goto fail_name;
 
-		dentry = d_alloc_name(root, name);
+		dentry = efivarfs_alloc_dentry(root, name);
 		if (!dentry)
 			goto fail_inode;
 
@@ -1148,8 +1227,20 @@ static struct file_system_type efivarfs_type = {
 	.kill_sb = efivarfs_kill_sb,
 };
 
+/*
+ * Handle negative dentry.
+ */
+static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry,
+				      unsigned int flags)
+{
+	if (dentry->d_name.len > NAME_MAX)
+		return ERR_PTR(-ENAMETOOLONG);
+	d_add(dentry, NULL);
+	return NULL;
+}
+
 static const struct inode_operations efivarfs_dir_inode_operations = {
-	.lookup = simple_lookup,
+	.lookup = efivarfs_lookup,
 	.unlink = efivarfs_unlink,
 	.create = efivarfs_create,
 };
-- 
1.7.11.7

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

* Re: [PATCH 2/2 v2] efivarfs: guid part of filenames are case-insensitive
       [not found]         ` <20130212123934.GC14790-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
@ 2013-02-14 16:04           ` Al Viro
       [not found]             ` <20130214160405.GU4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
  0 siblings, 1 reply; 15+ messages in thread
From: Al Viro @ 2013-02-14 16:04 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr, Matt Fleming

On Tue, Feb 12, 2013 at 12:39:34PM +0000, Matt Fleming wrote:

> +static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
> +{
> +	struct qstr q;
> +
> +	q.name = name;
> +	q.len = strlen(name);
> +
> +	if (efivarfs_d_hash(NULL, NULL, &q))
> +		return NULL;
> +
> +	return d_alloc(parent, &q);
> +}

> @@ -1098,7 +1177,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
>  		if (!inode)
>  			goto fail_name;
>  
> -		dentry = d_alloc_name(root, name);
> +		dentry = efivarfs_alloc_dentry(root, name);
>  		if (!dentry)
>  			goto fail_inode;

Umm...  That name has just been built by efivarfs_fill_super() itself, and
AFAICS there's no way for its GUID part to be _not_ lowercase
hex and with proper locations of dashes.  So
	a) hash value will be exactly full_name_hash(name), unless
efivarfs_valid_name() manages to fail.
	b) efivarfs_valid_name() is a serious overkill here - the only things
that might go wrong are length of and dashes in entry->var.VariableName.
Both are trivially checked while we are constructing name (before we do
inode allocation, etc.) and IMO they would be better off there.

IOW, I think this part of the patch is better handled by doing those two
checks several lines before d_alloc_name() and not bothering with
efivarfs_alloc_dentry() at all.  I.e.
		if (len + 1 + GUID_LEN > NAME_MAX)
			goto fail;

		name = kmalloc(len + 1 + GUID_LEN + 1, GFP_ATOMIC);
		if (!name)
			goto fail;

		for (i = 0; i < len; i++) {
			name[i] = entry->var.VariableName[i] & 0xFF;
			if (name[i] == '-')
				goto fail_name;
		}
and then as in the current efivarfs_fill_super().

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

* Re: [PATCH 2/2 v2] efivarfs: guid part of filenames are case-insensitive
       [not found]             ` <20130214160405.GU4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
@ 2013-02-14 17:11               ` Matt Fleming
       [not found]                 ` <1360861876.24917.52.camel-ZqTwcBeJ+wsBof6jY8KHXm7IUlhRatedral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
  0 siblings, 1 reply; 15+ messages in thread
From: Matt Fleming @ 2013-02-14 17:11 UTC (permalink / raw)
  To: Al Viro
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr

On Thu, 2013-02-14 at 16:04 +0000, Al Viro wrote:
> On Tue, Feb 12, 2013 at 12:39:34PM +0000, Matt Fleming wrote:
> 
> > +static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
> > +{
> > +	struct qstr q;
> > +
> > +	q.name = name;
> > +	q.len = strlen(name);
> > +
> > +	if (efivarfs_d_hash(NULL, NULL, &q))
> > +		return NULL;
> > +
> > +	return d_alloc(parent, &q);
> > +}
> 
> > @@ -1098,7 +1177,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
> >  		if (!inode)
> >  			goto fail_name;
> >  
> > -		dentry = d_alloc_name(root, name);
> > +		dentry = efivarfs_alloc_dentry(root, name);
> >  		if (!dentry)
> >  			goto fail_inode;
> 
> Umm...  That name has just been built by efivarfs_fill_super() itself, and
> AFAICS there's no way for its GUID part to be _not_ lowercase
> hex and with proper locations of dashes.  So
> 	a) hash value will be exactly full_name_hash(name), unless
> efivarfs_valid_name() manages to fail.

In my testing calling full_name_hash() and doing the partial_name_hash()
loop returned different results. This is on x86, with
CONFIG_DCACHE_WORD_ACCESS=y. I assumed they weren't compatible because
most (all?) file systems that do fs-specific hashing also fill out the
hash member using their fs-specific function, whereas efivarfs was
previously using d_alloc_name().

Is this mismatch indicative of a bug in efivarfs hashing?

-- 
Matt Fleming, Intel Open Source Technology Center

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

* Re: [PATCH 2/2 v2] efivarfs: guid part of filenames are case-insensitive
       [not found]                 ` <1360861876.24917.52.camel-ZqTwcBeJ+wsBof6jY8KHXm7IUlhRatedral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
@ 2013-02-14 17:55                   ` Al Viro
  0 siblings, 0 replies; 15+ messages in thread
From: Al Viro @ 2013-02-14 17:55 UTC (permalink / raw)
  To: Matt Fleming
  Cc: linux-efi-u79uwXL29TY76Z2rM5mHXA, Lingzhu Xiang, Matthew Garrett,
	Jeremy Kerr

On Thu, Feb 14, 2013 at 05:11:16PM +0000, Matt Fleming wrote:
> On Thu, 2013-02-14 at 16:04 +0000, Al Viro wrote:
> > On Tue, Feb 12, 2013 at 12:39:34PM +0000, Matt Fleming wrote:
> > 
> > > +static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name)
> > > +{
> > > +	struct qstr q;
> > > +
> > > +	q.name = name;
> > > +	q.len = strlen(name);
> > > +
> > > +	if (efivarfs_d_hash(NULL, NULL, &q))
> > > +		return NULL;
> > > +
> > > +	return d_alloc(parent, &q);
> > > +}
> > 
> > > @@ -1098,7 +1177,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent)
> > >  		if (!inode)
> > >  			goto fail_name;
> > >  
> > > -		dentry = d_alloc_name(root, name);
> > > +		dentry = efivarfs_alloc_dentry(root, name);
> > >  		if (!dentry)
> > >  			goto fail_inode;
> > 
> > Umm...  That name has just been built by efivarfs_fill_super() itself, and
> > AFAICS there's no way for its GUID part to be _not_ lowercase
> > hex and with proper locations of dashes.  So
> > 	a) hash value will be exactly full_name_hash(name), unless
> > efivarfs_valid_name() manages to fail.
> 
> In my testing calling full_name_hash() and doing the partial_name_hash()
> loop returned different results. This is on x86, with
> CONFIG_DCACHE_WORD_ACCESS=y. I assumed they weren't compatible because
> most (all?) file systems that do fs-specific hashing also fill out the
> hash member using their fs-specific function, whereas efivarfs was
> previously using d_alloc_name().
> 
> Is this mismatch indicative of a bug in efivarfs hashing?

No, just me forgetting about DCACHE_WORD_ACCESS.  Nevermind...

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

end of thread, other threads:[~2013-02-14 17:55 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-11 14:28 [PATCH 0/2] efivarfs patch queue Matt Fleming
     [not found] ` <1360592935-26026-1-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2013-02-11 14:28   ` [PATCH 1/2] efivarfs: Validate filenames much more aggressively Matt Fleming
     [not found]     ` <1360592935-26026-2-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2013-02-11 15:01       ` Al Viro
     [not found]         ` <20130211150109.GK4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
2013-02-11 15:12           ` Matt Fleming
2013-02-12 12:36       ` [PATCH 1/2 v2] " Matt Fleming
2013-02-11 14:28   ` [PATCH 2/2] efivarfs: guid part of filenames are case-insensitive Matt Fleming
     [not found]     ` <1360592935-26026-3-git-send-email-matt-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2013-02-11 15:22       ` Al Viro
     [not found]         ` <20130211152221.GL4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
2013-02-11 15:37           ` Al Viro
2013-02-11 16:05           ` Matt Fleming
     [not found]             ` <20130211160557.GB26681-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2013-02-11 17:30               ` Al Viro
     [not found]                 ` <20130211173057.GM4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
2013-02-12 12:31                   ` Matt Fleming
2013-02-12 12:39       ` [PATCH 2/2 v2] " Matt Fleming
     [not found]         ` <20130212123934.GC14790-HNK1S37rvNbeXh+fF434Mdi2O/JbrIOy@public.gmane.org>
2013-02-14 16:04           ` Al Viro
     [not found]             ` <20130214160405.GU4503-3bDd1+5oDREiFSDQTTA3OLVCufUGDwFn@public.gmane.org>
2013-02-14 17:11               ` Matt Fleming
     [not found]                 ` <1360861876.24917.52.camel-ZqTwcBeJ+wsBof6jY8KHXm7IUlhRatedral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
2013-02-14 17:55                   ` Al Viro

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.