* [PATCH] nvme: make sending wall-clock time to NVMe opt-in
@ 2026-07-13 20:34 Daniel Colascione
2026-07-13 20:52 ` Keith Busch
0 siblings, 1 reply; 3+ messages in thread
From: Daniel Colascione @ 2026-07-13 20:34 UTC (permalink / raw)
To: Keith Busch, Jens Axboe, Christoph Hellwig, Sagi Grimberg
Cc: linux-nvme, linux-kernel, Daniel Colascione
Some NVMe devices maintain a persistent log, the PEL, of events like
power-on and thermal excursions. The NVMe Set Features (Timestamp)
command allows an operating system to inform the NVMe of the current
wall-clock time. Wall-clock timestamp updates are logged to the PEL
alongside other events. By correlating PEL records, an attacker can
infer a user's usage patterns and even guess at time zone changes.
This change adds a per-controller NVMe flag accessible via sysfs that
controls whether we send the device the time. The flag is always present
and does nothing if the Timestamp feature is not supported. We update
the NVMe device's wall-clock time during controller initialization (if
the flag is enabled) and whenever the flag transitions from 0 to 1 on a
live controller. The flag latches the requested value. Sending the
timestamp to the device is best-effort and warns on every failure.
The nvme_core.timestamps_enabled_default module parameter supplies the
default value of the per-controller flag. Default it to false as the
privacy-preserving choice. Users who want to provide controllers with
real-world time can set the module parameter to true or enable
the per-controller sysfs flag, perhaps via udev.
As an alternative, we could also get the timestamp updates out of the
kernel entirely and have interested users run nvme(1) to
update timestamps.
Signed-off-by: Daniel Colascione <dancol@dancol.org>
---
Documentation/ABI/testing/sysfs-nvme | 21 ++++++++++++++++++
MAINTAINERS | 2 +-
drivers/nvme/host/core.c | 22 +++++++++++--------
drivers/nvme/host/nvme.h | 2 ++
drivers/nvme/host/sysfs.c | 32 ++++++++++++++++++++++++++++
5 files changed, 69 insertions(+), 10 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-nvme
diff --git a/Documentation/ABI/testing/sysfs-nvme b/Documentation/ABI/testing/sysfs-nvme
new file mode 100644
index 0000000000000..bb98974f22b7f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-nvme
@@ -0,0 +1,21 @@
+What: /sys/class/nvme/nvmeX/timestamps_enabled
+Date: July 2026
+KernelVersion: 7.3
+Contact: Linux NVMe mailing list <linux-nvme@lists.infradead.org>
+Description:
+ Shows or sets whether the kernel sends wall-clock time to the
+ controller using the NVMe Timestamp feature. Reading returns 1
+ (enabled) or 0 (disabled). Writing accepts a boolean value. The
+ attribute is present for every controller. On controllers that
+ do not support the Timestamp feature, the setting has no effect.
+
+ The per-controller value is initialized from
+ nvme_core.timestamps_enabled_default, which defaults to false. When
+ enabled, the kernel sends a timestamp whenever the controller
+ starts, including on controller resets and resume from suspend.
+ Changing the value from 0 to 1 on a live controller
+ sends a timestamp immediately. Disabling does not affect a
+ timestamp already sent.
+
+ Writes latch the requested value. Sending timestamps to the
+ controller is best-effort; every failure generates a warning.
diff --git a/MAINTAINERS b/MAINTAINERS
index 5bbc5b49d36a3..c6f46a4e8dd37 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19207,7 +19207,7 @@ L: linux-nvme@lists.infradead.org
S: Supported
W: http://git.infradead.org/nvme.git
T: git git://git.infradead.org/nvme.git
-F: Documentation/ABI/stable/sysfs-nvme
+F: Documentation/ABI/*/sysfs-nvme
F: Documentation/admin-guide/nvme-multipath.rst
F: Documentation/fault-injection/nvme-fault-injection.rst
F: Documentation/nvme/
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index db0c8ad4628a7..a49f71563e112 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -71,6 +71,11 @@ module_param(default_ps_max_latency_us, ulong, 0644);
MODULE_PARM_DESC(default_ps_max_latency_us,
"max power saving latency for new devices; use PM QOS to change per device");
+static bool timestamps_enabled_default;
+module_param(timestamps_enabled_default, bool, 0644);
+MODULE_PARM_DESC(timestamps_enabled_default,
+ "default value of the per-controller timestamps_enabled sysfs attribute");
+
static bool force_apst;
module_param(force_apst, bool, 0644);
MODULE_PARM_DESC(force_apst, "allow APST for newly enumerated devices even if quirked off");
@@ -2824,21 +2829,21 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl)
}
EXPORT_SYMBOL_GPL(nvme_enable_ctrl);
-static int nvme_configure_timestamp(struct nvme_ctrl *ctrl)
+void nvme_configure_timestamp(struct nvme_ctrl *ctrl)
{
__le64 ts;
int ret;
- if (!(ctrl->oncs & NVME_CTRL_ONCS_TIMESTAMP))
- return 0;
+ if (!READ_ONCE(ctrl->timestamps_enabled) ||
+ !(ctrl->oncs & NVME_CTRL_ONCS_TIMESTAMP))
+ return;
ts = cpu_to_le64(ktime_to_ms(ktime_get_real()));
ret = nvme_set_features(ctrl, NVME_FEAT_TIMESTAMP, 0, &ts, sizeof(ts),
NULL);
if (ret)
- dev_warn_once(ctrl->device,
- "could not set timestamp (%d)\n", ret);
- return ret;
+ dev_warn(ctrl->device,
+ "could not set timestamp (%d)\n", ret);
}
static int nvme_configure_host_options(struct nvme_ctrl *ctrl)
@@ -3771,9 +3776,7 @@ int nvme_init_ctrl_finish(struct nvme_ctrl *ctrl, bool was_suspended)
if (ret < 0)
return ret;
- ret = nvme_configure_timestamp(ctrl);
- if (ret < 0)
- return ret;
+ nvme_configure_timestamp(ctrl);
ret = nvme_configure_host_options(ctrl);
if (ret < 0)
@@ -5168,6 +5171,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
WRITE_ONCE(ctrl->state, NVME_CTRL_NEW);
ctrl->passthru_err_log_enabled = false;
+ ctrl->timestamps_enabled = READ_ONCE(timestamps_enabled_default);
clear_bit(NVME_CTRL_FAILFAST_EXPIRED, &ctrl->flags);
spin_lock_init(&ctrl->lock);
mutex_init(&ctrl->namespaces_lock);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index a679a4c61462d..47f7d6e7c5438 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -335,6 +335,7 @@ struct nvme_ctrl {
bool comp_seen;
bool identified;
bool passthru_err_log_enabled;
+ bool timestamps_enabled;
enum nvme_ctrl_state state;
spinlock_t lock;
struct mutex scan_lock;
@@ -891,6 +892,7 @@ void nvme_uninit_ctrl(struct nvme_ctrl *ctrl);
void nvme_start_ctrl(struct nvme_ctrl *ctrl);
void nvme_stop_ctrl(struct nvme_ctrl *ctrl);
int nvme_init_ctrl_finish(struct nvme_ctrl *ctrl, bool was_suspended);
+void nvme_configure_timestamp(struct nvme_ctrl *ctrl);
int nvme_alloc_admin_tag_set(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
const struct blk_mq_ops *ops, unsigned int cmd_size);
void nvme_remove_admin_tag_set(struct nvme_ctrl *ctrl);
diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c
index abf8edaae371b..fe62747630014 100644
--- a/drivers/nvme/host/sysfs.c
+++ b/drivers/nvme/host/sysfs.c
@@ -36,6 +36,37 @@ static ssize_t nvme_sysfs_rescan(struct device *dev,
}
static DEVICE_ATTR(rescan_controller, S_IWUSR, NULL, nvme_sysfs_rescan);
+static ssize_t timestamps_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", READ_ONCE(ctrl->timestamps_enabled));
+}
+
+static ssize_t timestamps_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+ bool enabled, was_enabled;
+ int ret;
+
+ ret = kstrtobool(buf, &enabled);
+ if (ret)
+ return ret;
+
+ was_enabled = READ_ONCE(ctrl->timestamps_enabled);
+ WRITE_ONCE(ctrl->timestamps_enabled, enabled);
+
+ if (enabled && !was_enabled &&
+ nvme_ctrl_state(ctrl) == NVME_CTRL_LIVE)
+ nvme_configure_timestamp(ctrl);
+
+ return count;
+}
+static DEVICE_ATTR_RW(timestamps_enabled);
+
static ssize_t nvme_adm_passthru_err_log_enabled_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -929,6 +960,7 @@ static DEVICE_ATTR(dhchap_ctrl_secret, S_IRUGO | S_IWUSR,
static struct attribute *nvme_dev_attrs[] = {
&dev_attr_reset_controller.attr,
&dev_attr_rescan_controller.attr,
+ &dev_attr_timestamps_enabled.attr,
&dev_attr_model.attr,
&dev_attr_serial.attr,
&dev_attr_firmware_rev.attr,
base-commit: cdf9a65e80ec874b630502946d269fac38dc5de8
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] nvme: make sending wall-clock time to NVMe opt-in
2026-07-13 20:34 [PATCH] nvme: make sending wall-clock time to NVMe opt-in Daniel Colascione
@ 2026-07-13 20:52 ` Keith Busch
2026-07-13 21:05 ` Daniel Colascione
0 siblings, 1 reply; 3+ messages in thread
From: Keith Busch @ 2026-07-13 20:52 UTC (permalink / raw)
To: Daniel Colascione
Cc: Jens Axboe, Christoph Hellwig, Sagi Grimberg, linux-nvme,
linux-kernel
On Mon, Jul 13, 2026 at 04:34:43PM -0400, Daniel Colascione wrote:
> Some NVMe devices maintain a persistent log, the PEL, of events like
> power-on and thermal excursions. The NVMe Set Features (Timestamp)
> command allows an operating system to inform the NVMe of the current
> wall-clock time. Wall-clock timestamp updates are logged to the PEL
> alongside other events. By correlating PEL records, an attacker can
> infer a user's usage patterns and even guess at time zone changes.
How does an attacker come to acquire PEL records if the system isn't
already compromised?
> The nvme_core.timestamps_enabled_default module parameter supplies the
> default value of the per-controller flag. Default it to false as the
> privacy-preserving choice. Users who want to provide controllers with
> real-world time can set the module parameter to true or enable
> the per-controller sysfs flag, perhaps via udev.
>
> As an alternative, we could also get the timestamp updates out of the
> kernel entirely and have interested users run nvme(1) to
> update timestamps.
The use cases for the timestamp feature are outside the specification.
But I know of at least one implementation that uses it to determine how
long it has been powered off so it can better apply correction to media
drift. Yeah yeah, depending on the host for something so critical is
pretty fragile, but it apparently worked out well enough. This proposal
would break them.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] nvme: make sending wall-clock time to NVMe opt-in
2026-07-13 20:52 ` Keith Busch
@ 2026-07-13 21:05 ` Daniel Colascione
0 siblings, 0 replies; 3+ messages in thread
From: Daniel Colascione @ 2026-07-13 21:05 UTC (permalink / raw)
To: Keith Busch
Cc: Jens Axboe, Christoph Hellwig, Sagi Grimberg, linux-nvme,
linux-kernel
Keith Busch <kbusch@kernel.org> writes:
> On Mon, Jul 13, 2026 at 04:34:43PM -0400, Daniel Colascione wrote:
>> Some NVMe devices maintain a persistent log, the PEL, of events like
>> power-on and thermal excursions. The NVMe Set Features (Timestamp)
>> command allows an operating system to inform the NVMe of the current
>> wall-clock time. Wall-clock timestamp updates are logged to the PEL
>> alongside other events. By correlating PEL records, an attacker can
>> infer a user's usage patterns and even guess at time zone changes.
>
> How does an attacker come to acquire PEL records if the system isn't
> already compromised?
The PEL can be read without any authentication whatsoever, so even an
otherwise completely measured secure-boot system with FDE could leak the
log to an evil maid. A sophisticated evil maid could execute some kind
of adaptive attack, but the PEL is right there and easy to read even for
an unsophisticated one-shot adversary.
Another thing to emphasize is that the log persists *through*
cryptographic sanitize, so you can "wipe" a drive using the strongest
available NVMe command, hand it to someone else, and still unwittingly
leak real-world usage patterns. It's a side channel I'd rather not have,
especially if I'm not getting any value from the timestamp.
>> The nvme_core.timestamps_enabled_default module parameter supplies the
>> default value of the per-controller flag. Default it to false as the
>> privacy-preserving choice. Users who want to provide controllers with
>> real-world time can set the module parameter to true or enable
>> the per-controller sysfs flag, perhaps via udev.
>>
>> As an alternative, we could also get the timestamp updates out of the
>> kernel entirely and have interested users run nvme(1) to
>> update timestamps.
>
> The use cases for the timestamp feature are outside the specification.
> But I know of at least one implementation that uses it to determine how
> long it has been powered off so it can better apply correction to media
> drift. Yeah yeah, depending on the host for something so critical is
> pretty fragile, but it apparently worked out well enough. This proposal
> would break them.
We could make the timestamp-updating opt-out instead of opt-in or
require users of this hardware to change the module parameter. Also,
IIUC, nothing in the spec requires a host to use this command.
I mostly care about having *some* knob to turn this off. Right now, it's
unconditional. I'm happy with either opt-in or opt-out, although I have
a weak preference for the former.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-13 21:05 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13 20:34 [PATCH] nvme: make sending wall-clock time to NVMe opt-in Daniel Colascione
2026-07-13 20:52 ` Keith Busch
2026-07-13 21:05 ` Daniel Colascione
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox