From 0995be1d38ac6045cc93d31d08b74e557fd46df4 Mon Sep 17 00:00:00 2001 From: Aaron Li Date: Tue, 7 Jan 2025 11:09:27 +0800 Subject: [PATCH 1/4] Fix chunk_seq_no wrap in chunk send. Fix #2875 Signed-off-by: Aaron Li --- .../libspdm_req_send_receive.c | 21 +++-- unit_test/test_spdm_requester/chunk_send.c | 78 ++++++++++++++++++- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/library/spdm_requester_lib/libspdm_req_send_receive.c b/library/spdm_requester_lib/libspdm_req_send_receive.c index 8639de04129..7db85c129f4 100644 --- a/library/spdm_requester_lib/libspdm_req_send_receive.c +++ b/library/spdm_requester_lib/libspdm_req_send_receive.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -316,6 +316,7 @@ libspdm_return_t libspdm_handle_large_request( size_t copy_size; libspdm_chunk_info_t *send_info; uint32_t min_data_transfer_size; + uint64_t max_chunk_data_transfer_size; spdm_error_response_t *spdm_error; if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_12) { @@ -330,6 +331,20 @@ libspdm_return_t libspdm_handle_large_request( return LIBSPDM_STATUS_ERROR_PEER; } + /* Fail if exceed max chunks */ + min_data_transfer_size = LIBSPDM_MIN( + spdm_context->connection_info.capability.data_transfer_size, + spdm_context->local_context.capability.sender_data_transfer_size); + + max_chunk_data_transfer_size = + ((size_t) min_data_transfer_size - sizeof(spdm_chunk_send_request_t)) * 65536 - + sizeof(uint32_t); + /* max_spdm_msg_size is already checked in caller */ + + if (request_size > max_chunk_data_transfer_size) { + return LIBSPDM_STATUS_BUFFER_TOO_SMALL; + } + /* now we can get sender buffer */ transport_header_size = spdm_context->local_context.capability.transport_header_size; @@ -359,10 +374,6 @@ libspdm_return_t libspdm_handle_large_request( request = NULL; /* Invalidate to prevent accidental use. */ request_size = 0; - min_data_transfer_size = LIBSPDM_MIN( - spdm_context->connection_info.capability.data_transfer_size, - spdm_context->local_context.capability.sender_data_transfer_size); - do { LIBSPDM_ASSERT(send_info->large_message_capacity >= transport_header_size); spdm_request = (spdm_chunk_send_request_t *)((uint8_t *)message + transport_header_size); diff --git a/unit_test/test_spdm_requester/chunk_send.c b/unit_test/test_spdm_requester/chunk_send.c index b66e411754f..9aa312fdfb2 100644 --- a/unit_test/test_spdm_requester/chunk_send.c +++ b/unit_test/test_spdm_requester/chunk_send.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2022 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -105,6 +105,10 @@ libspdm_return_t libspdm_requester_chunk_send_test_send_message( LIBSPDM_ASSERT(chunk_send->header.request_response_code == SPDM_CHUNK_SEND); return LIBSPDM_STATUS_SUCCESS; } + if (spdm_test_context->case_id == 13) { + /* Should never reach here since the test case is meant to fail before send */ + LIBSPDM_ASSERT(0); + } return LIBSPDM_STATUS_SEND_FAIL; } @@ -323,6 +327,10 @@ libspdm_return_t libspdm_requester_chunk_send_test_receive_message( return LIBSPDM_STATUS_SUCCESS; } + if (spdm_test_context->case_id == 13) { + /* Should never reach here since the test case is meant to fail before send */ + LIBSPDM_ASSERT(0); + } return LIBSPDM_STATUS_RECEIVE_FAIL; } @@ -380,6 +388,56 @@ libspdm_return_t libspdm_test_requester_chunk_send_generic_test_case( return status; } +libspdm_return_t libspdm_test_requester_chunk_send_vendor_specific_test_case( + void** state, uint32_t case_id) +{ + /* Use vendor specific request to generate a large request. */ + libspdm_return_t status; + libspdm_test_context_t* spdm_test_context; + libspdm_context_t* spdm_context; + + uint16_t standard_id = 6; + uint8_t vendor_id_len = 2; + uint8_t vendor_id[SPDM_MAX_VENDOR_ID_LENGTH] = {0xAA, 0xAA}; + uint16_t data_len = 65535; + uint8_t data[65535] = {0}; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = case_id; + spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_12 << + SPDM_VERSION_NUMBER_SHIFT_BIT; + /* Large request need a large scratch buffer. */ + spdm_context->connection_info.capability.max_spdm_msg_size = 0x12000; + spdm_context->local_context.capability.max_spdm_msg_size = 0x12000; + spdm_context->connection_info.connection_state = + LIBSPDM_CONNECTION_STATE_NEGOTIATED; + spdm_context->connection_info.capability.flags |= + (SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP + | SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP); + spdm_context->connection_info.capability.data_transfer_size + = sizeof(spdm_chunk_send_request_t) + 1; + spdm_context->local_context.capability.sender_data_transfer_size + = CHUNK_SEND_REQUESTER_UNIT_TEST_DATA_TRANSFER_SIZE; + spdm_context->local_context.is_requester = true; + + spdm_test_context->scratch_buffer_size = + libspdm_get_sizeof_required_scratch_buffer(spdm_context); + spdm_test_context->scratch_buffer = (void *)malloc(spdm_test_context->scratch_buffer_size); + libspdm_set_scratch_buffer (spdm_context, + spdm_test_context->scratch_buffer, + spdm_test_context->scratch_buffer_size); + + libspdm_reset_message_a(spdm_context); + + status = libspdm_vendor_send_request_receive_response(spdm_context, NULL, + standard_id, vendor_id_len, vendor_id, + data_len, data, + &standard_id, &vendor_id_len, vendor_id, + &data_len, data); + return status; +} + void libspdm_test_requester_chunk_send_case1(void** state) { libspdm_return_t status; @@ -472,6 +530,20 @@ void libspdm_test_requester_chunk_send_case12(void** state) assert_int_equal(status, LIBSPDM_STATUS_ERROR_PEER); } +#if LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES +/** + * Test 13: Request size shall not exceed max supported transfer size. + * Expected behavior: returns a status of LIBSPDM_STATUS_SEND_FAIL, + **/ +void libspdm_test_requester_chunk_send_case13(void** state) +{ + libspdm_return_t status; + + status = libspdm_test_requester_chunk_send_vendor_specific_test_case(state, 13); + assert_int_equal(status, LIBSPDM_STATUS_SEND_FAIL); +} +#endif + int libspdm_requester_chunk_send_test_main(void) { /* Test the CHUNK_SEND handlers in various requester handlers */ @@ -500,6 +572,10 @@ int libspdm_requester_chunk_send_test_main(void) cmocka_unit_test(libspdm_test_requester_chunk_send_case11), /* ErrorCode == LargeResponse shall not be allowed in ResponseToLargeRequest */ cmocka_unit_test(libspdm_test_requester_chunk_send_case12), +#if LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES + /* Request size exceed max chunks */ + cmocka_unit_test(libspdm_test_requester_chunk_send_case13), +#endif }; libspdm_test_context_t test_context = { From 63642f1cfdaaa975cd36a77d17c65932be0a033e Mon Sep 17 00:00:00 2001 From: Aaron Li Date: Tue, 7 Jan 2025 14:24:07 +0800 Subject: [PATCH 2/4] Fix chunk_seq_no wrap in chunk send ack. Fix #2875 Signed-off-by: Aaron Li --- .../libspdm_rsp_chunk_send_ack.c | 10 +- .../test_spdm_responder/chunk_send_ack.c | 165 +++++++++++++++++- 2 files changed, 173 insertions(+), 2 deletions(-) diff --git a/library/spdm_responder_lib/libspdm_rsp_chunk_send_ack.c b/library/spdm_responder_lib/libspdm_rsp_chunk_send_ack.c index 94aa6b02415..941dba1148e 100644 --- a/library/spdm_responder_lib/libspdm_rsp_chunk_send_ack.c +++ b/library/spdm_responder_lib/libspdm_rsp_chunk_send_ack.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -25,6 +25,7 @@ libspdm_return_t libspdm_get_response_chunk_send(libspdm_context_t *spdm_context size_t scratch_buffer_size; uint8_t* chunk_response; size_t chunk_response_size; + uint64_t max_chunk_data_transfer_size; spdm_request = (const spdm_chunk_send_request_t*) request; @@ -103,6 +104,9 @@ libspdm_return_t libspdm_get_response_chunk_send(libspdm_context_t *spdm_context chunk = (((const uint8_t*) (spdm_request + 1)) + sizeof(uint32_t)); calc_max_chunk_size = (uint32_t)request_size - (sizeof(spdm_chunk_send_request_t) + sizeof(uint32_t)); + max_chunk_data_transfer_size = + ((size_t) spdm_context->local_context.capability.data_transfer_size + - sizeof(spdm_chunk_send_request_t)) * 65536 - sizeof(uint32_t); if (spdm_request->chunk_seq_no != 0 || (spdm_request->chunk_size @@ -112,6 +116,7 @@ libspdm_return_t libspdm_get_response_chunk_send(libspdm_context_t *spdm_context || spdm_request->chunk_size > calc_max_chunk_size || (uint32_t)request_size > spdm_context->local_context.capability.data_transfer_size || large_message_size > spdm_context->local_context.capability.max_spdm_msg_size + || large_message_size > max_chunk_data_transfer_size || large_message_size <= SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12 || (spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK) ) { @@ -160,6 +165,9 @@ libspdm_return_t libspdm_get_response_chunk_send(libspdm_context_t *spdm_context || ((uint32_t) request_size > spdm_context->local_context.capability.data_transfer_size))) { status = LIBSPDM_STATUS_INVALID_MSG_FIELD; + } else if (spdm_request->chunk_seq_no == 0) { + /* Chunk seq no wrapped */ + status = LIBSPDM_STATUS_INVALID_MSG_FIELD; } else { libspdm_copy_mem( diff --git a/unit_test/test_spdm_responder/chunk_send_ack.c b/unit_test/test_spdm_responder/chunk_send_ack.c index f392daa61fb..c5628acbc3a 100644 --- a/unit_test/test_spdm_responder/chunk_send_ack.c +++ b/unit_test/test_spdm_responder/chunk_send_ack.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2022 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -1705,6 +1705,165 @@ void libspdm_test_responder_chunk_send_ack_rsp_case19(void** state) libspdm_test_responder_chunk_send_ack_reset_send_state(spdm_context); } +/** + * Test 20: Request chunk seq wrapped. + **/ +void libspdm_test_responder_chunk_send_ack_rsp_case20(void** state) +{ + libspdm_return_t status; + + libspdm_test_context_t* spdm_test_context; + libspdm_context_t* spdm_context; + + size_t request_size; + size_t response_size; + + uint8_t request[LIBSPDM_MAX_SPDM_MSG_SIZE]; + uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE]; + + spdm_chunk_send_request_t* chunk_send_request; + spdm_chunk_send_ack_response_t* chunk_send_ack_response; + spdm_error_response_t* error_response; + + const uint8_t* chunk_src; + uint8_t* chunk_dst; + + /* Format the last chunk. */ + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 20; + + libspdm_test_responder_chunk_send_ack_setup_algo_state(spdm_context); + spdm_context->chunk_context.send.chunk_in_use = true; + spdm_context->chunk_context.send.chunk_handle = (uint8_t) spdm_test_context->case_id; + spdm_context->chunk_context.send.chunk_seq_no = 65535; /* maximum chunk seq received */ + spdm_context->chunk_context.send.large_message_size = 0x10000; + spdm_context->chunk_context.send.chunk_bytes_transferred = 0x8000; + + chunk_src = (const uint8_t*) &m_libspdm_chunk_send_negotiate_algorithm_request1; + + libspdm_zero_mem(request, sizeof(request)); + chunk_send_request = (spdm_chunk_send_request_t*) request; + + chunk_send_request->header.spdm_version = SPDM_MESSAGE_VERSION_12; + chunk_send_request->header.request_response_code = SPDM_CHUNK_SEND; + chunk_send_request->header.param1 = 0; + chunk_send_request->header.param2 = (uint8_t) spdm_test_context->case_id; /* chunk_handle */ + chunk_send_request->chunk_seq_no = 0; /* chunk seq wrapped */ + + chunk_send_request->chunk_size = + spdm_context->local_context.capability.data_transfer_size + - sizeof(spdm_chunk_send_request_t); + + chunk_dst = ((uint8_t*) (chunk_send_request + 1)); + + request_size = sizeof(spdm_chunk_send_request_t) + + chunk_send_request->chunk_size; + + libspdm_copy_mem(chunk_dst, chunk_send_request->chunk_size, + chunk_src, chunk_send_request->chunk_size); + + response_size = sizeof(response); + status = libspdm_get_response_chunk_send( + spdm_context, + request_size, request, + &response_size, response); + + assert_int_equal(status, LIBSPDM_STATUS_INVALID_MSG_FIELD); + assert_true(response_size == sizeof(spdm_chunk_send_ack_response_t) + + sizeof(spdm_error_response_t)); + + chunk_send_ack_response = (spdm_chunk_send_ack_response_t*) response; + assert_int_equal(chunk_send_ack_response->header.param1, + SPDM_CHUNK_SEND_ACK_RESPONSE_ATTRIBUTE_EARLY_ERROR_DETECTED); + + error_response = (spdm_error_response_t*) (chunk_send_ack_response + 1); + assert_int_equal(error_response->header.spdm_version, SPDM_MESSAGE_VERSION_12); + assert_int_equal(error_response->header.request_response_code, SPDM_ERROR); + assert_int_equal(error_response->header.param1, SPDM_ERROR_CODE_INVALID_REQUEST); + assert_int_equal(error_response->header.param2, 0); + + libspdm_test_responder_chunk_send_ack_reset_send_state(spdm_context); +} + +/** + * Test 21: Request size exceed max chunk seq no. + **/ +void libspdm_test_responder_chunk_send_ack_rsp_case21(void** state) +{ + libspdm_return_t status; + + libspdm_test_context_t* spdm_test_context; + libspdm_context_t* spdm_context; + + size_t request_size; + size_t response_size; + + uint8_t request[LIBSPDM_MAX_SPDM_MSG_SIZE]; + uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE]; + + spdm_chunk_send_request_t* chunk_send_request; + spdm_chunk_send_ack_response_t* chunk_send_ack_response; + spdm_error_response_t* error_response; + + const uint8_t* chunk_src; + uint8_t* chunk_dst; + + /* Format the last chunk. */ + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 21; + + libspdm_test_responder_chunk_send_ack_setup_algo_state(spdm_context); + chunk_src = (const uint8_t*) &m_libspdm_chunk_send_negotiate_algorithm_request1; + + libspdm_zero_mem(request, sizeof(request)); + chunk_send_request = (spdm_chunk_send_request_t*) request; + + chunk_send_request->header.spdm_version = SPDM_MESSAGE_VERSION_12; + chunk_send_request->header.request_response_code = SPDM_CHUNK_SEND; + chunk_send_request->header.param1 = 0; + chunk_send_request->header.param2 = (uint8_t) spdm_test_context->case_id; /* chunk_handle */ + chunk_send_request->chunk_seq_no = 0; + + + *((uint32_t*) (chunk_send_request + 1)) = 0x1000000; /* a size exceed max chunk seq */ + chunk_send_request->chunk_size = + spdm_context->local_context.capability.data_transfer_size + - sizeof(spdm_chunk_send_request_t) - sizeof(uint32_t); + + chunk_dst = ((uint8_t*) (chunk_send_request + 1)) + sizeof(uint32_t); + + request_size = sizeof(spdm_chunk_send_request_t) + + sizeof(uint32_t) + + chunk_send_request->chunk_size; + + libspdm_copy_mem(chunk_dst, chunk_send_request->chunk_size, + chunk_src, chunk_send_request->chunk_size); + + response_size = sizeof(response); + status = libspdm_get_response_chunk_send( + spdm_context, + request_size, request, + &response_size, response); + + assert_int_equal(status, LIBSPDM_STATUS_INVALID_MSG_FIELD); + assert_true(response_size == sizeof(spdm_chunk_send_ack_response_t) + + sizeof(spdm_error_response_t)); + + chunk_send_ack_response = (spdm_chunk_send_ack_response_t*) response; + assert_int_equal(chunk_send_ack_response->header.param1, + SPDM_CHUNK_SEND_ACK_RESPONSE_ATTRIBUTE_EARLY_ERROR_DETECTED); + + error_response = (spdm_error_response_t*) (chunk_send_ack_response + 1); + assert_int_equal(error_response->header.spdm_version, SPDM_MESSAGE_VERSION_12); + assert_int_equal(error_response->header.request_response_code, SPDM_ERROR); + assert_int_equal(error_response->header.param1, SPDM_ERROR_CODE_INVALID_REQUEST); + assert_int_equal(error_response->header.param2, 0); + + libspdm_test_responder_chunk_send_ack_reset_send_state(spdm_context); +} + int libspdm_responder_chunk_send_ack_test_main(void) { const struct CMUnitTest spdm_responder_chunk_send_ack_tests[] = { @@ -1750,6 +1909,10 @@ int libspdm_responder_chunk_send_ack_test_main(void) cmocka_unit_test(libspdm_test_responder_chunk_send_ack_rsp_case18), /* Request missing LAST_CHUNK when request size != data transfer size. */ cmocka_unit_test(libspdm_test_responder_chunk_send_ack_rsp_case19), + /* Request chunk seq warpped. */ + cmocka_unit_test(libspdm_test_responder_chunk_send_ack_rsp_case20), + /* Request size exceed max chunk seq no. */ + cmocka_unit_test(libspdm_test_responder_chunk_send_ack_rsp_case21), }; libspdm_test_context_t test_context = { From 8d1e90048a7b2f290e9f2a7362cb6e0fc2261b15 Mon Sep 17 00:00:00 2001 From: Aaron Li Date: Wed, 8 Jan 2025 14:19:20 +0800 Subject: [PATCH 3/4] Fix chunk_seq_no wrap in chunk get. Fix #2875 Signed-off-by: Aaron Li --- .../libspdm_req_handle_error_response.c | 17 +++- unit_test/test_spdm_requester/chunk_get.c | 93 ++++++++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/library/spdm_requester_lib/libspdm_req_handle_error_response.c b/library/spdm_requester_lib/libspdm_req_handle_error_response.c index 262b844a079..8a135d4a7f0 100644 --- a/library/spdm_requester_lib/libspdm_req_handle_error_response.c +++ b/library/spdm_requester_lib/libspdm_req_handle_error_response.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -225,6 +225,7 @@ libspdm_return_t libspdm_handle_error_large_response( size_t large_response_capacity; size_t large_response_size; size_t large_response_size_so_far; + uint64_t max_chunk_data_transfer_size; if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_12) { return LIBSPDM_STATUS_UNSUPPORTED_CAP; @@ -262,6 +263,10 @@ libspdm_return_t libspdm_handle_error_large_response( message = scratch_buffer + libspdm_get_scratch_buffer_sender_receiver_offset(spdm_context); message_size = libspdm_get_scratch_buffer_sender_receiver_capacity(spdm_context); + max_chunk_data_transfer_size = + ((size_t) spdm_context->local_context.capability.data_transfer_size + - sizeof(spdm_chunk_response_response_t)) * 65536 - sizeof(uint32_t); + libspdm_zero_mem(large_response, large_response_capacity); large_response_size = 0; large_response_size_so_far = 0; @@ -280,6 +285,12 @@ libspdm_return_t libspdm_handle_error_large_response( spdm_request->chunk_seq_no = chunk_seq_no; spdm_request_size = sizeof(spdm_chunk_get_request_t); + if (chunk_seq_no == 0 && large_response_size_so_far != 0) { + /* chunk_seq_no wrapped */ + status = LIBSPDM_STATUS_INVALID_MSG_FIELD; + break; + } + LIBSPDM_DEBUG((LIBSPDM_DEBUG_INFO, "CHUNK_GET Handle %d SeqNo %d\n", chunk_handle, chunk_seq_no)); @@ -346,6 +357,10 @@ libspdm_return_t libspdm_handle_error_large_response( status = LIBSPDM_STATUS_INVALID_MSG_FIELD; break; } + if (large_response_size > max_chunk_data_transfer_size) { + status = LIBSPDM_STATUS_INVALID_MSG_FIELD; + break; + } if (large_response_size <= SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12) { status = LIBSPDM_STATUS_INVALID_MSG_FIELD; break; diff --git a/unit_test/test_spdm_requester/chunk_get.c b/unit_test/test_spdm_requester/chunk_get.c index 90827206948..68873710b43 100644 --- a/unit_test/test_spdm_requester/chunk_get.c +++ b/unit_test/test_spdm_requester/chunk_get.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -15,6 +15,10 @@ static size_t m_libspdm_local_certificate_chain_size_test_case_1; static uint8_t m_libspdm_local_large_response_buffer[LIBSPDM_MAX_SPDM_MSG_SIZE]; +#define BUFFER_SIZE_FOR_CHUNK_SEQ_NO_WRAP_TEST 0x200000 +static uint8_t m_libspdm_local_response_buffer_for_chunk_seq_no_wrap_test[ + BUFFER_SIZE_FOR_CHUNK_SEQ_NO_WRAP_TEST]; + static size_t m_libspdm_local_buffer_size; static uint8_t m_libspdm_local_buffer[LIBSPDM_MAX_MESSAGE_M1M2_BUFFER_SIZE]; @@ -259,6 +263,29 @@ void libspdm_requester_chunk_get_test_case4_build_digest_response( spdm_response->header.param2 |= (0xFF << 0); } +void libspdm_requester_chunk_get_test_case5_build_vendor_response( + void* context, void* response, size_t* response_size) +{ + spdm_vendor_defined_response_msg_t *spdm_response; + + /* For exceed max chunk seq no */ + *response_size = + (CHUNK_GET_REQUESTER_UNIT_TEST_DATA_TRANSFER_SIZE - + sizeof(spdm_chunk_response_response_t)) * 65536 - sizeof(uint32_t) + 0x10; + + libspdm_set_mem(response, *response_size, 0xff); + + spdm_response = response; + + spdm_response->header.spdm_version = SPDM_MESSAGE_VERSION_12; + spdm_response->header.request_response_code = SPDM_VENDOR_DEFINED_RESPONSE; + spdm_response->header.param1 = 0; + spdm_response->header.param2 = 0; + + spdm_response->standard_id = 6; + spdm_response->len = 2; +} + libspdm_return_t libspdm_requester_chunk_get_test_send_message( void* spdm_context, size_t request_size, const void* request, uint64_t timeout) @@ -431,6 +458,9 @@ libspdm_return_t libspdm_requester_chunk_get_test_receive_message( } else if (spdm_test_context->case_id == 0x4) { build_response_func = libspdm_requester_chunk_get_test_case4_build_digest_response; + } else if (spdm_test_context->case_id == 0x5) { + build_response_func = + libspdm_requester_chunk_get_test_case5_build_vendor_response; } else { LIBSPDM_ASSERT(0); return LIBSPDM_STATUS_RECEIVE_FAIL; @@ -453,6 +483,12 @@ libspdm_return_t libspdm_requester_chunk_get_test_receive_message( sub_rsp = (spdm_message_header_t*) m_libspdm_local_large_response_buffer; sub_rsp_size = sizeof(m_libspdm_local_large_response_buffer); + if (spdm_test_context->case_id == 0x5) { + sub_rsp = + (spdm_message_header_t*) + m_libspdm_local_response_buffer_for_chunk_seq_no_wrap_test; + sub_rsp_size = sizeof(m_libspdm_local_response_buffer_for_chunk_seq_no_wrap_test); + } libspdm_zero_mem(sub_rsp, sub_rsp_size); build_response_func(spdm_context, sub_rsp, &sub_rsp_size); @@ -832,6 +868,57 @@ void libspdm_test_requester_chunk_get_case4(void** state) } #endif +#if LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES +static void libspdm_test_requester_chunk_get_case5(void **state) +{ + /* Copied from Vendor Request case 1*/ + libspdm_return_t status; + libspdm_test_context_t *spdm_test_context; + libspdm_context_t *spdm_context; + + uint16_t standard_id = 6; + uint8_t vendor_id_len = 2; + uint8_t vendor_id[SPDM_MAX_VENDOR_ID_LENGTH] = {0xAA, 0xAA}; + uint16_t data_len = 16; + uint8_t data[16]; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 0x5; + spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_12 << + SPDM_VERSION_NUMBER_SHIFT_BIT; + /* Large response need a large scratch buffer. */ + spdm_context->connection_info.capability.max_spdm_msg_size = + BUFFER_SIZE_FOR_CHUNK_SEQ_NO_WRAP_TEST; + spdm_context->local_context.capability.max_spdm_msg_size = + BUFFER_SIZE_FOR_CHUNK_SEQ_NO_WRAP_TEST; + spdm_context->connection_info.connection_state = + LIBSPDM_CONNECTION_STATE_NEGOTIATED; + spdm_context->connection_info.capability.data_transfer_size = + CHUNK_GET_REQUESTER_UNIT_TEST_DATA_TRANSFER_SIZE; + spdm_context->local_context.capability.sender_data_transfer_size = + CHUNK_GET_REQUESTER_UNIT_TEST_DATA_TRANSFER_SIZE; + spdm_context->local_context.is_requester = true; + + spdm_test_context->scratch_buffer_size = + libspdm_get_sizeof_required_scratch_buffer(spdm_context); + spdm_test_context->scratch_buffer = (void *)malloc(spdm_test_context->scratch_buffer_size); + libspdm_set_scratch_buffer (spdm_context, + spdm_test_context->scratch_buffer, + spdm_test_context->scratch_buffer_size); + + libspdm_set_mem(data, sizeof(data), 0xAA); + + status = libspdm_vendor_send_request_receive_response(spdm_context, NULL, + standard_id, vendor_id_len, vendor_id, + data_len, data, + &standard_id, &vendor_id_len, vendor_id, + &data_len, data); + + assert_int_equal(status, LIBSPDM_STATUS_RECEIVE_FAIL); +} +#endif /* LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES */ + int libspdm_requester_chunk_get_test_main(void) { /* Test the CHUNK_GET handlers in various requester handlers */ @@ -851,6 +938,10 @@ int libspdm_requester_chunk_get_test_main(void) #if LIBSPDM_SEND_GET_CERTIFICATE_SUPPORT /* Request Digests */ cmocka_unit_test(libspdm_test_requester_chunk_get_case4), +#endif +#if LIBSPDM_ENABLE_VENDOR_DEFINED_MESSAGES + /* Request Vendor Specific Response and chunk seq no wrapped */ + cmocka_unit_test(libspdm_test_requester_chunk_get_case5), #endif }; From 4c2e63df57839edb26f8032687b742e5100cc729 Mon Sep 17 00:00:00 2001 From: Aaron Li Date: Mon, 13 Jan 2025 09:54:38 +0800 Subject: [PATCH 4/4] Fix chunk_seq_no wrap in chunk get response. Fix #2875 Signed-off-by: Aaron Li --- .../libspdm_rsp_chunk_get.c | 20 +++- unit_test/test_spdm_responder/chunk_get.c | 99 ++++++++++++++++++- 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/library/spdm_responder_lib/libspdm_rsp_chunk_get.c b/library/spdm_responder_lib/libspdm_rsp_chunk_get.c index 0fb140ee0b6..04f957501e3 100644 --- a/library/spdm_responder_lib/libspdm_rsp_chunk_get.c +++ b/library/spdm_responder_lib/libspdm_rsp_chunk_get.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2024 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -17,6 +17,7 @@ libspdm_return_t libspdm_get_response_chunk_get( { libspdm_chunk_info_t* get_info; uint32_t min_data_transfer_size; + uint64_t max_chunk_data_transfer_size; const spdm_chunk_get_request_t* spdm_request; spdm_chunk_response_response_t* spdm_response; @@ -103,12 +104,25 @@ libspdm_return_t libspdm_get_response_chunk_get( response_size, response); } - libspdm_zero_mem(response, *response_size); - min_data_transfer_size = LIBSPDM_MIN( spdm_context->connection_info.capability.data_transfer_size, spdm_context->local_context.capability.sender_data_transfer_size); + /* Fail if exceed max chunks */ + max_chunk_data_transfer_size = + ((size_t) min_data_transfer_size - sizeof(spdm_chunk_response_response_t)) * 65536 - + sizeof(uint32_t); + /* max_spdm_msg_size already checked in caller */ + + if (get_info->large_message_size > max_chunk_data_transfer_size) { + return libspdm_generate_error_response( + spdm_context, + SPDM_ERROR_CODE_RESPONSE_TOO_LARGE, 0, + response_size, response); + } + + libspdm_zero_mem(response, *response_size); + /* Assert the data transfer size is smaller than the response size. * Otherwise there is no reason to chunk this response. */ LIBSPDM_ASSERT(min_data_transfer_size < *response_size); diff --git a/unit_test/test_spdm_responder/chunk_get.c b/unit_test/test_spdm_responder/chunk_get.c index cfc04574195..26017c0b3a4 100644 --- a/unit_test/test_spdm_responder/chunk_get.c +++ b/unit_test/test_spdm_responder/chunk_get.c @@ -1,6 +1,6 @@ /** * Copyright Notice: - * Copyright 2021-2022 DMTF. All rights reserved. + * Copyright 2021-2025 DMTF. All rights reserved. * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md **/ @@ -996,6 +996,101 @@ void libspdm_test_responder_chunk_get_rsp_case13(void** state) } } + +/** + * Test 14: Responder has reponse exceed chunk seq no + **/ +void libspdm_test_responder_chunk_get_rsp_case14(void** state) +{ + libspdm_return_t status; + libspdm_test_context_t* spdm_test_context; + libspdm_context_t* spdm_context; + size_t response_size; + uint8_t response[LIBSPDM_MAX_SPDM_MSG_SIZE]; + spdm_error_response_t* spdm_response; + spdm_chunk_get_request_t spdm_request; + void* scratch_buffer; + size_t scratch_buffer_size; + + uint8_t chunk_handle; + uint32_t data_transfer_size; + uint32_t total_chunk_size; + + spdm_test_context = *state; + spdm_context = spdm_test_context->spdm_context; + spdm_test_context->case_id = 10; + spdm_context->connection_info.version = SPDM_MESSAGE_VERSION_12 << + SPDM_VERSION_NUMBER_SHIFT_BIT; + + spdm_context->connection_info.connection_state = + LIBSPDM_CONNECTION_STATE_NEGOTIATED; + spdm_context->connection_info.algorithm.base_hash_algo = + m_libspdm_use_hash_algo; + + data_transfer_size = CHUNK_GET_RESPONDER_UNIT_TEST_DATA_TRANSFER_SIZE; + spdm_context->local_context.capability.data_transfer_size = data_transfer_size; + spdm_context->local_context.capability.flags |= + SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP; + spdm_context->connection_info.capability.flags |= + SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP; + + /* large response need a large scratch buffer */ + spdm_context->connection_info.capability.max_spdm_msg_size = data_transfer_size * 65536; + spdm_context->local_context.capability.max_spdm_msg_size = data_transfer_size * 65536; + spdm_test_context->scratch_buffer_size = + libspdm_get_sizeof_required_scratch_buffer(spdm_context); + spdm_test_context->scratch_buffer = (void *)malloc(spdm_test_context->scratch_buffer_size); + libspdm_set_scratch_buffer (spdm_context, + spdm_test_context->scratch_buffer, + spdm_test_context->scratch_buffer_size); + + + libspdm_get_scratch_buffer(spdm_context, &scratch_buffer, &scratch_buffer_size); + + scratch_buffer = (uint8_t*)scratch_buffer + + libspdm_get_scratch_buffer_large_message_offset(spdm_context); + scratch_buffer_size = scratch_buffer_size - + libspdm_get_scratch_buffer_large_message_offset(spdm_context); + libspdm_zero_mem(scratch_buffer, scratch_buffer_size); + + /* a huge chunk size to cause the chunk seq no wrap */ + total_chunk_size = data_transfer_size * 65536; + + LIBSPDM_ASSERT(total_chunk_size <= scratch_buffer_size); + + chunk_handle = (uint8_t) spdm_test_context->case_id; /* Any number is fine */ + spdm_context->chunk_context.get.chunk_in_use = true; + spdm_context->chunk_context.get.chunk_handle = chunk_handle; + spdm_context->chunk_context.get.chunk_seq_no = 0; + spdm_context->chunk_context.get.large_message = scratch_buffer; + spdm_context->chunk_context.get.large_message_size = total_chunk_size; + spdm_context->chunk_context.get.chunk_bytes_transferred = 0; + + libspdm_zero_mem(&spdm_request, sizeof(spdm_request)); + spdm_request.header.spdm_version = SPDM_MESSAGE_VERSION_12; + spdm_request.header.request_response_code = SPDM_CHUNK_GET; + spdm_request.header.param1 = 0; + spdm_request.header.param2 = chunk_handle; + spdm_request.chunk_seq_no = 0; + + response_size = sizeof(response); + status = libspdm_get_response_chunk_get( + spdm_context, + sizeof(spdm_request), &spdm_request, + &response_size, response); + + assert_int_equal(status, LIBSPDM_STATUS_SUCCESS); + assert_int_equal(response_size, sizeof(spdm_error_response_t)); + + spdm_response = (spdm_error_response_t*) response; + + assert_int_equal(spdm_response->header.spdm_version, SPDM_MESSAGE_VERSION_12); + assert_int_equal(spdm_response->header.request_response_code, SPDM_ERROR); + assert_int_equal(spdm_response->header.param1, SPDM_ERROR_CODE_RESPONSE_TOO_LARGE); + assert_int_equal(spdm_response->header.param2, 0); +} + + int libspdm_responder_chunk_get_rsp_test_main(void) { const struct CMUnitTest spdm_responder_chunk_get_tests[] = { @@ -1025,6 +1120,8 @@ int libspdm_responder_chunk_get_rsp_test_main(void) cmocka_unit_test(libspdm_test_responder_chunk_get_rsp_case12), /* Successful request of last chunk where chunk size is exactly 1 byte */ cmocka_unit_test(libspdm_test_responder_chunk_get_rsp_case13), + /* Responder has reponse exceed chunk seq no */ + cmocka_unit_test(libspdm_test_responder_chunk_get_rsp_case14), }; libspdm_test_context_t test_context = {