fru: Introduce get_fru_record_by_option_check()

get_fru_record_by_option() protected the injection of record data into
the record buffer only using assert(). get_fru_record_by_option_check()
instead returns an error if the record data would overflow the record
buffer.

Callers should prefer get_fru_record_by_option_check(), and testing its
result, over get_fru_record_by_option().

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: Ifc1459863cd9f3f7289badb9f1842386d31cbd87
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f0b16e..6cdaa84 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
 ### Added
 
 1. bios_table: Introduce pldm_bios_table_append_pad_checksum_check()
+2. fru: Introduce get_fru_record_by_option_check()
 
 ### Changed
 
diff --git a/include/libpldm/fru.h b/include/libpldm/fru.h
index ce93e55..d046805 100644
--- a/include/libpldm/fru.h
+++ b/include/libpldm/fru.h
@@ -464,6 +464,22 @@
 void get_fru_record_by_option(const uint8_t *table, size_t table_size,
 			      uint8_t *record_table, size_t *record_size,
 			      uint16_t rsi, uint8_t rt, uint8_t ft);
+
+/** @brief Get FRU Record Table By Option or return an error
+ *  @param[in] table - The source fru record table
+ *  @param[in] table_size - Size of the source fru record table
+ *  @param[out] record_table - Fru table fetched based on the input option
+ *  @param[in/out] record_size - Size of the table fetched by fru record option
+ *  @param[in] rsi - FRU record set identifier
+ *  @param[in] rt - FRU record type
+ *  @param[in] ft - FRU field type
+ *
+ *  @return PLDM_SUCCESS if no error occurs. PLDM_ERROR_INVALID_LENGTH if record_size lacks capacity
+ *  	    to encode the relevant records.
+ */
+int get_fru_record_by_option_check(const uint8_t *table, size_t table_size,
+				   uint8_t *record_table, size_t *record_size,
+				   uint16_t rsi, uint8_t rt, uint8_t ft);
 /* SetFruRecordTable */
 
 /** @brief Decode SetFruRecordTable request data
diff --git a/src/fru.c b/src/fru.c
index aeb7d50..39d8576 100644
--- a/src/fru.c
+++ b/src/fru.c
@@ -216,6 +216,17 @@
 			      uint8_t *record_table, size_t *record_size,
 			      uint16_t rsi, uint8_t rt, uint8_t ft)
 {
+	int rc = get_fru_record_by_option_check(table, table_size, record_table,
+						record_size, rsi, rt, ft);
+	(void)rc;
+	assert(rc == PLDM_SUCCESS);
+}
+
+LIBPLDM_ABI_TESTING
+int get_fru_record_by_option_check(const uint8_t *table, size_t table_size,
+				   uint8_t *record_table, size_t *record_size,
+				   uint16_t rsi, uint8_t rt, uint8_t ft)
+{
 	const struct pldm_fru_record_data_format *record_data_src =
 		(const struct pldm_fru_record_data_format *)table;
 	struct pldm_fru_record_data_format *record_data_dest;
@@ -246,6 +257,9 @@
 		      sizeof(struct pldm_fru_record_tlv);
 
 		assert(pos - record_table + len < *record_size);
+		if (pos - record_table + len >= *record_size) {
+			return PLDM_ERROR_INVALID_LENGTH;
+		}
 		memcpy(pos, record_data_src, len);
 
 		record_data_dest = (struct pldm_fru_record_data_format *)pos;
@@ -257,6 +271,9 @@
 			len = sizeof(*tlv) - 1 + tlv->length;
 			if (tlv->type == ft || ft == 0) {
 				assert(pos - record_table + len < *record_size);
+				if (pos - record_table + len >= *record_size) {
+					return PLDM_ERROR_INVALID_LENGTH;
+				}
 				memcpy(pos, tlv, len);
 				pos += len;
 				count++;
@@ -270,6 +287,8 @@
 	}
 
 	*record_size = pos - record_table;
+
+	return PLDM_SUCCESS;
 }
 
 LIBPLDM_ABI_STABLE