Skip to content

Commit 7428c2a

Browse files
committed
net: openthread: rpc: fix handle leak in CoAP RPC server
1. Fix message handle leak after sending a CoAP response. 2. Add unit tests to verify that handle is allocated/freed. 3. Improve CoAP RPC server thread safety. 4. Other minor fixes and improvements. Signed-off-by: Damian Krolik <damian.krolik@nordicsemi.no>
1 parent 4bd5e19 commit 7428c2a

File tree

3 files changed

+80
-28
lines changed

3 files changed

+80
-28
lines changed

subsys/net/openthread/rpc/server/ot_rpc_coap.c

+22-21
Original file line numberDiff line numberDiff line change
@@ -71,23 +71,18 @@ static void ot_rpc_cmd_coap_new_message(const struct nrf_rpc_group *group,
7171

7272
openthread_api_mutex_lock(openthread_get_default_context());
7373
message = otCoapNewMessage(openthread_get_default_instance(), settings);
74-
openthread_api_mutex_unlock(openthread_get_default_context());
75-
76-
if (!message) {
77-
goto out;
78-
}
79-
8074
message_rep = ot_reg_msg_alloc(message);
8175

82-
if (!message_rep) {
76+
if ((message != NULL) && !message_rep) {
8377
/*
84-
* If failed to allocate the message handle, the ownership can't be passed to the
85-
* RPC client. Therefore, free the message.
78+
* Failed to allocate the message handle, so the ownership can't be passed to
79+
* the RPC client. Therefore, free the message.
8680
*/
8781
otMessageFree(message);
8882
}
8983

90-
out:
84+
openthread_api_mutex_unlock(openthread_get_default_context());
85+
9186
nrf_rpc_rsp_send_uint(group, message_rep);
9287
}
9388

@@ -180,7 +175,7 @@ static void ot_rpc_cmd_coap_message_append_uri_path_options(const struct nrf_rpc
180175
message_rep = nrf_rpc_decode_uint(ctx);
181176
nrf_rpc_decode_str(ctx, uri, sizeof(uri));
182177

183-
if (!nrf_rpc_decode_valid(ctx)) {
178+
if (!nrf_rpc_decoding_done_and_check(group, ctx)) {
184179
ot_rpc_report_cmd_decoding_error(OT_RPC_CMD_COAP_MESSAGE_APPEND_URI_PATH_OPTIONS);
185180
return;
186181
}
@@ -194,7 +189,6 @@ static void ot_rpc_cmd_coap_message_append_uri_path_options(const struct nrf_rpc
194189

195190
openthread_api_mutex_lock(openthread_get_default_context());
196191
error = otCoapMessageAppendUriPathOptions(message, uri);
197-
nrf_rpc_cbor_decoding_done(group, ctx);
198192
openthread_api_mutex_unlock(openthread_get_default_context());
199193

200194
nrf_rpc_rsp_send_uint(group, error);
@@ -455,6 +449,8 @@ static void ot_rpc_coap_resource_handler(void *aContext, otMessage *aMessage,
455449
message_rep = ot_reg_msg_alloc(aMessage);
456450

457451
if (!message_rep) {
452+
nrf_rpc_err(-ENOMEM, NRF_RPC_ERR_SRC_SEND, &ot_group,
453+
OT_RPC_CMD_COAP_RESOURCE_HANDLER, NRF_RPC_PACKET_TYPE_CMD);
458454
return;
459455
}
460456

@@ -559,6 +555,8 @@ static void ot_rpc_coap_default_handler(void *aContext, otMessage *aMessage,
559555
message_rep = ot_reg_msg_alloc(aMessage);
560556

561557
if (!message_rep) {
558+
nrf_rpc_err(-ENOMEM, NRF_RPC_ERR_SRC_SEND, &ot_group,
559+
OT_RPC_CMD_COAP_DEFAULT_HANDLER, NRF_RPC_PACKET_TYPE_CMD);
562560
return;
563561
}
564562

@@ -606,13 +604,12 @@ static void ot_rpc_coap_response_handler(void *context, otMessage *message,
606604
struct nrf_rpc_cbor_ctx ctx;
607605
size_t cbor_buffer_size = 0;
608606

609-
if (message) {
610-
message_rep = ot_reg_msg_alloc(message);
611-
612-
if (!message_rep) {
613-
return;
614-
}
615-
}
607+
message_rep = ot_reg_msg_alloc(message);
608+
/*
609+
* Ignore message handle allocation failure. It seems safer to call the client's response
610+
* handler without the response (which indicates response timeout) than not to call the
611+
* handler at all and make the client wait for the response indefinitely.
612+
*/
616613

617614
cbor_buffer_size += 1 + sizeof(ot_rpc_coap_request_key);
618615
cbor_buffer_size += 1 + sizeof(ot_msg_key);
@@ -657,12 +654,12 @@ static void ot_rpc_cmd_coap_send_request(const struct nrf_rpc_group *group,
657654
openthread_api_mutex_lock(openthread_get_default_context());
658655
error = otCoapSendRequest(openthread_get_default_instance(), message, &message_info,
659656
ot_rpc_coap_response_handler, (void *)request_rep);
660-
openthread_api_mutex_unlock(openthread_get_default_context());
661657

662658
if (error == OT_ERROR_NONE) {
663659
ot_msg_free(message_rep);
664660
}
665661

662+
openthread_api_mutex_unlock(openthread_get_default_context());
666663
nrf_rpc_rsp_send_uint(group, error);
667664
}
668665

@@ -694,8 +691,12 @@ static void ot_rpc_cmd_coap_send_response(const struct nrf_rpc_group *group,
694691

695692
openthread_api_mutex_lock(openthread_get_default_context());
696693
error = otCoapSendResponse(openthread_get_default_instance(), message, &message_info);
697-
openthread_api_mutex_unlock(openthread_get_default_context());
698694

695+
if (error == OT_ERROR_NONE) {
696+
ot_msg_free(message_rep);
697+
}
698+
699+
openthread_api_mutex_unlock(openthread_get_default_context());
699700
nrf_rpc_rsp_send_uint(group, error);
700701
}
701702

subsys/net/openthread/rpc/server/ot_rpc_message.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ ot_msg_key ot_reg_msg_alloc(otMessage *msg)
3737

3838
void ot_msg_free(ot_msg_key key)
3939
{
40-
key--;
41-
if (key >= OT_MESSAGES_POOL) {
40+
if (key == 0 || key > OT_MESSAGES_POOL) {
4241
return;
4342
}
4443

44+
key--;
45+
4546
if (ot_message_registry[key] != NULL) {
4647
ot_message_registry[key] = NULL;
4748
}

tests/subsys/net/openthread/rpc/server/src/coap_suite.c

+55-5
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ ZTEST(ot_rpc_coap, test_otCoapSetDefaultHandler)
454454

455455
/*
456456
* Test reception of otCoapSendRequest().
457+
* Test serialization of the result: OT_ERROR_NONE.
458+
* Test response handler invocation.
457459
*/
458460
ZTEST(ot_rpc_coap, test_otCoapSendRequest)
459461
{
@@ -484,9 +486,9 @@ ZTEST(ot_rpc_coap, test_otCoapSendRequest)
484486
mock_nrf_rpc_tr_expect_done();
485487

486488
zassert_equal(otCoapSendRequestWithParameters_fake.call_count, 1);
487-
zassert_equal(otCoapSendRequestWithParameters_fake.arg1_val, (otMessage *)MSG_ADDR);
488-
zassert_not_null(otCoapSendRequestWithParameters_fake.arg2_val);
489-
zassert_not_null(otCoapSendRequestWithParameters_fake.arg3_val);
489+
zexpect_equal(otCoapSendRequestWithParameters_fake.arg1_val, (otMessage *)MSG_ADDR);
490+
zexpect_not_null(otCoapSendRequestWithParameters_fake.arg2_val);
491+
zexpect_not_null(otCoapSendRequestWithParameters_fake.arg3_val);
490492

491493
/* Test serialization of the response handler call */
492494
mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_COAP_RESPONSE_HANDLER, request_rep,
@@ -498,22 +500,70 @@ ZTEST(ot_rpc_coap, test_otCoapSendRequest)
498500
mock_nrf_rpc_tr_expect_done();
499501
}
500502

503+
/*
504+
* Test reception of otCoapSendRequest().
505+
* Test serialization of the result: OT_ERROR_INVALID_STATE.
506+
* Test that the message handle has not been released on failure.
507+
*/
508+
ZTEST(ot_rpc_coap, test_otCoapSendRequest_failed)
509+
{
510+
ot_rpc_coap_request_key request_rep = 3;
511+
ot_msg_key message_rep = ot_reg_msg_alloc((otMessage *)MSG_ADDR);
512+
513+
otCoapSendRequestWithParameters_fake.return_val = OT_ERROR_INVALID_STATE;
514+
515+
mock_nrf_rpc_tr_expect_add(RPC_RSP(OT_ERROR_INVALID_STATE), NO_RSP);
516+
mock_nrf_rpc_tr_receive(
517+
RPC_CMD(OT_RPC_CMD_COAP_SEND_REQUEST, message_rep, CBOR_MSG_INFO, request_rep));
518+
mock_nrf_rpc_tr_expect_done();
519+
520+
zassert_equal(otCoapSendRequestWithParameters_fake.call_count, 1);
521+
zexpect_equal(otCoapSendRequestWithParameters_fake.arg1_val, (otMessage *)MSG_ADDR);
522+
zexpect_not_null(otCoapSendRequestWithParameters_fake.arg2_val);
523+
zexpect_not_null(otCoapSendRequestWithParameters_fake.arg3_val);
524+
zassert_not_null(ot_msg_get(message_rep));
525+
}
526+
501527
/*
502528
* Test reception of otCoapSendResponse().
529+
* Test serialization of the result: OT_ERROR_NONE.
530+
* Test that the response message handle has been released on success.
503531
*/
504532
ZTEST(ot_rpc_coap, test_otCoapSendResponse)
505533
{
506534
ot_msg_key message_rep = ot_reg_msg_alloc((otMessage *)MSG_ADDR);
507535

536+
otCoapSendResponseWithParameters_fake.return_val = OT_ERROR_NONE;
537+
538+
mock_nrf_rpc_tr_expect_add(RPC_RSP(OT_ERROR_NONE), NO_RSP);
539+
mock_nrf_rpc_tr_receive(RPC_CMD(OT_RPC_CMD_COAP_SEND_RESPONSE, message_rep, CBOR_MSG_INFO));
540+
mock_nrf_rpc_tr_expect_done();
541+
542+
zassert_equal(otCoapSendResponseWithParameters_fake.call_count, 1);
543+
zexpect_equal(otCoapSendResponseWithParameters_fake.arg1_val, (otMessage *)MSG_ADDR);
544+
zexpect_not_null(otCoapSendResponseWithParameters_fake.arg2_val);
545+
zassert_is_null(ot_msg_get(message_rep));
546+
}
547+
548+
/*
549+
* Test reception of otCoapSendResponse().
550+
* Test serialization of the result: OT_ERROR_INVALID_STATE.
551+
* Test that the response message handle hasn't been released on failure.
552+
*/
553+
ZTEST(ot_rpc_coap, test_otCoapSendResponse_failed)
554+
{
555+
ot_msg_key message_rep = ot_reg_msg_alloc((otMessage *)MSG_ADDR);
556+
508557
otCoapSendResponseWithParameters_fake.return_val = OT_ERROR_INVALID_STATE;
509558

510559
mock_nrf_rpc_tr_expect_add(RPC_RSP(OT_ERROR_INVALID_STATE), NO_RSP);
511560
mock_nrf_rpc_tr_receive(RPC_CMD(OT_RPC_CMD_COAP_SEND_RESPONSE, message_rep, CBOR_MSG_INFO));
512561
mock_nrf_rpc_tr_expect_done();
513562

514563
zassert_equal(otCoapSendResponseWithParameters_fake.call_count, 1);
515-
zassert_equal(otCoapSendResponseWithParameters_fake.arg1_val, (otMessage *)MSG_ADDR);
516-
zassert_not_null(otCoapSendResponseWithParameters_fake.arg2_val);
564+
zexpect_equal(otCoapSendResponseWithParameters_fake.arg1_val, (otMessage *)MSG_ADDR);
565+
zexpect_not_null(otCoapSendResponseWithParameters_fake.arg2_val);
566+
zassert_not_null(ot_msg_get(message_rep));
517567
}
518568

519569
ZTEST_SUITE(ot_rpc_coap, NULL, NULL, tc_setup, NULL, NULL);

0 commit comments

Comments
 (0)