libipt, image: validate an image section
Validate that a given section would be returned by an image lookup.
This allows image users who want to cache image sections provided by
pt_image_find() to check if their cached section would still be returned by
another call to pt_image_find().
For performance reasons we allow sporadic validation fails. They will require
the user to re-fetch the cached section with another call to pt_image_find(),
which causes a small loss of performance but is otherwise harmless.
Change-Id: I674e08969c2a29dc41e289b0faec1f5d09f27175
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
diff --git a/libipt/internal/include/pt_image.h b/libipt/internal/include/pt_image.h
index 22c659b..9812fa6 100644
--- a/libipt/internal/include/pt_image.h
+++ b/libipt/internal/include/pt_image.h
@@ -132,4 +132,21 @@
uint64_t *laddr, const struct pt_asid *asid,
uint64_t vaddr);
+/* Validate an image section.
+ *
+ * Validate that a lookup by @asid and @vaddr in @image would result in @section
+ * loaded at @laddr identified by @isid.
+ *
+ * Validation may fail sporadically, e.g. if @section has been evicted from
+ * @image's LRU cache.
+ *
+ * Returns zero on success, a negative error code otherwise.
+ * Returns -pte_invalid if @image or @asid is NULL.
+ * Returns -pte_nomap if validation failed.
+ */
+extern int pt_image_validate(const struct pt_image *image,
+ const struct pt_asid *asid, uint64_t vaddr,
+ const struct pt_section *section, uint64_t laddr,
+ int isid);
+
#endif /* PT_IMAGE_H */
diff --git a/libipt/src/pt_image.c b/libipt/src/pt_image.c
index 25faa6e..d2d406e 100644
--- a/libipt/src/pt_image.c
+++ b/libipt/src/pt_image.c
@@ -937,3 +937,38 @@
return slist->isid;
}
+
+int pt_image_validate(const struct pt_image *image, const struct pt_asid *asid,
+ uint64_t vaddr, const struct pt_section *section,
+ uint64_t laddr, int isid)
+{
+ struct pt_mapped_section *msec;
+ struct pt_section_list *slist;
+
+ if (!image)
+ return -pte_internal;
+
+ /* We only look at the top of our LRU stack and accept sporadic
+ * validation fails if @section moved down in the LRU stack or has been
+ * evicted.
+ *
+ * A failed validation requires decoders to re-fetch the section so it
+ * only results in a (relatively small) performance loss.
+ */
+ slist = image->sections;
+ if (!slist)
+ return -pte_nomap;
+
+ if (slist->isid != isid)
+ return -pte_nomap;
+
+ msec = &slist->section;
+
+ if (pt_msec_section(msec) != section)
+ return -pte_nomap;
+
+ if (pt_msec_begin(msec) != laddr)
+ return -pte_nomap;
+
+ return pt_image_check_msec(msec, asid, vaddr);
+}
diff --git a/libipt/test/src/ptunit-image.c b/libipt/test/src/ptunit-image.c
index a962289..137f0f7 100644
--- a/libipt/test/src/ptunit-image.c
+++ b/libipt/test/src/ptunit-image.c
@@ -2001,6 +2001,71 @@
return ptu_passed();
}
+static struct ptunit_result validate_null(struct image_fixture *ifix)
+{
+ int status;
+
+ status = pt_image_validate(NULL, &ifix->asid[0], 0x1004ull,
+ &ifix->section[0], 0x1000ull, 10);
+ ptu_int_eq(status, -pte_internal);
+
+ status = pt_image_validate(&ifix->image, NULL, 0x1004ull,
+ &ifix->section[0], 0x1000ull, 10);
+ ptu_int_eq(status, -pte_internal);
+
+ return ptu_passed();
+}
+
+static struct ptunit_result validate(struct image_fixture *ifix)
+{
+ int status;
+
+ /* This test depends on the order in which sections are stored when
+ * added to the image.
+ *
+ * Since pt_image_validate() only looks at the top of the LRU stack we
+ * can only validate that section - i.e. the one that was added first.
+ */
+ status = pt_image_validate(&ifix->image, &ifix->asid[0], 0x1004ull,
+ &ifix->section[0], 0x1000ull, 10);
+ ptu_int_eq(status, 0);
+
+ return ptu_passed();
+}
+
+static struct ptunit_result validate_bad_asid(struct image_fixture *ifix)
+{
+ int status;
+
+ status = pt_image_validate(&ifix->image, &ifix->asid[1], 0x1004ull,
+ &ifix->section[0], 0x1000ull, 10);
+ ptu_int_eq(status, -pte_nomap);
+
+ return ptu_passed();
+}
+
+static struct ptunit_result validate_bad_laddr(struct image_fixture *ifix)
+{
+ int status;
+
+ status = pt_image_validate(&ifix->image, &ifix->asid[0], 0x1004ull,
+ &ifix->section[0], 0x2000ull, 10);
+ ptu_int_eq(status, -pte_nomap);
+
+ return ptu_passed();
+}
+
+static struct ptunit_result validate_bad_isid(struct image_fixture *ifix)
+{
+ int status;
+
+ status = pt_image_validate(&ifix->image, &ifix->asid[0], 0x1004ull,
+ &ifix->section[0], 0x1000ull, 11);
+ ptu_int_eq(status, -pte_nomap);
+
+ return ptu_passed();
+}
+
struct ptunit_result ifix_init(struct image_fixture *ifix)
{
int index;
@@ -2163,6 +2228,12 @@
ptu_run_f(suite, find_bad_asid, rfix);
ptu_run_f(suite, find_nomem, rfix);
+ ptu_run_f(suite, validate_null, rfix);
+ ptu_run_f(suite, validate, rfix);
+ ptu_run_f(suite, validate_bad_asid, rfix);
+ ptu_run_f(suite, validate_bad_laddr, rfix);
+ ptu_run_f(suite, validate_bad_isid, rfix);
+
ptunit_report(&suite);
return suite.nr_fails;
}