From nobody Fri Dec 19 02:14:10 2025 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C8DEA4595B for ; Sun, 8 Sep 2024 04:57:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771474; cv=none; b=Hr5QC/48Y08EOl14HvLJ+zMogKA9U/F9fk78koiShMeLMvJpLUFmUb4Dql+f7PlGuH9VBIcSmlmZSfOhLFJae1kIyHwqrwiM+Ccb4lL37Lx9d7O6VMhJsmv9O0QK4Sk65ZbUgJSbhH/Ek44nkwBpVagpo+zth7F5T/jXFOezO88= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1725771474; c=relaxed/simple; bh=g5TC1j1p9H5K2ejsFtwRR8SX6cZRBTzpKxHuFtvAmkY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cMYVDlKYMf7g5f67PsV/m88/nPNW6DKY9DL3+z/UdCvG0iBOJjtGXIYJ7dlkqmc/7Ru3ppmlvjF6UGN7jvM4qrGA+ZK08AfkpvMx9GRMudETb0gMiiCYrrtMut93YwkvlEIUJxzSI1TpPiJzuZlOx9DRj1Lo+0vzQXBKAFdfqBY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=jobAqU28; arc=none smtp.client-ip=192.198.163.9 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="jobAqU28" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1725771473; x=1757307473; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=g5TC1j1p9H5K2ejsFtwRR8SX6cZRBTzpKxHuFtvAmkY=; b=jobAqU28Mix9be4x5J3EP+haJPWvM0VA74yFR1rTY1wFgI8I61CKXrD6 lcy0kruE4nX2ZjbX7FscNhtbRzjQNTr8ZTQfUZyOuTYDH41GGZh3QEejN yF7gsl/d6qSkaBw4Qxm0tTw/Wzgf6S/WukFfcD5QbSg1E+YhXTMHWpiar E251s3BQG2nf5STh4447CTtgZ2wEORLyAu40P2JfkK9zMTiMDk7OmDWhB 2goCMlpfXOm5W7EcVfmWW+wD1yxKu+eUpqinHDp8Mc77WpKJ+fVOZDX3s yvrCgaDVGN/Y6Pl/Ml9jY+7auWfFXL/rxNrPW+ubz4dKdgtgkezz+1h2o Q==; X-CSE-ConnectionGUID: IjotRfg6SqeBf9T+s7nlfw== X-CSE-MsgGUID: iG5NM4mKQK6O+5xUUDPjdA== X-IronPort-AV: E=McAfee;i="6700,10204,11188"; a="35152172" X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="35152172" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:50 -0700 X-CSE-ConnectionGUID: b8UHpuTQR9qtokyKo+WlsQ== X-CSE-MsgGUID: E9V5oA57Riy5M9VW0v5ohA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,211,1719903600"; d="scan'208";a="70924660" Received: from ibarbour-mobl.amr.corp.intel.com (HELO bxing-mobl1.clients.intel.com) ([10.246.116.49]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Sep 2024 21:57:49 -0700 From: Cedric Xing Date: Sat, 07 Sep 2024 23:56:20 -0500 Subject: [PATCH RFC 2/3] tsm: Add RTMR event logging Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20240907-tsm-rtmr-v1-2-12fc4d43d4e7@intel.com> References: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> In-Reply-To: <20240907-tsm-rtmr-v1-0-12fc4d43d4e7@intel.com> To: Dan Williams , Samuel Ortiz , James Bottomley , Lukas Wunner , Dionna Amalie Glaze , Qinkun Bao , Mikko Ylinen , Kuppuswamy Sathyanarayanan Cc: linux-kernel@vger.kernel.org, linux-coco@lists.linux.dev X-Mailer: b4 0.13.0 An RTMR typically accumulates measurements from multiple software component= s, making its value alone less informative without an accompanying log. The log format design distinguishes between the meaning (semantics) of even= ts and their storage. - **Storage**: Specifies how to delineate and hash event records, allowing = the kernel to accurately maintain logs without interpreting their contents. - **Semantics**: The internal structure and meaning of records, defined by = the agreements between applications and verifiers, are not processed by the kernel. Event Log Format: - Records are lines ending with `\n`. - Each record (line) is hashed in its entirety (excluding the trailing `\n`) and extended to the RTMR. - The log for an RTMR is stored at `/sys/kernel/tsm///event_log` and consists of t= hese delineated records. - Lines that are empty (containing only `\n`) or start with `#` are skipped (not hashed or extended). This patch adds two more files to every RTMR directory: - `event_log`: A read-only file containing the full event log. - `append_log`: A write-only file for appending new event records to the lo= g. Records will be processed (hashed/extended) according to the format descr= ibed above. Multiple records can be appended in a single write, provided their total size doesn't exceed the size limit (PAGE_SIZE). Partial records are= not permitted - the last line written will always be treated as complete, eve= n if not terminated by `\n`. Special Event Records/Lines: - A line starting with `SYNC` captures the RTMR value prior to the inclusio= n of that line, providing verifiers with the starting value of the RTMR. This = line must be hashed/extended to prevent front-end log truncation. - A line beginning with `# .EXTEND` indicates a direct extension to the RTM= R by writing to `digest`. The remainder of the line specifies the value extend= ed. Direct extensions cause the log to become out of sync; therefore, a `SYNC` line will be automatically generated at the next `append_event` write. Signed-off-by: Cedric Xing --- drivers/virt/coco/tsm.c | 230 ++++++++++++++++++++++++++++++++++++++++++++= +--- 1 file changed, 219 insertions(+), 11 deletions(-) diff --git a/drivers/virt/coco/tsm.c b/drivers/virt/coco/tsm.c index e83143f22fad..28a10330912c 100644 --- a/drivers/virt/coco/tsm.c +++ b/drivers/virt/coco/tsm.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include =20 @@ -481,12 +482,14 @@ EXPORT_SYMBOL_GPL(tsm_unregister); =20 enum _rtmr_bin_attr_index { _RTMR_BATTR_DIGEST, + _RTMR_BATTR_LOG, _RTMR_BATTR__COUNT, }; =20 struct _rtmr { struct kobject kobj; struct bin_attribute battrs[_RTMR_BATTR__COUNT]; + bool log_in_sync; }; =20 struct _mr_provider { @@ -505,8 +508,40 @@ _rtmr_mr(const struct _rtmr *rtmr) .private; } =20 +static inline char *_rtmr_log(const struct _rtmr *rtmr) +{ + return (char *)rtmr->battrs[_RTMR_BATTR_LOG].private; +} + +static inline size_t _rtmr_log_size(const struct _rtmr *rtmr) +{ + return rtmr->battrs[_RTMR_BATTR_LOG].size; +} + +static inline void _rtmr_log_set_buf(struct _rtmr *rtmr, char *log) +{ + rtmr->battrs[_RTMR_BATTR_LOG].private =3D log; +} + +static inline void _rtmr_log_inc_size(struct _rtmr *rtmr, size_t size) +{ + rtmr->battrs[_RTMR_BATTR_LOG].size +=3D size; +} + +static inline int _rtmr_log_update_attribute(struct _rtmr *rtmr) +{ + struct bin_attribute *attrs_to_update[] =3D { + &rtmr->battrs[_RTMR_BATTR_LOG], + NULL, + }; + struct attribute_group agrp =3D { + .bin_attrs =3D attrs_to_update, + }; + return sysfs_update_group(&rtmr->kobj, &agrp); +} + static inline struct _mr_provider * -_mr_to_group(const struct tsm_measurement_register *mr, struct kobject *ko= bj) +_mr_to_provider(const struct tsm_measurement_register *mr, struct kobject = *kobj) { if (!(mr->mr_flags & TSM_MR_F_X)) return container_of(kobj, struct _mr_provider, kset.kobj); @@ -561,7 +596,7 @@ static ssize_t _mr_read(struct file *filp, struct kobje= ct *kobj, mr =3D (typeof(mr))attr->private; BUG_ON(mr->mr_size !=3D attr->size); =20 - pvd =3D _mr_to_group(mr, kobj); + pvd =3D _mr_to_provider(mr, kobj); rc =3D down_read_interruptible(&pvd->rwsem); if (rc) return rc; @@ -591,9 +626,66 @@ static ssize_t _mr_read(struct file *filp, struct kobj= ect *kobj, return rc ?: count; } =20 -static inline size_t snprint_hex(char *sbuf, size_t size, const u8 *data, +#define _EVENTLOG_GRANULARITY HPAGE_SIZE + +static ssize_t _log_extend_line(struct _rtmr *rtmr, const char *line, + const char *end, int newlines, + struct crypto_shash *tfm) +{ + struct _mr_provider *pvd; + pvd =3D container_of(rtmr->kobj.kset, typeof(*pvd), kset); + lockdep_assert_held_write(&pvd->rwsem); + + BUG_ON(line > end); + + while (line < end && isspace(line[0])) + ++line; + while (line < end && isspace(end[-1])) + --end; + if (line =3D=3D end) + return 0; + + ssize_t count =3D end - line; + char *log =3D _rtmr_log(rtmr); + size_t needed =3D _rtmr_log_size(rtmr) + count + newlines; + if (ksize(log) < needed) { + log =3D krealloc(log, + ALIGN(needed + _EVENTLOG_GRANULARITY / 2, + _EVENTLOG_GRANULARITY), + GFP_KERNEL); + if (!log) + return -ENOMEM; + + _rtmr_log_set_buf(rtmr, log); + } + + log +=3D _rtmr_log_size(rtmr); + for (int i =3D 0; i < newlines; ++i) + *log++ =3D '\n'; + + if (*line !=3D '#') { + u8 digest[SHA512_DIGEST_SIZE]; + BUG_ON(tfm =3D=3D NULL); + BUG_ON(sizeof(digest) < crypto_shash_digestsize(tfm)); + + int rc =3D crypto_shash_tfm_digest(tfm, line, count, digest); + if (!rc) + rc =3D _call_extend(pvd, _rtmr_mr(rtmr), digest); + if (rc) + return rc; + } + + memcpy(log, line, count); + log[count] =3D '\n'; + _rtmr_log_inc_size(rtmr, count +=3D newlines + 1); + + return _rtmr_log_update_attribute(rtmr) ?: count; +} + +static inline size_t snprint_hex(char *sbuf, ssize_t size, const u8 *data, size_t len) { + BUG_ON(size < len * 2); size_t ret =3D 0; for (size_t i =3D 0; i < len; ++i) ret +=3D snprintf(sbuf + ret, size - ret, "%02x", data[i]); @@ -614,15 +706,26 @@ static ssize_t _mr_write(struct file *filp, struct ko= bject *kobj, mr =3D (typeof(mr))attr->private; BUG_ON(mr->mr_size !=3D attr->size); =20 - pvd =3D _mr_to_group(mr, kobj); + pvd =3D _mr_to_provider(mr, kobj); rc =3D down_write_killable(&pvd->rwsem); if (rc) return rc; =20 - if (mr->mr_flags & TSM_MR_F_X) - rc =3D pvd->provider->extend(pvd->provider, mr, (u8 *)page); - else { - BUG_ON(!(mr->mr_flags & TSM_MR_F_W)); + if (mr->mr_flags & TSM_MR_F_X) { + struct _rtmr *rtmr; + rtmr =3D container_of(kobj, typeof(*rtmr), kobj); + + char ext_line[0x100] =3D "# .EXTEND "; + size_t len =3D strnlen(ext_line, sizeof(ext_line)); + len +=3D snprint_hex(ext_line + len, sizeof(ext_line) - len, page, + count); + rc =3D _log_extend_line(rtmr, ext_line, ext_line + len, + rtmr->log_in_sync, NULL); + if (!IS_ERR_VALUE(rc)) + rc =3D _call_extend(pvd, mr, page); + if (!rc) + rtmr->log_in_sync =3D false; + } else { memcpy(mr->mr_value, page, count); } =20 @@ -636,11 +739,110 @@ static ssize_t _mr_write(struct file *filp, struct k= object *kobj, return rc ?: count; } =20 +static ssize_t _log_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *page, loff_t off, + size_t count) +{ + struct _mr_provider *pvd; + int rc; + + if (unlikely(off < 0)) + return -EINVAL; + + if (unlikely(off > attr->size)) + return 0; + + count =3D min(count, attr->size - off); + if (likely(count > 0)) { + pvd =3D container_of(kobj->kset, typeof(*pvd), kset); + rc =3D down_read_interruptible(&pvd->rwsem); + if (rc) + return rc; + + memcpy(page, (char *)attr->private + off, count); + + up_read(&pvd->rwsem); + } + + return count; +} + +static ssize_t _log_extend(struct _rtmr *rtmr, const char *page, size_t co= unt, + struct crypto_shash *tfm) +{ + ssize_t rc =3D 0, sz =3D 0; + for (size_t i =3D 0; i < count && !IS_ERR_VALUE(rc);) { + size_t j; + for (j =3D i; j < count && (page[j] !=3D '\n' && page[j] !=3D '\r');) + ++j; + + rc =3D _log_extend_line(rtmr, &page[i], &page[j], sz =3D=3D 0, tfm); + sz +=3D rc; + + for (i =3D j; i < count && (page[i] =3D=3D '\n' || page[i] =3D=3D '\r');) + ++i; + } + + return IS_ERR_VALUE(rc) ? rc : sz; +} + +DEFINE_FREE(shash, struct crypto_shash *, + if (!IS_ERR(_T)) crypto_free_shash(_T)); + +static ssize_t append_event_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *page, + size_t count) +{ + struct _rtmr *rtmr; + rtmr =3D container_of(kobj, typeof(*rtmr), kobj); + + const struct tsm_measurement_register *mr; + mr =3D _rtmr_mr(rtmr); + + struct crypto_shash *tfm __free(shash) =3D + crypto_alloc_shash(hash_algo_name[mr->mr_hash], 0, 0); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + struct _mr_provider *pvd; + pvd =3D container_of(kobj->kset, typeof(*pvd), kset); + + ssize_t rc =3D down_write_killable(&pvd->rwsem); + if (rc) + return rc; + + if (!rtmr->log_in_sync) { + if (mr->mr_flags & TSM_MR_F_L) + rc =3D _call_refresh(pvd, mr); + + if (!IS_ERR_VALUE(rc)) { + char sync[0x100] =3D "SYNC "; + strncat(sync, hash_algo_name[mr->mr_hash], + sizeof(sync)); + size_t len =3D strnlen(sync, sizeof(sync)); + sync[len++] =3D '/'; + len +=3D snprint_hex(sync + len, sizeof(sync) - len, + mr->mr_value, mr->mr_size); + rc =3D _log_extend_line(rtmr, sync, sync + len, + _rtmr_log_size(rtmr) > 0, tfm); + } + } + + if (!IS_ERR_VALUE(rc)) { + rtmr->log_in_sync =3D true; + rc =3D _log_extend(rtmr, page, count, tfm); + } + + up_write(&pvd->rwsem); + return IS_ERR_VALUE(rc) ? rc : count; +} + static void _rtmr_release(struct kobject *kobj) { struct _rtmr *rtmr; rtmr =3D container_of(kobj, typeof(*rtmr), kobj); pr_debug("%s(%s)\n", __func__, kobject_name(kobj)); + kfree(_rtmr_log(rtmr)); kfree(rtmr); } =20 @@ -663,7 +865,7 @@ static struct _rtmr *_rtmr_create(const struct tsm_meas= urement_register *mr, sysfs_bin_attr_init(&rtmr->battrs[_RTMR_BATTR_DIGEST]); rtmr->battrs[_RTMR_BATTR_DIGEST].attr.name =3D "digest"; if (mr->mr_flags & TSM_MR_F_W) - rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IWUSR; + rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IWUSR; if (mr->mr_flags & TSM_MR_F_R) rtmr->battrs[_RTMR_BATTR_DIGEST].attr.mode |=3D S_IRUGO; =20 @@ -672,6 +874,11 @@ static struct _rtmr *_rtmr_create(const struct tsm_mea= surement_register *mr, rtmr->battrs[_RTMR_BATTR_DIGEST].write =3D _mr_write; rtmr->battrs[_RTMR_BATTR_DIGEST].private =3D (void *)mr; =20 + sysfs_bin_attr_init(&rtmr->battrs[_RTMR_BATTR_LOG]); + rtmr->battrs[_RTMR_BATTR_LOG].attr.name =3D "event_log"; + rtmr->battrs[_RTMR_BATTR_LOG].attr.mode =3D S_IRUGO; + rtmr->battrs[_RTMR_BATTR_LOG].read =3D _log_read; + rtmr->kobj.kset =3D &pvd->kset; rc =3D kobject_init_and_add(&rtmr->kobj, &_rtmr_ktype, NULL, "%s", mr->mr_name); @@ -734,6 +941,7 @@ DEFINE_FREE(_unregister_measurement_provider, struct _m= r_provider *, int tsm_register_measurement_provider(struct tsm_measurement_provider *tpv= d) { static struct kobj_attribute _attr_hash =3D __ATTR_RO(hash_algo); + static struct kobj_attribute _attr_append =3D __ATTR_WO(append_event); =20 struct _mr_provider *pvd __free(_unregister_measurement_provider); int rc, nr; @@ -754,11 +962,11 @@ int tsm_register_measurement_provider(struct tsm_meas= urement_provider *tpvd) return PTR_ERR(rtmr); =20 struct attribute *attrs[] =3D { + &_attr_append.attr, &_attr_hash.attr, NULL, }; - struct bin_attribute - *battrs[_RTMR_BATTR__COUNT + 1] =3D {}; + struct bin_attribute *battrs[_RTMR_BATTR__COUNT + 1] =3D {}; for (int j =3D 0; j < _RTMR_BATTR__COUNT; ++j) battrs[j] =3D &rtmr->battrs[j]; struct attribute_group agrp =3D { --=20 2.43.0