bios_table: pldm_bios_table_iter_next(): Invalid entry halts iteration

The attribute iterator machinary prevented misbehaviour through use of
assert(). The attribute list is maintained as a linear sequence of
variably sized data structures that are packed against each other in the
address space. The iterator is implemented by assigning a callback that
can determine the length of each entry as appropriate for the entry's
type, and then moving the iterator's cursor between elements.

The length derivation for some elements was protected by assert(). To
avoid the asserts we rework the length callback prototype to return
a signed size value and indicate an error state with a negative size.

pldm_bios_table_iter_next() is reworked to detect the error case on
deriving the element size (negative size) and behave as if the iterator
has terminated.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: I80db3fe179201b169acc68c68633d8dd3f3a6334
diff --git a/tests/bios_table_iter.c b/tests/bios_table_iter.c
new file mode 100644
index 0000000..f8da402
--- /dev/null
+++ b/tests/bios_table_iter.c
@@ -0,0 +1,54 @@
+/* Force elision of assert() */
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/* NOLINTNEXTLINE(bugprone-suspicious-include) */
+#include "bios_table.c"
+
+/* Satisfy the symbol needs of bios_table.c */
+uint32_t crc32(const void* data __attribute__((unused)),
+               size_t size __attribute__((unused)))
+{
+    return 0;
+}
+
+/* This is the non-death version of TEST(Iterator, DeathTest) */
+int main(void)
+{
+    struct pldm_bios_attr_table_entry entries[2] = {0};
+    struct pldm_bios_table_iter* iter;
+    int result;
+
+    static_assert(2 * sizeof(entries[0]) == sizeof(entries), "");
+
+    entries[0].attr_type = PLDM_BIOS_PASSWORD;
+    entries[1].attr_type = PLDM_BIOS_STRING_READ_ONLY;
+
+    iter = pldm_bios_table_iter_create(entries, sizeof(entries),
+                                       PLDM_BIOS_ATTR_TABLE);
+
+    /*
+     * We expect the test configuration to claim the iterator has reached the
+     * end beause the there's no entry length descriptor for the
+     * PLDM_BIOS_PASSWORD entry type. By the attr_able_entry_length()
+     * implementation this would normally trigger an assert() to uphold that the
+     * necessary pointers are not NULL. However, we've defined NDEBUG above and
+     * so the assert() is elided. That should force us down the path of the
+     * early-exit, which should in-turn yield a `true` result from
+     * pldm_bios_table_iter_is_end() to prevent further attempts to access
+     * invalid objects.
+     */
+    result = pldm_bios_table_iter_is_end(iter) ? EXIT_SUCCESS : EXIT_FAILURE;
+
+    pldm_bios_table_iter_free(iter);
+
+    exit(result);
+
+    return 0;
+}