libnsm: bound FPGA diagnostics decode copy by destination size

decode_get_fpga_diagnostics_settings_resp copied an on-wire data_size
into a caller-provided buffer without verifying it fits the destination,
so a crafted response could overflow the small fixed buffer used by the
write-protect path (the reported PoC). Add a data_size_max capacity
argument and reject an oversized data_size with NSM_SW_ERROR_LENGTH
before the memcpy; the six wrappers pass sizeof their destination
struct, so no caller signature changes. Regression tests updated.

Fixes nvbug https://nvbugspro.nvidia.com/bug/6232725
Signed-off-by: Abhishek Verma <abhisverma@nvidia.com>
diff --git a/libnsm/device-configuration.c b/libnsm/device-configuration.c
index 3cfedea..acca8f5 100644
--- a/libnsm/device-configuration.c
+++ b/libnsm/device-configuration.c
@@ -1250,11 +1250,9 @@
 	return NSM_SW_SUCCESS;
 }
 
-int decode_get_fpga_diagnostics_settings_resp(const struct nsm_msg *msg,
-					      size_t msg_len, uint8_t *cc,
-					      uint16_t *data_size,
-					      uint16_t *reason_code,
-					      uint8_t *data)
+int decode_get_fpga_diagnostics_settings_resp(
+    const struct nsm_msg *msg, size_t msg_len, uint8_t *cc, uint16_t *data_size,
+    uint16_t *reason_code, uint8_t *data, size_t data_size_max)
 {
 	if (msg == NULL || cc == NULL || data_size == NULL ||
 	    reason_code == NULL || data == NULL) {
@@ -1281,6 +1279,9 @@
 	    msg_len) {
 		return NSM_SW_ERROR_LENGTH;
 	}
+	if ((size_t)*data_size > data_size_max) {
+		return NSM_SW_ERROR_LENGTH;
+	}
 	memcpy(data, resp->data, *data_size);
 	return NSM_SW_SUCCESS;
 }
@@ -1301,7 +1302,8 @@
 {
 	uint16_t data_size = 0;
 	int ret = decode_get_fpga_diagnostics_settings_resp(
-	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)data);
+	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)data,
+	    sizeof(struct nsm_fpga_diagnostics_settings_wp));
 	if (ret != NSM_SW_SUCCESS || *cc != NSM_SUCCESS)
 		return ret;
 	if (data_size < sizeof(struct nsm_fpga_diagnostics_settings_wp))
@@ -1325,7 +1327,8 @@
 {
 	uint16_t data_size = 0;
 	int ret = decode_get_fpga_diagnostics_settings_resp(
-	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)data);
+	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)data,
+	    sizeof(struct nsm_fpga_diagnostics_settings_wp_jumper));
 	if (ret != NSM_SW_SUCCESS || *cc != NSM_SUCCESS)
 		return ret;
 	if (data_size < sizeof(struct nsm_fpga_diagnostics_settings_wp_jumper))
@@ -1351,7 +1354,7 @@
 	uint16_t data_size = 0;
 	int ret = decode_get_fpga_diagnostics_settings_resp(
 	    msg, msg_len, cc, &data_size, reason_code,
-	    (uint8_t *)power_supply_status);
+	    (uint8_t *)power_supply_status, sizeof(uint8_t));
 	if (ret != NSM_SW_SUCCESS || *cc != NSM_SUCCESS)
 		return ret;
 	if (data_size < sizeof(uint8_t))
@@ -1373,7 +1376,8 @@
 {
 	uint16_t data_size = 0;
 	int ret = decode_get_fpga_diagnostics_settings_resp(
-	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)presence);
+	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)presence,
+	    sizeof(uint8_t));
 	if (ret != NSM_SW_SUCCESS || *cc != NSM_SUCCESS)
 		return ret;
 	if (data_size < sizeof(uint8_t))
@@ -1395,7 +1399,8 @@
 {
 	uint16_t data_size = 0;
 	int ret = decode_get_fpga_diagnostics_settings_resp(
-	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)power_status);
+	    msg, msg_len, cc, &data_size, reason_code, (uint8_t *)power_status,
+	    sizeof(uint8_t));
 	if (ret != NSM_SW_SUCCESS || *cc != NSM_SUCCESS)
 		return ret;
 	if (data_size < sizeof(uint8_t))
@@ -1417,7 +1422,7 @@
 {
 	uint16_t data_size = 0;
 	int ret = decode_get_fpga_diagnostics_settings_resp(
-	    msg, msg_len, cc, &data_size, reason_code, mode);
+	    msg, msg_len, cc, &data_size, reason_code, mode, sizeof(uint8_t));
 	if (ret != NSM_SW_SUCCESS || *cc != NSM_SUCCESS)
 		return ret;
 	if (data_size < sizeof(uint8_t))
diff --git a/libnsm/device-configuration.h b/libnsm/device-configuration.h
index d0eebac..b096951 100644
--- a/libnsm/device-configuration.h
+++ b/libnsm/device-configuration.h
@@ -1311,13 +1311,13 @@
  *  @param[out] cc - pointer to response message completion code
  *  @param[in] data_size - data size
  *  @param[out] data  - pointer to the array of data
+ *  @param[in] data_size_max - maximum size of the data buffer in bytes; the
+ *             decoded data_size must not exceed this
  *  @return nsm_completion_codes
  */
