Skip to content

Commit 2e2281a

Browse files
authored
Fix: NVM Read: Enforce Objects Boundaries (#182)
* nvm_flash: checks object boundaries in reads * test: nvm: test object bounds check in nvm reads
1 parent 8d579d6 commit 2e2281a

File tree

4 files changed

+152
-39
lines changed

4 files changed

+152
-39
lines changed

src/wh_nvm_flash.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,16 @@ static int nfObject_ReadDataBytes(whNvmFlashContext* context, int partition,
665665
start = context->directory.objects[object_index].state.start;
666666
startOffset = nfPartition_DataOffset(context, partition) + start;
667667

668+
/* object bounds checks, do both to avoid integer overflow checks */
669+
if (byte_offset >=
670+
(context->directory.objects[object_index].metadata.len)) {
671+
return WH_ERROR_BADARGS;
672+
}
673+
if ((byte_offset + byte_count) >
674+
(context->directory.objects[object_index].metadata.len)) {
675+
return WH_ERROR_BADARGS;
676+
}
677+
668678
/* Ensure we don't read off the end of the active partition */
669679
if (WH_ERROR_OK != nfPartition_CheckDataRange(context, partition,
670680
startOffset * WHFU_BYTES_PER_UNIT + byte_offset,

src/wh_server_cert.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id,
228228
uint8_t* cert, uint32_t* inout_cert_len)
229229
{
230230
int rc;
231-
whNvmSize userLen;
232231
whNvmMetadata meta;
233232

234233
if ((server == NULL) || (cert == NULL) || (inout_cert_len == NULL) ||
@@ -243,17 +242,16 @@ int wh_Server_CertReadTrusted(whServerContext* server, whNvmId id,
243242
return rc;
244243
}
245244

246-
/* Clamp the input length to the actual length of the certificate. This will
247-
* be reflected back to the user on length mismatch failure */
248-
userLen = *inout_cert_len;
249-
*inout_cert_len = meta.len;
250-
251245
/* Check if the provided buffer is large enough */
252-
if (meta.len > userLen) {
246+
if (meta.len > *inout_cert_len) {
253247
return WH_ERROR_BUFFER_SIZE;
254248
}
255249

256-
return wh_Nvm_Read(server->nvm, id, 0, userLen, cert);
250+
/* Clamp the input length to the actual length of the certificate. This will
251+
* be reflected back to the user on length mismatch failure */
252+
*inout_cert_len = meta.len;
253+
254+
return wh_Nvm_Read(server->nvm, id, 0, meta.len, cert);
257255
}
258256

259257
/* Verify a certificate against trusted certificates */

src/wh_server_nvm.c

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,46 @@
4343
#include "wolfhsm/wh_server.h"
4444
#include "wolfhsm/wh_server_nvm.h"
4545

46+
/* Handle NVM read, do access checking and clamping */
47+
static int _HandleNvmRead(whServerContext* server, uint8_t* out_data,
48+
whNvmSize offset, whNvmSize len, whNvmSize* out_len,
49+
whNvmId id)
50+
{
51+
whNvmMetadata meta;
52+
int32_t rc;
53+
54+
if ((server == NULL) || (out_data == NULL) || (out_len == NULL)) {
55+
return WH_ERROR_BADARGS;
56+
}
57+
58+
if (len > WH_MESSAGE_NVM_MAX_READ_LEN) {
59+
return WH_ERROR_ABORTED;
60+
}
61+
62+
rc = wh_Nvm_GetMetadata(server->nvm, id, &meta);
63+
if (rc != WH_ERROR_OK) {
64+
return rc;
65+
}
66+
67+
if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) {
68+
return WH_ERROR_ACCESS;
69+
}
70+
71+
if (offset >= meta.len)
72+
return WH_ERROR_BADARGS;
73+
74+
/* Clamp length to object size */
75+
if ((offset + len) > meta.len) {
76+
len = meta.len - offset;
77+
}
78+
79+
rc = wh_Nvm_Read(server->nvm, id, offset, len, out_data);
80+
if (rc != WH_ERROR_OK)
81+
return rc;
82+
*out_len = len;
83+
return WH_ERROR_OK;
84+
}
85+
4686
int wh_Server_HandleNvmRequest(whServerContext* server,
4787
uint16_t magic, uint16_t action, uint16_t seq,
4888
uint16_t req_size, const void* req_packet,
@@ -240,44 +280,30 @@ int wh_Server_HandleNvmRequest(whServerContext* server,
240280

241281
case WH_MESSAGE_NVM_ACTION_READ:
242282
{
243-
whMessageNvm_ReadRequest req = {0};
244-
whMessageNvm_ReadResponse resp = {0};
245-
uint16_t hdr_len = sizeof(resp);
246-
uint8_t* data = (uint8_t*)resp_packet + hdr_len;
247-
uint16_t data_len = 0;
248-
whNvmMetadata meta = {0};
283+
whMessageNvm_ReadRequest req = {0};
284+
whMessageNvm_ReadResponse resp = {0};
285+
uint16_t hdr_len = sizeof(resp);
286+
uint8_t* data = (uint8_t*)resp_packet + hdr_len;
287+
whNvmSize data_len = 0;
249288

250289
if (req_size != sizeof(req)) {
251290
/* Request is malformed */
252291
resp.rc = WH_ERROR_ABORTED;
253-
} else {
292+
}
293+
else {
254294
/* Convert request struct */
255-
wh_MessageNvm_TranslateReadRequest(magic,
256-
(whMessageNvm_ReadRequest*)req_packet, &req);
295+
wh_MessageNvm_TranslateReadRequest(
296+
magic, (whMessageNvm_ReadRequest*)req_packet, &req);
257297

258-
if (req.data_len > WH_MESSAGE_NVM_MAX_READ_LEN) {
259-
resp.rc = WH_ERROR_ABORTED;
260-
} else {
261-
/* Check metadata for non-exportable flag */
262-
resp.rc = wh_Nvm_GetMetadata(server->nvm, req.id, &meta);
263-
if (resp.rc == 0) {
264-
if (meta.flags & WH_NVM_FLAGS_NONEXPORTABLE) {
265-
resp.rc = WH_ERROR_ACCESS;
266-
}
267-
else {
268-
/* Process the Read action */
269-
resp.rc = wh_Nvm_Read(server->nvm, req.id, req.offset,
270-
req.data_len, data);
271-
if (resp.rc == 0) {
272-
data_len = req.data_len;
273-
}
274-
}
275-
}
298+
resp.rc = _HandleNvmRead(server, data, req.offset, req.data_len,
299+
&req.data_len, req.id);
300+
if (resp.rc == WH_ERROR_OK) {
301+
data_len = req.data_len;
276302
}
277303
}
278304
/* Convert the response struct */
279-
wh_MessageNvm_TranslateReadResponse(magic,
280-
&resp, (whMessageNvm_ReadResponse*)resp_packet);
305+
wh_MessageNvm_TranslateReadResponse(
306+
magic, &resp, (whMessageNvm_ReadResponse*)resp_packet);
281307
*out_resp_size = sizeof(resp) + data_len;
282308
}; break;
283309

@@ -338,6 +364,7 @@ int wh_Server_HandleNvmRequest(whServerContext* server,
338364
whMessageNvm_ReadDmaRequest req = {0};
339365
whMessageNvm_SimpleResponse resp = {0};
340366
whNvmMetadata meta = {0};
367+
whNvmSize read_len;
341368
void* data = NULL;
342369

343370
if (req_size != sizeof(req)) {
@@ -355,6 +382,23 @@ int wh_Server_HandleNvmRequest(whServerContext* server,
355382
resp.rc = WH_ERROR_ACCESS;
356383
}
357384
}
385+
386+
if (resp.rc == 0) {
387+
if (req.offset >= meta.len) {
388+
resp.rc = WH_ERROR_BADARGS;
389+
}
390+
}
391+
392+
if (resp.rc == 0) {
393+
read_len = req.data_len;
394+
/* Clamp length to object size */
395+
if ((req.offset + read_len) > meta.len) {
396+
read_len = meta.len - req.offset;
397+
}
398+
}
399+
400+
/* use unclamped length for DMA address processing in case DMA callbacks
401+
* are sensible to alignment and/or size */
358402
if (resp.rc == 0) {
359403
/* perform platform-specific host address processing */
360404
resp.rc = wh_Server_DmaProcessClientAddress(
@@ -363,8 +407,8 @@ int wh_Server_HandleNvmRequest(whServerContext* server,
363407
}
364408
if (resp.rc == 0) {
365409
/* Process the Read action */
366-
resp.rc = wh_Nvm_Read(server->nvm, req.id, req.offset, req.data_len,
367-
(uint8_t*)data);
410+
resp.rc = wh_Nvm_Read(server->nvm, req.id, req.offset, read_len,
411+
(uint8_t*)data);
368412
}
369413
if (resp.rc == 0) {
370414
/* perform platform-specific host address processing */

test/wh_test_clientserver.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,64 @@ static int _clientServerSequentialTestConnectCb(void* context,
669669
connected);
670670
}
671671

672+
static int _testOutOfBoundsNvmReads(whClientContext* client,
673+
whServerContext* server, whNvmId id)
674+
{
675+
uint8_t buffer[WOLFHSM_CFG_COMM_DATA_LEN];
676+
whNvmMetadata meta;
677+
whNvmSize off, len;
678+
int32_t server_rc;
679+
680+
/* Get object metadata */
681+
WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetMetadataRequest(client, id));
682+
WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
683+
WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetMetadataResponse(
684+
client, &server_rc, &meta.id, &meta.access, &meta.flags, &meta.len,
685+
sizeof(meta.label), meta.label));
686+
WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
687+
688+
/* Try to read len + 1 bytes, should clamp to len */
689+
off = 0;
690+
len = meta.len + 1;
691+
WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadRequest(client, id, off, len));
692+
WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
693+
WH_TEST_RETURN_ON_FAIL(
694+
wh_Client_NvmReadResponse(client, &server_rc, &len, buffer));
695+
WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
696+
WH_TEST_ASSERT_RETURN(len == meta.len);
697+
698+
/* Try to read len bytes starting at 1 should clamp to len - 1 */
699+
off = 1;
700+
len = meta.len;
701+
WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadRequest(client, id, off, len));
702+
WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
703+
WH_TEST_RETURN_ON_FAIL(
704+
wh_Client_NvmReadResponse(client, &server_rc, &len, buffer));
705+
WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
706+
WH_TEST_ASSERT_RETURN(len == meta.len - 1);
707+
708+
/* Try to read starting at len - 1 len bytes, should clamp to 1 */
709+
off = meta.len - 1;
710+
len = meta.len;
711+
WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadRequest(client, id, off, len));
712+
WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
713+
WH_TEST_RETURN_ON_FAIL(
714+
wh_Client_NvmReadResponse(client, &server_rc, &len, buffer));
715+
WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
716+
WH_TEST_ASSERT_RETURN(len == 1);
717+
718+
/* Try to read starting at len, should fail */
719+
off = meta.len;
720+
len = 0;
721+
WH_TEST_RETURN_ON_FAIL(wh_Client_NvmReadRequest(client, id, off, len));
722+
WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
723+
WH_TEST_RETURN_ON_FAIL(
724+
wh_Client_NvmReadResponse(client, &server_rc, &len, buffer));
725+
WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_BADARGS);
726+
727+
return WH_ERROR_OK;
728+
}
729+
672730
int whTest_ClientServerSequential(void)
673731
{
674732
int ret = 0;
@@ -1016,6 +1074,9 @@ int whTest_ClientServerSequential(void)
10161074
WH_TEST_ASSERT_RETURN(0 == memcmp(send_buffer, recv_buffer, len));
10171075
}
10181076

1077+
/* Perform out-of-bounds read tests on first object written */
1078+
WH_TEST_RETURN_ON_FAIL(_testOutOfBoundsNvmReads(client, server, 20));
1079+
10191080
whNvmAccess list_access = WH_NVM_ACCESS_ANY;
10201081
whNvmFlags list_flags = WH_NVM_FLAGS_NONE;
10211082
whNvmId list_id = 0;

0 commit comments

Comments
 (0)