diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-27 19:17:42 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-27 19:17:42 -0500 |
commit | e3c4877de8b9d93bd47b6ee88eb594b1c1e10da5 (patch) | |
tree | 17ade3a3a13b1e7768abe3561b6808dc4980a5f6 /drivers/firmware | |
parent | 18a44a7ff1075ce5157ac07cde573aca6b5e9973 (diff) | |
parent | 6b59e366e074d3962e04f01efb8acc10a33c0e1e (diff) |
Merge branch 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86/EFI changes from Peter Anvin:
- Improve the initrd handling in the EFI boot stub by allowing forward
slashes in the pathname - from Chun-Yi Lee.
- Cleanup code duplication in the EFI mixed kernel/firmware code - from
Satoru Takeuchi.
- efivarfs bug fixes for more strict filename validation, with lots of
input from Al Viro.
* 'x86-efi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86, efi: remove duplicate code in setup_arch() by using, efi_is_native()
efivarfs: guid part of filenames are case-insensitive
efivarfs: Validate filenames much more aggressively
efivarfs: Use sizeof() instead of magic number
x86, efi: Allow slash in file path of initrd
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efivars.c | 146 |
1 files changed, 138 insertions, 8 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index fed08b661711..7320bf891706 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -79,6 +79,7 @@ | |||
79 | #include <linux/device.h> | 79 | #include <linux/device.h> |
80 | #include <linux/slab.h> | 80 | #include <linux/slab.h> |
81 | #include <linux/pstore.h> | 81 | #include <linux/pstore.h> |
82 | #include <linux/ctype.h> | ||
82 | 83 | ||
83 | #include <linux/fs.h> | 84 | #include <linux/fs.h> |
84 | #include <linux/ramfs.h> | 85 | #include <linux/ramfs.h> |
@@ -908,6 +909,48 @@ static struct inode *efivarfs_get_inode(struct super_block *sb, | |||
908 | return inode; | 909 | return inode; |
909 | } | 910 | } |
910 | 911 | ||
912 | /* | ||
913 | * Return true if 'str' is a valid efivarfs filename of the form, | ||
914 | * | ||
915 | * VariableName-12345678-1234-1234-1234-1234567891bc | ||
916 | */ | ||
917 | static bool efivarfs_valid_name(const char *str, int len) | ||
918 | { | ||
919 | static const char dashes[GUID_LEN] = { | ||
920 | [8] = 1, [13] = 1, [18] = 1, [23] = 1 | ||
921 | }; | ||
922 | const char *s = str + len - GUID_LEN; | ||
923 | int i; | ||
924 | |||
925 | /* | ||
926 | * We need a GUID, plus at least one letter for the variable name, | ||
927 | * plus the '-' separator | ||
928 | */ | ||
929 | if (len < GUID_LEN + 2) | ||
930 | return false; | ||
931 | |||
932 | /* GUID should be right after the first '-' */ | ||
933 | if (s - 1 != strchr(str, '-')) | ||
934 | return false; | ||
935 | |||
936 | /* | ||
937 | * Validate that 's' is of the correct format, e.g. | ||
938 | * | ||
939 | * 12345678-1234-1234-1234-123456789abc | ||
940 | */ | ||
941 | for (i = 0; i < GUID_LEN; i++) { | ||
942 | if (dashes[i]) { | ||
943 | if (*s++ != '-') | ||
944 | return false; | ||
945 | } else { | ||
946 | if (!isxdigit(*s++)) | ||
947 | return false; | ||
948 | } | ||
949 | } | ||
950 | |||
951 | return true; | ||
952 | } | ||
953 | |||
911 | static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) | 954 | static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) |
912 | { | 955 | { |
913 | guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]); | 956 | guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]); |
@@ -936,11 +979,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, | |||
936 | struct efivar_entry *var; | 979 | struct efivar_entry *var; |
937 | int namelen, i = 0, err = 0; | 980 | int namelen, i = 0, err = 0; |
938 | 981 | ||
939 | /* | 982 | if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) |
940 | * We need a GUID, plus at least one letter for the variable name, | ||
941 | * plus the '-' separator | ||
942 | */ | ||
943 | if (dentry->d_name.len < GUID_LEN + 2) | ||
944 | return -EINVAL; | 983 | return -EINVAL; |
945 | 984 | ||
946 | inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); | 985 | inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); |
@@ -1012,6 +1051,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) | |||
1012 | return -EINVAL; | 1051 | return -EINVAL; |
1013 | }; | 1052 | }; |
1014 | 1053 | ||
1054 | /* | ||
1055 | * Compare two efivarfs file names. | ||
1056 | * | ||
1057 | * An efivarfs filename is composed of two parts, | ||
1058 | * | ||
1059 | * 1. A case-sensitive variable name | ||
1060 | * 2. A case-insensitive GUID | ||
1061 | * | ||
1062 | * So we need to perform a case-sensitive match on part 1 and a | ||
1063 | * case-insensitive match on part 2. | ||
1064 | */ | ||
1065 | static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode, | ||
1066 | const struct dentry *dentry, const struct inode *inode, | ||
1067 | unsigned int len, const char *str, | ||
1068 | const struct qstr *name) | ||
1069 | { | ||
1070 | int guid = len - GUID_LEN; | ||
1071 | |||
1072 | if (name->len != len) | ||
1073 | return 1; | ||
1074 | |||
1075 | /* Case-sensitive compare for the variable name */ | ||
1076 | if (memcmp(str, name->name, guid)) | ||
1077 | return 1; | ||
1078 | |||
1079 | /* Case-insensitive compare for the GUID */ | ||
1080 | return strncasecmp(name->name + guid, str + guid, GUID_LEN); | ||
1081 | } | ||
1082 | |||
1083 | static int efivarfs_d_hash(const struct dentry *dentry, | ||
1084 | const struct inode *inode, struct qstr *qstr) | ||
1085 | { | ||
1086 | unsigned long hash = init_name_hash(); | ||
1087 | const unsigned char *s = qstr->name; | ||
1088 | unsigned int len = qstr->len; | ||
1089 | |||
1090 | if (!efivarfs_valid_name(s, len)) | ||
1091 | return -EINVAL; | ||
1092 | |||
1093 | while (len-- > GUID_LEN) | ||
1094 | hash = partial_name_hash(*s++, hash); | ||
1095 | |||
1096 | /* GUID is case-insensitive. */ | ||
1097 | while (len--) | ||
1098 | hash = partial_name_hash(tolower(*s++), hash); | ||
1099 | |||
1100 | qstr->hash = end_name_hash(hash); | ||
1101 | return 0; | ||
1102 | } | ||
1103 | |||
1104 | /* | ||
1105 | * Retaining negative dentries for an in-memory filesystem just wastes | ||
1106 | * memory and lookup time: arrange for them to be deleted immediately. | ||
1107 | */ | ||
1108 | static int efivarfs_delete_dentry(const struct dentry *dentry) | ||
1109 | { | ||
1110 | return 1; | ||
1111 | } | ||
1112 | |||
1113 | static struct dentry_operations efivarfs_d_ops = { | ||
1114 | .d_compare = efivarfs_d_compare, | ||
1115 | .d_hash = efivarfs_d_hash, | ||
1116 | .d_delete = efivarfs_delete_dentry, | ||
1117 | }; | ||
1118 | |||
1119 | static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) | ||
1120 | { | ||
1121 | struct qstr q; | ||
1122 | |||
1123 | q.name = name; | ||
1124 | q.len = strlen(name); | ||
1125 | |||
1126 | if (efivarfs_d_hash(NULL, NULL, &q)) | ||
1127 | return NULL; | ||
1128 | |||
1129 | return d_alloc(parent, &q); | ||
1130 | } | ||
1131 | |||
1015 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | 1132 | static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) |
1016 | { | 1133 | { |
1017 | struct inode *inode = NULL; | 1134 | struct inode *inode = NULL; |
@@ -1027,6 +1144,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
1027 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | 1144 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; |
1028 | sb->s_magic = EFIVARFS_MAGIC; | 1145 | sb->s_magic = EFIVARFS_MAGIC; |
1029 | sb->s_op = &efivarfs_ops; | 1146 | sb->s_op = &efivarfs_ops; |
1147 | sb->s_d_op = &efivarfs_d_ops; | ||
1030 | sb->s_time_gran = 1; | 1148 | sb->s_time_gran = 1; |
1031 | 1149 | ||
1032 | inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); | 1150 | inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); |
@@ -1067,7 +1185,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
1067 | if (!inode) | 1185 | if (!inode) |
1068 | goto fail_name; | 1186 | goto fail_name; |
1069 | 1187 | ||
1070 | dentry = d_alloc_name(root, name); | 1188 | dentry = efivarfs_alloc_dentry(root, name); |
1071 | if (!dentry) | 1189 | if (!dentry) |
1072 | goto fail_inode; | 1190 | goto fail_inode; |
1073 | 1191 | ||
@@ -1084,7 +1202,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
1084 | 1202 | ||
1085 | mutex_lock(&inode->i_mutex); | 1203 | mutex_lock(&inode->i_mutex); |
1086 | inode->i_private = entry; | 1204 | inode->i_private = entry; |
1087 | i_size_write(inode, size+4); | 1205 | i_size_write(inode, size + sizeof(entry->var.Attributes)); |
1088 | mutex_unlock(&inode->i_mutex); | 1206 | mutex_unlock(&inode->i_mutex); |
1089 | d_add(dentry, inode); | 1207 | d_add(dentry, inode); |
1090 | } | 1208 | } |
@@ -1117,8 +1235,20 @@ static struct file_system_type efivarfs_type = { | |||
1117 | .kill_sb = efivarfs_kill_sb, | 1235 | .kill_sb = efivarfs_kill_sb, |
1118 | }; | 1236 | }; |
1119 | 1237 | ||
1238 | /* | ||
1239 | * Handle negative dentry. | ||
1240 | */ | ||
1241 | static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry, | ||
1242 | unsigned int flags) | ||
1243 | { | ||
1244 | if (dentry->d_name.len > NAME_MAX) | ||
1245 | return ERR_PTR(-ENAMETOOLONG); | ||
1246 | d_add(dentry, NULL); | ||
1247 | return NULL; | ||
1248 | } | ||
1249 | |||
1120 | static const struct inode_operations efivarfs_dir_inode_operations = { | 1250 | static const struct inode_operations efivarfs_dir_inode_operations = { |
1121 | .lookup = simple_lookup, | 1251 | .lookup = efivarfs_lookup, |
1122 | .unlink = efivarfs_unlink, | 1252 | .unlink = efivarfs_unlink, |
1123 | .create = efivarfs_create, | 1253 | .create = efivarfs_create, |
1124 | }; | 1254 | }; |