Skip to content

Commit fddca52

Browse files
James Bottomleyardbiesheuvel
authored andcommitted
efivarfs: move variable lifetime management into the inodes
Make the inodes the default management vehicle for struct efivar_entry, so they are now all freed automatically if the file is removed and on unmount in kill_litter_super(). Remove the now superfluous iterator to free the entries after kill_litter_super(). Also fixes a bug where some entry freeing was missing causing efivarfs to leak memory. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
1 parent 7e365c7 commit fddca52

File tree

4 files changed

+48
-72
lines changed

4 files changed

+48
-72
lines changed

fs/efivarfs/inode.c

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,23 @@ static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
8282
struct efivar_entry *var;
8383
int namelen, i = 0, err = 0;
8484
bool is_removable = false;
85+
efi_guid_t vendor;
8586

8687
if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len))
8788
return -EINVAL;
8889

89-
var = kzalloc(sizeof(struct efivar_entry), GFP_KERNEL);
90-
if (!var)
91-
return -ENOMEM;
92-
9390
/* length of the variable name itself: remove GUID and separator */
9491
namelen = dentry->d_name.len - EFI_VARIABLE_GUID_LEN - 1;
9592

96-
err = guid_parse(dentry->d_name.name + namelen + 1, &var->var.VendorGuid);
93+
err = guid_parse(dentry->d_name.name + namelen + 1, &vendor);
9794
if (err)
9895
goto out;
99-
if (guid_equal(&var->var.VendorGuid, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) {
96+
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID)) {
10097
err = -EPERM;
10198
goto out;
10299
}
103100

104-
if (efivar_variable_is_removable(var->var.VendorGuid,
101+
if (efivar_variable_is_removable(vendor,
105102
dentry->d_name.name, namelen))
106103
is_removable = true;
107104

@@ -110,14 +107,16 @@ static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
110107
err = -ENOMEM;
111108
goto out;
112109
}
110+
var = efivar_entry(inode);
111+
112+
var->var.VendorGuid = vendor;
113113

114114
for (i = 0; i < namelen; i++)
115115
var->var.VariableName[i] = dentry->d_name.name[i];
116116

117117
var->var.VariableName[i] = '\0';
118118

119119
inode->i_private = var;
120-
kmemleak_ignore(var);
121120

122121
err = efivar_entry_add(var, &info->efivarfs_list);
123122
if (err)
@@ -126,11 +125,9 @@ static int efivarfs_create(struct mnt_idmap *idmap, struct inode *dir,
126125
d_instantiate(dentry, inode);
127126
dget(dentry);
128127
out:
129-
if (err) {
130-
kfree(var);
131-
if (inode)
132-
iput(inode);
133-
}
128+
if (err && inode)
129+
iput(inode);
130+
134131
return err;
135132
}
136133

fs/efivarfs/internal.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,20 @@ struct efi_variable {
2929
struct efivar_entry {
3030
struct efi_variable var;
3131
struct list_head list;
32+
struct inode vfs_inode;
3233
};
3334

35+
static inline struct efivar_entry *efivar_entry(struct inode *inode)
36+
{
37+
return container_of(inode, struct efivar_entry, vfs_inode);
38+
}
39+
3440
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *,
3541
struct list_head *),
3642
void *data, struct list_head *head);
3743

3844
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
3945
void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
40-
void efivar_entry_remove(struct efivar_entry *entry);
4146
int efivar_entry_delete(struct efivar_entry *entry);
4247

4348
int efivar_entry_size(struct efivar_entry *entry, unsigned long *size);

fs/efivarfs/super.c

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,25 @@ static int efivarfs_ops_notifier(struct notifier_block *nb, unsigned long event,
3939
return NOTIFY_OK;
4040
}
4141

42-
static void efivarfs_evict_inode(struct inode *inode)
42+
static struct inode *efivarfs_alloc_inode(struct super_block *sb)
4343
{
44-
clear_inode(inode);
44+
struct efivar_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
45+
46+
if (!entry)
47+
return NULL;
48+
49+
inode_init_once(&entry->vfs_inode);
50+
51+
return &entry->vfs_inode;
52+
}
53+
54+
static void efivarfs_free_inode(struct inode *inode)
55+
{
56+
struct efivar_entry *entry = efivar_entry(inode);
57+
58+
if (inode->i_private)
59+
list_del(&entry->list);
60+
kfree(entry);
4561
}
4662

4763
static int efivarfs_show_options(struct seq_file *m, struct dentry *root)
@@ -106,7 +122,8 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
106122
static const struct super_operations efivarfs_ops = {
107123
.statfs = efivarfs_statfs,
108124
.drop_inode = generic_delete_inode,
109-
.evict_inode = efivarfs_evict_inode,
125+
.alloc_inode = efivarfs_alloc_inode,
126+
.free_inode = efivarfs_free_inode,
110127
.show_options = efivarfs_show_options,
111128
};
112129

@@ -227,28 +244,26 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
227244
if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
228245
return 0;
229246

230-
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
231-
if (!entry)
232-
return err;
233-
234-
memcpy(entry->var.VariableName, name16, name_size);
235-
memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
236-
237247
name = efivar_get_utf8name(name16, &vendor);
238248
if (!name)
239-
goto fail;
249+
return err;
240250

241251
/* length of the variable name itself: remove GUID and separator */
242252
len = strlen(name) - EFI_VARIABLE_GUID_LEN - 1;
243253

244-
if (efivar_variable_is_removable(entry->var.VendorGuid, name, len))
254+
if (efivar_variable_is_removable(vendor, name, len))
245255
is_removable = true;
246256

247257
inode = efivarfs_get_inode(sb, d_inode(root), S_IFREG | 0644, 0,
248258
is_removable);
249259
if (!inode)
250260
goto fail_name;
251261

262+
entry = efivar_entry(inode);
263+
264+
memcpy(entry->var.VariableName, name16, name_size);
265+
memcpy(&(entry->var.VendorGuid), &vendor, sizeof(efi_guid_t));
266+
252267
dentry = efivarfs_alloc_dentry(root, name);
253268
if (IS_ERR(dentry)) {
254269
err = PTR_ERR(dentry);
@@ -273,16 +288,8 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
273288
iput(inode);
274289
fail_name:
275290
kfree(name);
276-
fail:
277-
kfree(entry);
278-
return err;
279-
}
280291

281-
static int efivarfs_destroy(struct efivar_entry *entry, void *data)
282-
{
283-
efivar_entry_remove(entry);
284-
kfree(entry);
285-
return 0;
292+
return err;
286293
}
287294

288295
enum {
@@ -407,7 +414,7 @@ static void efivarfs_kill_sb(struct super_block *sb)
407414
kill_litter_super(sb);
408415

409416
/* Remove all entries and destroy */
410-
efivar_entry_iter(efivarfs_destroy, &sfi->efivarfs_list, NULL);
417+
WARN_ON(!list_empty(&sfi->efivarfs_list));
411418
kfree(sfi);
412419
}
413420

fs/efivarfs/vars.c

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -485,34 +485,6 @@ void __efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
485485
list_add(&entry->list, head);
486486
}
487487

488-
/**
489-
* efivar_entry_remove - remove entry from variable list
490-
* @entry: entry to remove from list
491-
*
492-
* Returns 0 on success, or a kernel error code on failure.
493-
*/
494-
void efivar_entry_remove(struct efivar_entry *entry)
495-
{
496-
list_del(&entry->list);
497-
}
498-
499-
/*
500-
* efivar_entry_list_del_unlock - remove entry from variable list
501-
* @entry: entry to remove
502-
*
503-
* Remove @entry from the variable list and release the list lock.
504-
*
505-
* NOTE: slightly weird locking semantics here - we expect to be
506-
* called with the efivars lock already held, and we release it before
507-
* returning. This is because this function is usually called after
508-
* set_variable() while the lock is still held.
509-
*/
510-
static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
511-
{
512-
list_del(&entry->list);
513-
efivar_unlock();
514-
}
515-
516488
/**
517489
* efivar_entry_delete - delete variable and remove entry from list
518490
* @entry: entry containing variable to delete
@@ -536,12 +508,10 @@ int efivar_entry_delete(struct efivar_entry *entry)
536508
status = efivar_set_variable_locked(entry->var.VariableName,
537509
&entry->var.VendorGuid,
538510
0, 0, NULL, false);
539-
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
540-
efivar_unlock();
511+
efivar_unlock();
512+
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND))
541513
return efi_status_to_err(status);
542-
}
543514

544-
efivar_entry_list_del_unlock(entry);
545515
return 0;
546516
}
547517

@@ -679,10 +649,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
679649
&entry->var.VendorGuid,
680650
NULL, size, NULL);
681651

682-
if (status == EFI_NOT_FOUND)
683-
efivar_entry_list_del_unlock(entry);
684-
else
685-
efivar_unlock();
652+
efivar_unlock();
686653

687654
if (status && status != EFI_BUFFER_TOO_SMALL)
688655
return efi_status_to_err(status);

0 commit comments

Comments
 (0)