From patchwork Mon Feb 22 09:41:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Catangiu, Adrian Costin" X-Patchwork-Id: 12098211 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DEEE0C4332B for ; Mon, 22 Feb 2021 09:44:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B48C464E44 for ; Mon, 22 Feb 2021 09:44:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230135AbhBVJnl (ORCPT ); Mon, 22 Feb 2021 04:43:41 -0500 Received: from smtp-fw-4101.amazon.com ([72.21.198.25]:41129 "EHLO smtp-fw-4101.amazon.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230053AbhBVJnI (ORCPT ); Mon, 22 Feb 2021 04:43:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.com; i=@amazon.com; q=dns/txt; s=amazon201209; t=1613986983; x=1645522983; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version; bh=GoFiHmkiRLqGpatl14iXFUQWsHeBd/6RoTe4MpnGs+M=; b=uIbCzHnfAnbgJUykkem4JnXQvFjGev2lboEQqYYySOI3/rd125dNMz01 tqdiZEGwTq1RV6+Fcgtm9F4b+hyeX50yeqBLrWKyC2YutEUH3Bxv7lPRm 00EGZCaP5l5k/EJFYtFqDedFJV0bz676i6N44VwcjoPCJHzSwx2+yMH5J o=; X-IronPort-AV: E=Sophos;i="5.81,196,1610409600"; d="scan'208";a="86462559" Received: from iad12-co-svc-p1-lb1-vlan2.amazon.com (HELO email-inbound-relay-2a-c5104f52.us-west-2.amazon.com) ([10.43.8.2]) by smtp-border-fw-out-4101.iad4.amazon.com with ESMTP; 22 Feb 2021 09:42:12 +0000 Received: from EX13D08EUB004.ant.amazon.com (pdx1-ws-svc-p6-lb9-vlan3.pdx.amazon.com [10.236.137.198]) by email-inbound-relay-2a-c5104f52.us-west-2.amazon.com (Postfix) with ESMTPS id 006C2A1E92; Mon, 22 Feb 2021 09:42:09 +0000 (UTC) Received: from uf6ed9c851f4556.ant.amazon.com (10.43.161.87) by EX13D08EUB004.ant.amazon.com (10.43.166.158) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Mon, 22 Feb 2021 09:41:55 +0000 From: Adrian Catangiu To: , , , , CC: , , , , , , <0x7f454c46@gmail.com>, , , , , , , , , , , , , , , , , , , , , , , , , , , , , Subject: [PATCH v6 1/2] drivers/misc: sysgenid: add system generation id driver Date: Mon, 22 Feb 2021 11:41:25 +0200 Message-ID: <1613986886-29493-2-git-send-email-acatan@amazon.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1613986886-29493-1-git-send-email-acatan@amazon.com> References: <1613986886-29493-1-git-send-email-acatan@amazon.com> MIME-Version: 1.0 X-Originating-IP: [10.43.161.87] X-ClientProxiedBy: EX13D41UWC004.ant.amazon.com (10.43.162.31) To EX13D08EUB004.ant.amazon.com (10.43.166.158) Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org - Background and problem The System Generation ID feature is required in virtualized or containerized environments by applications that work with local copies or caches of world-unique data such as random values, uuids, monotonically increasing counters, etc. Such applications can be negatively affected by VM or container snapshotting when the VM or container is either cloned or returned to an earlier point in time. Furthermore, simply finding out about a system generation change is only the starting point of a process to renew internal states of possibly multiple applications across the system. This process requires a standard interface that applications can rely on and through which orchestration can be easily done. - Solution The System Generation ID is meant to help in these scenarios by providing a monotonically increasing u32 counter that changes each time the VM or container is restored from a snapshot. The `sysgenid` driver exposes a monotonic incremental System Generation u32 counter via a char-dev filesystem interface accessible through `/dev/sysgenid`. It provides synchronous and asynchronous SysGen counter update notifications, as well as counter retrieval and confirmation mechanisms. The counter starts from zero when the driver is initialized and monotonically increments every time the system generation changes. Userspace applications or libraries can (a)synchronously consume the system generation counter through the provided filesystem interface, to make any necessary internal adjustments following a system generation update. The provided filesystem interface operations can be used to build a system level safe workflow that guest software can follow to protect itself from negative system snapshot effects. The `sysgenid` driver exports the `void sysgenid_bump_generation()` symbol which can be used by backend drivers to drive system generation changes based on hardware events. System generation changes can also be driven by userspace software through a dedicated driver ioctl. The driver also generates a `SYSGENID=%u` uevent containing the new system generation counter/id value every time it changes. Unlike the filesystem interface, the uevent has no synchronization guarantees therefore it should not be used by any sensitive system components. **Please note**, SysGenID alone does not guarantee complete snapshot safety to applications using it. A certain workflow needs to be followed at the system level, in order to make the system snapshot-resilient. Please see the "Snapshot Safety Prerequisites" section in the included documentation. Signed-off-by: Adrian Catangiu --- Documentation/misc-devices/sysgenid.rst | 234 ++++++++++++++ Documentation/userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 8 + drivers/misc/Kconfig | 15 + drivers/misc/Makefile | 1 + drivers/misc/sysgenid.c | 338 +++++++++++++++++++++ include/uapi/linux/sysgenid.h | 18 ++ 7 files changed, 615 insertions(+) create mode 100644 Documentation/misc-devices/sysgenid.rst create mode 100644 drivers/misc/sysgenid.c create mode 100644 include/uapi/linux/sysgenid.h diff --git a/Documentation/misc-devices/sysgenid.rst b/Documentation/misc-devices/sysgenid.rst new file mode 100644 index 0000000..4c24559 --- /dev/null +++ b/Documentation/misc-devices/sysgenid.rst @@ -0,0 +1,234 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======== +SYSGENID +======== + +The System Generation ID feature is required in virtualized or +containerized environments by applications that work with local copies +or caches of world-unique data such as random values, UUIDs, +monotonically increasing counters, etc. +Such applications can be negatively affected by VM or container +snapshotting when the VM or container is either cloned or returned to +an earlier point in time. + +The System Generation ID is meant to help in these scenarios by +providing a monotonically increasing counter that changes each time the +VM or container is restored from a snapshot. The driver for it lives at +``drivers/misc/sysgenid.c``. + +The ``sysgenid`` driver exposes a monotonic incremental System +Generation u32 counter via a char-dev filesystem interface accessible +through ``/dev/sysgenid`` that provides sync and async SysGen counter +update notifications. It also provides SysGen counter retrieval and +confirmation mechanisms. + +The counter starts from zero when the driver is initialized and +monotonically increments every time the system generation changes. + +The ``sysgenid`` driver exports the ``void sysgenid_bump_generation()`` +symbol which can be used by backend drivers to drive system generation +changes based on hardware events. +System generation changes can also be driven by userspace software +through a dedicated driver ioctl. + +Userspace applications or libraries can (a)synchronously consume the +system generation counter through the provided filesystem interface, to +make any necessary internal adjustments following a system generation +update. + +The driver also generates a ``SYSGENID=%u`` uevent containing the new +system generation counter/id value every time it changes. Unlike the +filesystem interface, the uevent has no synchronization guarantees +therefore it should not be used by any sensitive system components. + +**Please note**, SysGenID alone does not guarantee complete snapshot +safety to applications using it. A certain workflow needs to be +followed at the system level, in order to make the system +snapshot-resilient. Please see the "Snapshot Safety Prerequisites" +section below. + +Driver filesystem interface +=========================== + +``open()``: + When the device is opened, a copy of the current SysGenID (counter) + is associated with the open file descriptor. Every open file + descriptor will have readable data available (EPOLLIN) while its + current copy of the SysGenID is outdated. Reading from the fd will + provide the latest SysGenID, while writing to the fd will update the + fd-local copy of the SysGenID and is used as a confirmation + mechanism. + +``read()``: + Read is meant to provide the *new* system generation counter when a + generation change takes place. The read operation blocks until the + associated counter is no longer up to date, at which point the new + counter is provided/returned. Nonblocking ``read()`` returns + ``EAGAIN`` to signal that there is no *new* counter value available. + The generation counter is considered *new* for each open file + descriptor that hasn't confirmed the new value following a generation + change. Therefore, once a generation change takes place, all + ``read()`` calls will immediately return the new generation counter + and will continue to do so until the new value is confirmed back to + the driver through ``write()``. + Partial reads are not allowed - read buffer needs to be at least + 32 bits in size. + +``write()``: + Write is used to confirm the up-to-date SysGenID counter back to the + driver. + Following a VM generation change, all existing watchers are marked + as *outdated*. Each file descriptor will maintain the *outdated* + status until a ``write()`` containing the new up-to-date generation + counter is used as an update confirmation mechanism. + Partial writes are not allowed - write buffer should be exactly + 32 bits in size. + +``poll()``: + Poll is implemented to allow polling for generation counter updates. + Such updates result in ``EPOLLIN`` polling status until the new + up-to-date counter is confirmed back to the driver through a + ``write()``. + +``ioctl()``: + The driver also adds support for waiting on open file descriptors + that haven't acknowledged a generation counter update, as well as a + mechanism for userspace to *trigger* a generation update: + + - SYSGENID_SET_WATCHER_TRACKING: takes a bool argument to set tracking + status for current file descriptor. When watcher tracking is + enabled, the driver tracks this file descriptor as an independent + *watcher*. The driver keeps accounting of how many watchers have + confirmed the latest Sys-Gen-Id counter and how many of them are + *outdated*; an outdated watcher is a *tracked* open file descriptor + that has lived through a Sys-Gen-Id change but has not yet confirmed + the new generation counter. + Software that wants to be waited on by the system while it adjusts + to generation changes, should turn tracking on. The sysgenid driver + then keeps track of it and can block system-level adjustment process + until the software has finished adjusting and confirmed it through a + ``write()``. + Tracking is disabled by default and file descriptors need to + explicitly opt-in using this IOCTL. + - SYSGENID_WAIT_WATCHERS: blocks until there are no more *outdated* + tracked watchers or, if a ``timeout`` argument is provided, until + the timeout expires. + If the current caller is *outdated* or a generation change happens + while waiting (thus making current caller *outdated*), the ioctl + returns ``-EINTR`` to signal the user to handle event and retry. + - SYSGENID_TRIGGER_GEN_UPDATE: triggers a generation counter increment. + It takes a ``minimum-generation`` argument which represents the + minimum value the generation counter will be set to. For example if + current generation is ``5`` and ``SYSGENID_TRIGGER_GEN_UPDATE(8)`` + is called, the generation counter will increment to ``8``. + This IOCTL can only be used by processes with CAP_CHECKPOINT_RESTORE + or CAP_SYS_ADMIN capabilities. + +``mmap()``: + The driver supports ``PROT_READ, MAP_SHARED`` mmaps of a single page + in size. The first 4 bytes of the mapped page will contain an + up-to-date u32 copy of the system generation counter. + The mapped memory can be used as a low-latency generation counter + probe mechanism in critical sections. + The mmap() interface is targeted at libraries or code that needs to + check for generation changes in-line, where an event loop is not + available or read()/write() syscalls are too expensive. + In such cases, logic can be added in-line with the sensitive code to + check and trigger on-demand/just-in-time readjustments when changes + are detected on the memory mapped generation counter. + Users of this interface that plan to lazily adjust should not enable + watcher tracking, since waiting on them doesn't make sense. + +``close()``: + Removes the file descriptor as a system generation counter *watcher*. + +Snapshot Safety Prerequisites +============================= + +If VM, container or other system-level snapshots happen asynchronously, +at arbitrary times during an active workload there is no practical way +to ensure that in-flight local copies or caches of world-unique data +such as random values, secrets, UUIDs, etc are properly scrubbed and +regenerated. +The challenge stems from the fact that the categorization of data as +snapshot-sensitive is only known to the software working with it, and +this software has no logical control over the moment in time when an +external system snapshot occurs. + +Let's take an OpenSSL session token for example. Even if the library +code is made 100% snapshot-safe, meaning the library guarantees that +the session token is unique (any snapshot that happened during the +library call did not duplicate or leak the token), the token is still +vulnerable to snapshot events while it transits the various layers of +the library caller, then the various layers of the OS before leaving +the system. + +To catch a secret while it's in-flight, we'd have to validate system +generation at every layer, every step of the way. Even if that would +be deemed the right solution, it would be a long road and a whole +universe to patch before we get there. + +Bottom line is we don't have a way to track all of these in-flight +secrets and dynamically scrub them from existence with snapshot +events happening arbitrarily. + +Simplifyng assumption - safety prerequisite +------------------------------------------- + +**Control the snapshot flow**, disallow snapshots coming at arbitrary +moments in the workload lifetime. + +Use a system-level overseer entity that quiesces the system before +snapshot, and post-snapshot-resume oversees that software components +have readjusted to new environment, to the new generation. Only after, +will the overseer un-quiesce the system and allow active workloads. + +Software components can choose whether they want to be tracked and +waited on by the overseer by using the ``SYSGENID_SET_WATCHER_TRACKING`` +IOCTL. + +The sysgenid framework standardizes the API for system software to +find out about needing to readjust and at the same time provides a +mechanism for the overseer entity to wait for everyone to be done, the +system to have readjusted, so it can un-quiesce. + +Example snapshot-safe workflow +------------------------------ + +1) Before taking a snapshot, quiesce the VM/container/system. Exactly + how this is achieved is very workload-specific, but the general + description is to get all software to an expected state where their + event loops dry up and they are effectively quiesced. +2) Take snapshot. +3) Resume the VM/container/system from said snapshot. +4) SysGenID counter will either automatically increment if there is + a vmgenid backend (hw-driven), or overseer will trigger generation + bump using ``SYSGENID_TRIGGER_GEN_UPDATE`` IOCLT (sw-driven). +5) Software components which have ``/dev/sysgenid`` in their event + loops (either using ``poll()`` or ``read()``) are notified of the + generation change. + They do their specific internal adjustments. Some may have requested + to be tracked and waited on by the overseer, others might choose to + do their adjustments out of band and not block the overseer. + Tracked ones *must* signal when they are done/ready with a ``write()`` + while the rest *should* also do so for cleanliness, but it's not + mandatory. +6) Overseer will block and wait for all tracked watchers by using the + ``SYSGENID_WAIT_WATCHERS`` IOCTL. Once all tracked watchers are done + in step 5, this overseer will return from this blocking ioctl knowing + that the system has readjusted and is ready for active workload. +7) Overseer un-quiesces system. +8) There is a class of software, usually libraries, most notably PRNGs + or SSLs, that don't fit the event-loop model and also have strict + latency requirements. These can take advantage of the ``mmap()`` + interface and lazily adjust on-demand whenever they are called after + un-quiesce. + For a well-designed service stack, these libraries should not be + called while system is quiesced. When workload is resumed by the + overseer, on the first call into these libs, they will safely JIT + readjust. + Users of this lazy on-demand readjustment model should not enable + watcher tracking since doing so would introduce a logical deadlock: + lazy adjustments happen only after un-quiesce, but un-quiesce is + blocked until all tracked watchers are up-to-date. diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index a4c75a2..d31702c 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -356,6 +356,7 @@ Code Seq# Include File Comments 0xDB 00-0F drivers/char/mwave/mwavepub.h 0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ +0xE4 01-03 uapi/linux/sysgenid.h SysGenID misc driver 0xE5 00-3F linux/fuse.h 0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) diff --git a/MAINTAINERS b/MAINTAINERS index bfc1b86..9ae62d5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17203,6 +17203,14 @@ L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/sdhci-pci-dwc-mshc.c +SYSGENID +M: Adrian Catangiu +L: linux-kernel@vger.kernel.org +S: Supported +F: Documentation/misc-devices/sysgenid.rst +F: drivers/misc/sysgenid.c +F: include/uapi/linux/sysgenid.h + SYSTEM CONFIGURATION (SYSCON) M: Lee Jones M: Arnd Bergmann diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index fafa8b0..a2b7cae 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -456,6 +456,21 @@ config PVPANIC a paravirtualized device provided by QEMU; it lets a virtual machine (guest) communicate panic events to the host. +config SYSGENID + tristate "System Generation ID driver" + help + This is a System Generation ID driver which provides a system + generation counter. The driver exposes FS ops on /dev/sysgenid + through which it can provide information and notifications on system + generation changes that happen because of VM or container snapshots + or cloning. + This enables applications and libraries that store or cache + sensitive information, to know that they need to regenerate it + after process memory has been exposed to potential copying. + + To compile this driver as a module, choose M here: the + module will be called sysgenid. + config HISI_HIKEY_USB tristate "USB GPIO Hub on HiSilicon Hikey 960/970 Platform" depends on (OF && GPIOLIB) || COMPILE_TEST diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d23231e..4b4933d 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI) += habanalabs/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o +obj-$(CONFIG_SYSGENID) += sysgenid.o diff --git a/drivers/misc/sysgenid.c b/drivers/misc/sysgenid.c new file mode 100644 index 0000000..a8f6d0a --- /dev/null +++ b/drivers/misc/sysgenid.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Generation ID driver + * + * Copyright (C) 2020 Amazon. All rights reserved. + * + * Authors: + * Adrian Catangiu + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sysgenid_data { + unsigned long map_buf; + wait_queue_head_t read_waitq; + atomic_t generation_counter; + + unsigned int watchers; + atomic_t outdated_watchers; + wait_queue_head_t outdated_waitq; + spinlock_t lock; +}; +static struct sysgenid_data sysgenid_data; + +struct file_data { + bool tracked_watcher; + int acked_gen_counter; +}; + +static void sysgenid_uevent(unsigned int gen_counter); + +static int equals_gen_counter(unsigned int counter) +{ + return counter == atomic_read(&sysgenid_data.generation_counter); +} + +static void _bump_generation(int min_gen) +{ + unsigned long flags; + int counter; + + spin_lock_irqsave(&sysgenid_data.lock, flags); + counter = max(min_gen, 1 + atomic_read(&sysgenid_data.generation_counter)); + atomic_set(&sysgenid_data.generation_counter, counter); + *((int *) sysgenid_data.map_buf) = counter; + atomic_set(&sysgenid_data.outdated_watchers, sysgenid_data.watchers); + + wake_up_interruptible(&sysgenid_data.read_waitq); + wake_up_interruptible(&sysgenid_data.outdated_waitq); + spin_unlock_irqrestore(&sysgenid_data.lock, flags); + + sysgenid_uevent(counter); +} + +void sysgenid_bump_generation(void) +{ + _bump_generation(0); +} +EXPORT_SYMBOL_GPL(sysgenid_bump_generation); + +static void put_outdated_watchers(void) +{ + if (atomic_dec_and_test(&sysgenid_data.outdated_watchers)) + wake_up_interruptible(&sysgenid_data.outdated_waitq); +} + +static void start_fd_tracking(struct file_data *fdata) +{ + unsigned long flags; + + if (!fdata->tracked_watcher) { + /* enable tracking this fd as a watcher */ + spin_lock_irqsave(&sysgenid_data.lock, flags); + fdata->tracked_watcher = 1; + ++sysgenid_data.watchers; + if (!equals_gen_counter(fdata->acked_gen_counter)) + atomic_inc(&sysgenid_data.outdated_watchers); + spin_unlock_irqrestore(&sysgenid_data.lock, flags); + } +} + +static void stop_fd_tracking(struct file_data *fdata) +{ + unsigned long flags; + + if (fdata->tracked_watcher) { + /* stop tracking this fd as a watcher */ + spin_lock_irqsave(&sysgenid_data.lock, flags); + if (!equals_gen_counter(fdata->acked_gen_counter)) + put_outdated_watchers(); + --sysgenid_data.watchers; + fdata->tracked_watcher = 0; + spin_unlock_irqrestore(&sysgenid_data.lock, flags); + } +} + +static int sysgenid_open(struct inode *inode, struct file *file) +{ + struct file_data *fdata = kzalloc(sizeof(struct file_data), GFP_KERNEL); + + if (!fdata) + return -ENOMEM; + fdata->tracked_watcher = 0; + fdata->acked_gen_counter = atomic_read(&sysgenid_data.generation_counter); + file->private_data = fdata; + + return 0; +} + +static int sysgenid_close(struct inode *inode, struct file *file) +{ + struct file_data *fdata = file->private_data; + + stop_fd_tracking(fdata); + kfree(fdata); + + return 0; +} + +static ssize_t sysgenid_read(struct file *file, char __user *ubuf, + size_t nbytes, loff_t *ppos) +{ + struct file_data *fdata = file->private_data; + ssize_t ret; + int gen_counter; + + if (nbytes == 0) + return 0; + /* disallow partial reads */ + if (nbytes < sizeof(gen_counter)) + return -EINVAL; + + if (equals_gen_counter(fdata->acked_gen_counter)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible( + sysgenid_data.read_waitq, + !equals_gen_counter(fdata->acked_gen_counter) + ); + if (ret) + return ret; + } + + gen_counter = atomic_read(&sysgenid_data.generation_counter); + ret = copy_to_user(ubuf, &gen_counter, sizeof(gen_counter)); + if (ret) + return -EFAULT; + + return sizeof(gen_counter); +} + +static ssize_t sysgenid_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct file_data *fdata = file->private_data; + unsigned int new_acked_gen; + unsigned long flags; + + /* disallow partial writes */ + if (count != sizeof(new_acked_gen)) + return -ENOBUFS; + if (copy_from_user(&new_acked_gen, ubuf, count)) + return -EFAULT; + + spin_lock_irqsave(&sysgenid_data.lock, flags); + /* wrong gen-counter acknowledged */ + if (!equals_gen_counter(new_acked_gen)) { + spin_unlock_irqrestore(&sysgenid_data.lock, flags); + return -EINVAL; + } + /* update acked gen-counter if necessary */ + if (!equals_gen_counter(fdata->acked_gen_counter)) { + fdata->acked_gen_counter = new_acked_gen; + if (fdata->tracked_watcher) + put_outdated_watchers(); + } + spin_unlock_irqrestore(&sysgenid_data.lock, flags); + + return (ssize_t)count; +} + +static __poll_t sysgenid_poll(struct file *file, poll_table *wait) +{ + __poll_t mask = 0; + struct file_data *fdata = file->private_data; + + if (!equals_gen_counter(fdata->acked_gen_counter)) + return EPOLLIN | EPOLLRDNORM; + + poll_wait(file, &sysgenid_data.read_waitq, wait); + + if (!equals_gen_counter(fdata->acked_gen_counter)) + mask = EPOLLIN | EPOLLRDNORM; + + return mask; +} + +static long sysgenid_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct file_data *fdata = file->private_data; + bool tracking = !!arg; + unsigned long timeout_ns, min_gen; + ktime_t until; + int ret = 0; + + switch (cmd) { + case SYSGENID_SET_WATCHER_TRACKING: + if (tracking) + start_fd_tracking(fdata); + else + stop_fd_tracking(fdata); + break; + case SYSGENID_WAIT_WATCHERS: + timeout_ns = arg * NSEC_PER_MSEC; + until = timeout_ns ? ktime_set(0, timeout_ns) : KTIME_MAX; + + ret = wait_event_interruptible_hrtimeout( + sysgenid_data.outdated_waitq, + (!atomic_read(&sysgenid_data.outdated_watchers) || + !equals_gen_counter(fdata->acked_gen_counter)), + until + ); + if (!equals_gen_counter(fdata->acked_gen_counter)) + ret = -EINTR; + break; + case SYSGENID_TRIGGER_GEN_UPDATE: + if (!checkpoint_restore_ns_capable(current_user_ns())) + return -EACCES; + min_gen = arg; + _bump_generation(min_gen); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int sysgenid_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct file_data *fdata = file->private_data; + + if (vma->vm_pgoff != 0 || vma_pages(vma) > 1) + return -EINVAL; + + if ((vma->vm_flags & VM_WRITE) != 0) + return -EPERM; + + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_flags &= ~VM_MAYWRITE; + vma->vm_private_data = fdata; + + return vm_insert_page(vma, vma->vm_start, + virt_to_page(sysgenid_data.map_buf)); +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .mmap = sysgenid_mmap, + .open = sysgenid_open, + .release = sysgenid_close, + .read = sysgenid_read, + .write = sysgenid_write, + .poll = sysgenid_poll, + .unlocked_ioctl = sysgenid_ioctl, +}; + +static struct miscdevice sysgenid_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sysgenid", + .fops = &fops, +}; + +static void sysgenid_uevent(unsigned int gen_counter) +{ + char event_string[22]; + char *envp[2] = { event_string, NULL }; + struct device *dev = sysgenid_misc.this_device; + + if (dev) { + sprintf(event_string, "SYSGENID=%u", gen_counter); + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); + } +} + +static int __init sysgenid_init(void) +{ + int ret; + + sysgenid_data.map_buf = get_zeroed_page(GFP_KERNEL); + if (!sysgenid_data.map_buf) + return -ENOMEM; + + atomic_set(&sysgenid_data.generation_counter, 0); + atomic_set(&sysgenid_data.outdated_watchers, 0); + init_waitqueue_head(&sysgenid_data.read_waitq); + init_waitqueue_head(&sysgenid_data.outdated_waitq); + spin_lock_init(&sysgenid_data.lock); + + ret = misc_register(&sysgenid_misc); + if (ret < 0) { + pr_err("misc_register() failed for sysgenid\n"); + goto err; + } + + return 0; + +err: + free_pages(sysgenid_data.map_buf, 0); + sysgenid_data.map_buf = 0; + + return ret; +} + +static void __exit sysgenid_exit(void) +{ + misc_deregister(&sysgenid_misc); + free_pages(sysgenid_data.map_buf, 0); + sysgenid_data.map_buf = 0; +} + +module_init(sysgenid_init); +module_exit(sysgenid_exit); + +MODULE_AUTHOR("Adrian Catangiu"); +MODULE_DESCRIPTION("System Generation ID"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); diff --git a/include/uapi/linux/sysgenid.h b/include/uapi/linux/sysgenid.h new file mode 100644 index 0000000..7279df6 --- /dev/null +++ b/include/uapi/linux/sysgenid.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ + +#ifndef _UAPI_LINUX_SYSGENID_H +#define _UAPI_LINUX_SYSGENID_H + +#include + +#define SYSGENID_IOCTL 0xE4 +#define SYSGENID_SET_WATCHER_TRACKING _IO(SYSGENID_IOCTL, 1) +#define SYSGENID_WAIT_WATCHERS _IO(SYSGENID_IOCTL, 2) +#define SYSGENID_TRIGGER_GEN_UPDATE _IO(SYSGENID_IOCTL, 3) + +#ifdef __KERNEL__ +void sysgenid_bump_generation(void); +#endif /* __KERNEL__ */ + +#endif /* _UAPI_LINUX_SYSGENID_H */ + From patchwork Mon Feb 22 09:41:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Catangiu, Adrian Costin" X-Patchwork-Id: 12098209 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0F1DFC4332D for ; Mon, 22 Feb 2021 09:44:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D6C3964E2F for ; Mon, 22 Feb 2021 09:44:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230295AbhBVJns (ORCPT ); Mon, 22 Feb 2021 04:43:48 -0500 Received: from smtp-fw-4101.amazon.com ([72.21.198.25]:41304 "EHLO smtp-fw-4101.amazon.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230044AbhBVJnO (ORCPT ); Mon, 22 Feb 2021 04:43:14 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.com; i=@amazon.com; q=dns/txt; s=amazon201209; t=1613986991; x=1645522991; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version; bh=8UTpe6qmQnTzNV2cZ919RiLXlAwB3jtqeyIcXPKbGLs=; b=pQmdo1BbiIvrz+m8kHPFZR4/9901sW6FLCvyT+98hBHoEoPC9i2cS+om jnIqmE8W3vzu2nz5AIdjI+uPbwDKUgfmuFeC6F7EqYYzWpJiXsJuyUl0O 1i0CVQogfWFGHny4NRq0g3kuptrVKk1DXo0ZsHWbFW5PhjF42f9if18/T E=; X-IronPort-AV: E=Sophos;i="5.81,196,1610409600"; d="scan'208";a="86462655" Received: from iad12-co-svc-p1-lb1-vlan2.amazon.com (HELO email-inbound-relay-2b-baacba05.us-west-2.amazon.com) ([10.43.8.2]) by smtp-border-fw-out-4101.iad4.amazon.com with ESMTP; 22 Feb 2021 09:42:27 +0000 Received: from EX13D08EUB004.ant.amazon.com (pdx1-ws-svc-p6-lb9-vlan2.pdx.amazon.com [10.236.137.194]) by email-inbound-relay-2b-baacba05.us-west-2.amazon.com (Postfix) with ESMTPS id AB903A1D25; Mon, 22 Feb 2021 09:42:23 +0000 (UTC) Received: from uf6ed9c851f4556.ant.amazon.com (10.43.161.87) by EX13D08EUB004.ant.amazon.com (10.43.166.158) with Microsoft SMTP Server (TLS) id 15.0.1497.2; Mon, 22 Feb 2021 09:42:08 +0000 From: Adrian Catangiu To: , , , , CC: , , , , , , <0x7f454c46@gmail.com>, , , , , , , , , , , , , , , , , , , , , , , , , , , , , Subject: [PATCH v6 2/2] drivers/virt: vmgenid: add vm generation id driver Date: Mon, 22 Feb 2021 11:41:26 +0200 Message-ID: <1613986886-29493-3-git-send-email-acatan@amazon.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1613986886-29493-1-git-send-email-acatan@amazon.com> References: <1613986886-29493-1-git-send-email-acatan@amazon.com> MIME-Version: 1.0 X-Originating-IP: [10.43.161.87] X-ClientProxiedBy: EX13D41UWC004.ant.amazon.com (10.43.162.31) To EX13D08EUB004.ant.amazon.com (10.43.166.158) Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org The VM Generation ID is a feature defined by Microsoft (paper: http://go.microsoft.com/fwlink/?LinkId=260709) and supported by multiple hypervisor vendors. The feature can be used to drive the `sysgenid` mechanism required in virtualized environments by software that works with local copies and caches of world-unique data such as random values, uuids, monotonically increasing counters, etc. The VM Generation ID is a hypervisor/hardware provided 128-bit unique ID that changes each time the VM is restored from a snapshot. It can be used to differentiate between VMs or different generations of the same VM. This VM Generation ID is exposed through an ACPI device by multiple hypervisor vendors. The `vmgenid` driver acts as a backend for the `sysgenid` kernel module (`drivers/misc/sysgenid.c`, `Documentation/misc-devices/sysgenid.rst`) to drive changes to the "System Generation Id" which is further exposed to userspace as a monotonically increasing counter. The driver uses ACPI events to be notified by hardware of changes to the 128-bit Vm Gen Id UUID. Since the actual UUID value is not directly exposed to userspace, but only used to drive the System Generation Counter, the driver also adds it as device randomness to improve kernel entropy following VM snapshot events. This patch builds on top of Or Idgar 's proposal https://lkml.org/lkml/2018/3/1/498 Signed-off-by: Adrian Catangiu --- Documentation/virt/vmgenid.rst | 36 ++++++++++ MAINTAINERS | 7 ++ drivers/virt/Kconfig | 13 ++++ drivers/virt/Makefile | 1 + drivers/virt/vmgenid.c | 153 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 210 insertions(+) create mode 100644 Documentation/virt/vmgenid.rst create mode 100644 drivers/virt/vmgenid.c diff --git a/Documentation/virt/vmgenid.rst b/Documentation/virt/vmgenid.rst new file mode 100644 index 0000000..a429c2a3 --- /dev/null +++ b/Documentation/virt/vmgenid.rst @@ -0,0 +1,36 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======= +VMGENID +======= + +The VM Generation ID is a feature defined by Microsoft (paper: +http://go.microsoft.com/fwlink/?LinkId=260709) and supported by +multiple hypervisor vendors. + +The feature is required in virtualized environments by applications +that work with local copies/caches of world-unique data such as random +values, UUIDs, monotonically increasing counters, etc. +Such applications can be negatively affected by VM snapshotting when +the VM is either cloned or returned to an earlier point in time. + +The VM Generation ID is a simple concept through which a hypevisor +notifies its guest that a snapshot has taken place. The vmgenid device +provides a unique ID that changes each time the VM is restored from a +snapshot. The hardware provided UUID value can be used to differentiate +between VMs or different generations of the same VM. + +The VM Generation ID is exposed through an ACPI device by multiple +hypervisor vendors. The driver for it lives at +``drivers/virt/vmgenid.c`` + +The ``vmgenid`` driver acts as a backend for the ``sysgenid`` kernel module +(``drivers/misc/sysgenid.c``, ``Documentation/misc-devices/sysgenid.rst``) +to drive changes to the "System Generation Id" which is further exposed +to userspace as a monotonically increasing counter. + +The driver uses ACPI events to be notified by hardware of changes to the +128-bit Vm Gen Id UUID. Since the actual UUID value is not directly exposed +to userspace, but only used to drive the System Generation Counter, the +driver also adds it as device randomness to improve kernel entropy +following VM snapshot events. diff --git a/MAINTAINERS b/MAINTAINERS index 9ae62d5..e8b182d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19009,6 +19009,13 @@ F: drivers/staging/vme/ F: drivers/vme/ F: include/linux/vme* +VMGENID +M: Adrian Catangiu +L: linux-kernel@vger.kernel.org +S: Supported +F: Documentation/virt/vmgenid.rst +F: drivers/virt/vmgenid.c + VMWARE BALLOON DRIVER M: Nadav Amit M: "VMware, Inc." diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 80c5f9c1..95d82c9 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -13,6 +13,19 @@ menuconfig VIRT_DRIVERS if VIRT_DRIVERS +config VMGENID + tristate "Virtual Machine Generation ID driver" + depends on ACPI && SYSGENID + help + The driver uses the hypervisor provided Virtual Machine Generation ID + to drive the system generation counter mechanism exposed by sysgenid. + The vmgenid changes on VM snapshots or VM cloning. The hypervisor + provided 128-bit vmgenid is also used as device randomness to improve + kernel entropy following VM snapshot events. + + To compile this driver as a module, choose M here: the + module will be called vmgenid. + config FSL_HV_MANAGER tristate "Freescale hypervisor management driver" depends on FSL_SOC diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index f28425c..889be01 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_FSL_HV_MANAGER) += fsl_hypervisor.o +obj-$(CONFIG_VMGENID) += vmgenid.o obj-y += vboxguest/ obj-$(CONFIG_NITRO_ENCLAVES) += nitro_enclaves/ diff --git a/drivers/virt/vmgenid.c b/drivers/virt/vmgenid.c new file mode 100644 index 0000000..d9d089a --- /dev/null +++ b/drivers/virt/vmgenid.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Virtual Machine Generation ID driver + * + * Copyright (C) 2018 Red Hat Inc. All rights reserved. + * + * Copyright (C) 2020 Amazon. All rights reserved. + * + * Authors: + * Adrian Catangiu + * Or Idgar + * Gal Hammer + * + */ +#include +#include +#include +#include +#include +#include + +#define DEV_NAME "vmgenid" +ACPI_MODULE_NAME(DEV_NAME); + +struct vmgenid_data { + uuid_t uuid; + void *uuid_iomap; +}; +static struct vmgenid_data vmgenid_data; + +static int vmgenid_acpi_map(struct vmgenid_data *priv, acpi_handle handle) +{ + int i; + phys_addr_t phys_addr; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + union acpi_object *pss; + union acpi_object *element; + + status = acpi_evaluate_object(handle, "ADDR", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating ADDR")); + return -ENODEV; + } + pss = buffer.pointer; + if (!pss || pss->type != ACPI_TYPE_PACKAGE || pss->package.count != 2) + return -EINVAL; + + phys_addr = 0; + for (i = 0; i < pss->package.count; i++) { + element = &(pss->package.elements[i]); + if (element->type != ACPI_TYPE_INTEGER) + return -EINVAL; + phys_addr |= element->integer.value << i * 32; + } + + priv->uuid_iomap = acpi_os_map_memory(phys_addr, sizeof(uuid_t)); + if (!priv->uuid_iomap) { + pr_err("Could not map memory at 0x%llx, size %u\n", + phys_addr, + (u32) sizeof(uuid_t)); + return -ENOMEM; + } + + memcpy_fromio(&priv->uuid, priv->uuid_iomap, sizeof(uuid_t)); + + return 0; +} + +static int vmgenid_acpi_add(struct acpi_device *device) +{ + int ret; + + if (!device) + return -EINVAL; + device->driver_data = &vmgenid_data; + + ret = vmgenid_acpi_map(device->driver_data, device->handle); + if (ret < 0) { + pr_err("vmgenid: failed to map acpi device\n"); + device->driver_data = NULL; + } + + return ret; +} + +static int vmgenid_acpi_remove(struct acpi_device *device) +{ + if (!device || acpi_driver_data(device) != &vmgenid_data) + return -EINVAL; + device->driver_data = NULL; + + if (vmgenid_data.uuid_iomap) + acpi_os_unmap_memory(vmgenid_data.uuid_iomap, sizeof(uuid_t)); + vmgenid_data.uuid_iomap = NULL; + + return 0; +} + +static void vmgenid_acpi_notify(struct acpi_device *device, u32 event) +{ + uuid_t old_uuid; + + if (!device || acpi_driver_data(device) != &vmgenid_data) { + pr_err("VMGENID notify with unexpected driver private data\n"); + return; + } + + /* update VM Generation UUID */ + old_uuid = vmgenid_data.uuid; + memcpy_fromio(&vmgenid_data.uuid, vmgenid_data.uuid_iomap, sizeof(uuid_t)); + + if (memcmp(&old_uuid, &vmgenid_data.uuid, sizeof(uuid_t))) { + /* HW uuid updated */ + sysgenid_bump_generation(); + add_device_randomness(&vmgenid_data.uuid, sizeof(uuid_t)); + } +} + +static const struct acpi_device_id vmgenid_ids[] = { + {"VMGENID", 0}, + {"QEMUVGID", 0}, + {"", 0}, +}; + +static struct acpi_driver acpi_vmgenid_driver = { + .name = "vm_generation_id", + .ids = vmgenid_ids, + .owner = THIS_MODULE, + .ops = { + .add = vmgenid_acpi_add, + .remove = vmgenid_acpi_remove, + .notify = vmgenid_acpi_notify, + } +}; + +static int __init vmgenid_init(void) +{ + return acpi_bus_register_driver(&acpi_vmgenid_driver); +} + +static void __exit vmgenid_exit(void) +{ + acpi_bus_unregister_driver(&acpi_vmgenid_driver); +} + +module_init(vmgenid_init); +module_exit(vmgenid_exit); + +MODULE_AUTHOR("Adrian Catangiu"); +MODULE_DESCRIPTION("Virtual Machine Generation ID"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1");