Skip to content

Commit fba4cea

Browse files
committed
configfs-tsm-report: Fix NULL dereference of tsm_ops
Unlike sysfs, the lifetime of configfs objects is controlled by userspace. There is no mechanism for the kernel to find and delete all created config-items. Instead, the configfs-tsm-report mechanism has an expectation that tsm_unregister() can happen at any time and cause established config-item access to start failing. That expectation is not fully satisfied. While tsm_report_read(), tsm_report_{is,is_bin}_visible(), and tsm_report_make_item() safely fail if tsm_ops have been unregistered, tsm_report_privlevel_store() tsm_report_provider_show() fail to check for ops registration. Add the missing checks for tsm_ops having been removed. Now, in supporting the ability for tsm_unregister() to always succeed, it leaves the problem of what to do with lingering config-items. The expectation is that the admin that arranges for the ->remove() (unbind) of the ${tsm_arch}-guest driver is also responsible for deletion of all open config-items. Until that deletion happens, ->probe() (reload / bind) of the ${tsm_arch}-guest driver fails. This allows for emergency shutdown / revocation of attestation interfaces, and requires coordinated restart. Fixes: 70e6f7e ("configfs-tsm: Introduce a shared ABI for attestation reports") Cc: stable@vger.kernel.org Cc: Suzuki K Poulose <suzuki.poulose@arm.com> Cc: Steven Price <steven.price@arm.com> Cc: Sami Mujawar <sami.mujawar@arm.com> Cc: Borislav Petkov (AMD) <bp@alien8.de> Cc: Tom Lendacky <thomas.lendacky@amd.com> Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com> Reported-by: Cedric Xing <cedric.xing@intel.com> Reviewed-by: Kai Huang <kai.huang@intel.com> Link: https://patch.msgid.link/20250430203331.1177062-1-dan.j.williams@intel.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 8ffd015 commit fba4cea

File tree

1 file changed

+29
-2
lines changed

1 file changed

+29
-2
lines changed

drivers/virt/coco/tsm.c

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
static struct tsm_provider {
1616
const struct tsm_ops *ops;
1717
void *data;
18+
atomic_t count;
1819
} provider;
1920
static DECLARE_RWSEM(tsm_rwsem);
2021

@@ -92,6 +93,10 @@ static ssize_t tsm_report_privlevel_store(struct config_item *cfg,
9293
if (rc)
9394
return rc;
9495

96+
guard(rwsem_write)(&tsm_rwsem);
97+
if (!provider.ops)
98+
return -ENXIO;
99+
95100
/*
96101
* The valid privilege levels that a TSM might accept, if it accepts a
97102
* privilege level setting at all, are a max of TSM_PRIVLEVEL_MAX (see
@@ -101,7 +106,6 @@ static ssize_t tsm_report_privlevel_store(struct config_item *cfg,
101106
if (provider.ops->privlevel_floor > val || val > TSM_PRIVLEVEL_MAX)
102107
return -EINVAL;
103108

104-
guard(rwsem_write)(&tsm_rwsem);
105109
rc = try_advance_write_generation(report);
106110
if (rc)
107111
return rc;
@@ -115,6 +119,10 @@ static ssize_t tsm_report_privlevel_floor_show(struct config_item *cfg,
115119
char *buf)
116120
{
117121
guard(rwsem_read)(&tsm_rwsem);
122+
123+
if (!provider.ops)
124+
return -ENXIO;
125+
118126
return sysfs_emit(buf, "%u\n", provider.ops->privlevel_floor);
119127
}
120128
CONFIGFS_ATTR_RO(tsm_report_, privlevel_floor);
@@ -217,6 +225,9 @@ CONFIGFS_ATTR_RO(tsm_report_, generation);
217225
static ssize_t tsm_report_provider_show(struct config_item *cfg, char *buf)
218226
{
219227
guard(rwsem_read)(&tsm_rwsem);
228+
if (!provider.ops)
229+
return -ENXIO;
230+
220231
return sysfs_emit(buf, "%s\n", provider.ops->name);
221232
}
222233
CONFIGFS_ATTR_RO(tsm_report_, provider);
@@ -284,7 +295,7 @@ static ssize_t tsm_report_read(struct tsm_report *report, void *buf,
284295
guard(rwsem_write)(&tsm_rwsem);
285296
ops = provider.ops;
286297
if (!ops)
287-
return -ENOTTY;
298+
return -ENXIO;
288299
if (!report->desc.inblob_len)
289300
return -EINVAL;
290301

@@ -421,12 +432,20 @@ static struct config_item *tsm_report_make_item(struct config_group *group,
421432
if (!state)
422433
return ERR_PTR(-ENOMEM);
423434

435+
atomic_inc(&provider.count);
424436
config_item_init_type_name(&state->cfg, name, &tsm_report_type);
425437
return &state->cfg;
426438
}
427439

440+
static void tsm_report_drop_item(struct config_group *group, struct config_item *item)
441+
{
442+
config_item_put(item);
443+
atomic_dec(&provider.count);
444+
}
445+
428446
static struct configfs_group_operations tsm_report_group_ops = {
429447
.make_item = tsm_report_make_item,
448+
.drop_item = tsm_report_drop_item,
430449
};
431450

432451
static const struct config_item_type tsm_reports_type = {
@@ -459,6 +478,11 @@ int tsm_register(const struct tsm_ops *ops, void *priv)
459478
return -EBUSY;
460479
}
461480

481+
if (atomic_read(&provider.count)) {
482+
pr_err("configfs/tsm/report not empty\n");
483+
return -EBUSY;
484+
}
485+
462486
provider.ops = ops;
463487
provider.data = priv;
464488
return 0;
@@ -470,6 +494,9 @@ int tsm_unregister(const struct tsm_ops *ops)
470494
guard(rwsem_write)(&tsm_rwsem);
471495
if (ops != provider.ops)
472496
return -EBUSY;
497+
if (atomic_read(&provider.count))
498+
pr_warn("\"%s\" unregistered with items present in configfs/tsm/report\n",
499+
provider.ops->name);
473500
provider.ops = NULL;
474501
provider.data = NULL;
475502
return 0;

0 commit comments

Comments
 (0)