-int decode_get_fpga_diagnostics_settings_resp(const struct nsm_msg *msg,
-					      size_t msg_len, uint8_t *cc,
-					      uint16_t *data_size,
-					      uint16_t *reason_code,
-					      uint8_t *data);
+int decode_get_fpga_diagnostics_settings_resp(
+    const struct nsm_msg *msg, size_t msg_len, uint8_t *cc, uint16_t *data_size,
+    uint16_t *reason_code, uint8_t *data, size_t data_size_max);
 
 /** @brief Encode a Get FPGA Diagnostics Settings response msg for
  * Get WP Settings
diff --git a/libnsm/test/libnsm_device_configuration_branch2_test.cpp b/libnsm/test/libnsm_device_configuration_branch2_test.cpp
index 84bff72..60d0119 100644
--- a/libnsm/test/libnsm_device_configuration_branch2_test.cpp
+++ b/libnsm/test/libnsm_device_configuration_branch2_test.cpp
@@ -104,7 +104,8 @@
 	uint16_t data_size = 0, reason_code = 0;
 	uint8_t data[64] = {};
 	auto rc = decode_get_fpga_diagnostics_settings_resp(
-	    msg, buf.size(), nullptr, &data_size, &reason_code, data);
+	    msg, buf.size(), nullptr, &data_size, &reason_code, data,
+	    sizeof(data));
 	EXPECT_EQ(rc, NSM_SW_ERROR_NULL);
 }
 
@@ -119,7 +120,7 @@
 	uint16_t data_size = 0, reason_code = 0;
 	uint8_t data[64] = {};
 	auto rc = decode_get_fpga_diagnostics_settings_resp(
-	    msg, buf.size(), &cc, &data_size, &reason_code, data);
+	    msg, buf.size(), &cc, &data_size, &reason_code, data, sizeof(data));
 	EXPECT_EQ(rc, NSM_SW_SUCCESS);
 	EXPECT_EQ(cc, 0xFF);
 }
@@ -137,7 +138,7 @@
 	uint16_t data_size = 0, reason_code = 0;
 	uint8_t data[64] = {};
 	auto rc = decode_get_fpga_diagnostics_settings_resp(
-	    msg, buf.size(), &cc, &data_size, &reason_code, data);
+	    msg, buf.size(), &cc, &data_size, &reason_code, data, sizeof(data));
 	EXPECT_EQ(rc, NSM_SW_ERROR_LENGTH);
 }
 
diff --git a/libnsm/test/libnsm_device_configuration_branch4_test.cpp b/libnsm/test/libnsm_device_configuration_branch4_test.cpp
index 181b80b..7def13b 100644
--- a/libnsm/test/libnsm_device_configuration_branch4_test.cpp
+++ b/libnsm/test/libnsm_device_configuration_branch4_test.cpp
@@ -633,7 +633,7 @@
 	uint16_t ds = 0, reason = 0;
 	uint8_t data[32] = {};
 	auto rc = decode_get_fpga_diagnostics_settings_resp(
-	    msg, buf.size(), nullptr, &ds, &reason, data);
+	    msg, buf.size(), nullptr, &ds, &reason, data, sizeof(data));
 	EXPECT_EQ(rc, NSM_SW_ERROR_NULL);
 }
 
@@ -644,7 +644,7 @@
 	uint8_t cc = 0;
 	uint16_t ds = 0, reason = 0;
 	auto rc = decode_get_fpga_diagnostics_settings_resp(
-	    msg, buf.size(), &cc, &ds, &reason, nullptr);
+	    msg, buf.size(), &cc, &ds, &reason, nullptr, 0);
 	EXPECT_EQ(rc, NSM_SW_ERROR_NULL);
 }
 
diff --git a/libnsm/test/libnsm_device_configuration_test.cpp b/libnsm/test/libnsm_device_configuration_test.cpp
index a653a7e..ce8941b 100644
--- a/libnsm/test/libnsm_device_configuration_test.cpp
+++ b/libnsm/test/libnsm_device_configuration_test.cpp
@@ -751,7 +751,8 @@
 	EXPECT_EQ(rc, NSM_SW_ERROR_NULL);
 
 	rc = decode_get_fpga_diagnostics_settings_resp(
-	    response, msg_len, &cc, NULL, &reason_code, (uint8_t *)data);
+	    response, msg_len, &cc, NULL, &reason_code, (uint8_t *)data,
+	    sizeof(struct nsm_fpga_diagnostics_settings_wp));
 	EXPECT_EQ(rc, NSM_SW_ERROR_NULL);
 
 	rc = decode_get_fpga_diagnostics_settings_wp_resp(
@@ -874,7 +875,8 @@
 	EXPECT_EQ(rc, NSM_SW_ERROR_NULL);
 
 	rc = decode_get_fpga_diagnostics_settings_resp(
-	    response, msg_len, &cc, NULL, &reason_code, (uint8_t *)data);
+	    response, msg_len, &cc, NULL, &reason_code, (uint8_t *)data,
+	    sizeof(struct nsm_fpga_diagnostics_settings_wp_jumper));
 	EXPECT_EQ(rc, NSM_SW_ERROR_NULL);
 
 	rc = decode_get_fpga_diagnostics_settings_wp_jumper_resp(
@@ -3779,7 +3781,7 @@
 	uint16_t rc = 0;
 	uint8_t dst[8] = {0};
 	EXPECT_EQ(decode_get_fpga_diagnostics_settings_resp(
-		      asMsg(buf), buf.size(), &cc, &ds, &rc, dst),
+		      asMsg(buf), buf.size(), &cc, &ds, &rc, dst, sizeof(dst)),
 		  NSM_SW_ERROR_LENGTH);
 }