@@ -516,6 +516,49 @@ static int _clientServerSequentialTestConnectCb(void* context,
516516 connected );
517517}
518518
519+ /* Drive one INIT cycle on an already-initialized client/server pair to verify
520+ * the server enforces the [0, WH_CLIENT_ID_MAX] bound on client_id (so values
521+ * that would be silently truncated by the 4-bit USER field of whKeyId — e.g.
522+ * 16 aliasing the USER=0 global namespace, 17 aliasing client 1 — are
523+ * rejected at INIT instead of breaking per-client key isolation). Mutates
524+ * client->comm->client_id; caller is responsible for restoring it.
525+ *
526+ * Note: boundary coverage is limited to [0, 255] because
527+ * client->comm->client_id is uint8_t; values above 255 are truncated by the
528+ * host-side comm field before reaching the wire. */
529+ static int _testInitClientIdBoundary (whClientContext * client ,
530+ whServerContext * server ,
531+ uint32_t client_id , int expect_ok )
532+ {
533+ uint32_t resp_client_id = 0 ;
534+ uint32_t resp_server_id = 0 ;
535+ int rc ;
536+ uint8_t prior_server_client_id = server -> comm -> client_id ;
537+
538+ client -> comm -> client_id = (uint8_t )client_id ;
539+
540+ WH_TEST_RETURN_ON_FAIL (wh_Client_CommInitRequest (client ));
541+ WH_TEST_RETURN_ON_FAIL (wh_Server_HandleRequestMessage (server ));
542+ rc = wh_Client_CommInitResponse (client , & resp_client_id , & resp_server_id );
543+
544+ if (expect_ok ) {
545+ WH_TEST_ASSERT_RETURN (rc == WH_ERROR_OK );
546+ WH_TEST_ASSERT_RETURN (resp_client_id == client_id );
547+ }
548+ else {
549+ /* Server returns BADARGS with size=0; client decodes that as ABORTED.
550+ */
551+ WH_TEST_ASSERT_RETURN (rc == WH_ERROR_ABORTED );
552+ /* Rejection must not corrupt server-side identity: a regression that
553+ * moved the assignment in _wh_Server_HandleCommRequest above the
554+ * bound check would still produce ABORTED on the wire but would
555+ * leave server->comm->client_id holding the rejected value. */
556+ WH_TEST_ASSERT_RETURN (server -> comm -> client_id ==
557+ prior_server_client_id );
558+ }
559+ return WH_ERROR_OK ;
560+ }
561+
519562static int _testOutOfBoundsNvmReads (whClientContext * client ,
520563 whServerContext * server , whNvmId id )
521564{
@@ -739,6 +782,24 @@ int whTest_ClientServerSequential(whTestNvmBackendType nvmType)
739782 WH_TEST_RETURN_ON_FAIL (wh_Client_CommInitResponse (client , & client_id , & server_id ));
740783 WH_TEST_ASSERT_RETURN (client_id == client -> comm -> client_id );
741784
785+ /* Verify INIT rejects out-of-range client_id values that would otherwise
786+ * be silently truncated by the 4-bit USER field of whKeyId. */
787+ WH_TEST_RETURN_ON_FAIL (_testInitClientIdBoundary (client , server , 16 , 0 ));
788+ WH_TEST_RETURN_ON_FAIL (_testInitClientIdBoundary (client , server , 17 , 0 ));
789+ WH_TEST_RETURN_ON_FAIL (_testInitClientIdBoundary (client , server , 32 , 0 ));
790+ WH_TEST_RETURN_ON_FAIL (_testInitClientIdBoundary (client , server , 255 , 0 ));
791+ #ifdef WOLFHSM_CFG_GLOBAL_KEYS
792+ /* USER=0 is reserved for global keys */
793+ WH_TEST_RETURN_ON_FAIL (_testInitClientIdBoundary (client , server , 0 , 0 ));
794+ #else
795+ WH_TEST_RETURN_ON_FAIL (_testInitClientIdBoundary (client , server , 0 , 1 ));
796+ #endif
797+ WH_TEST_RETURN_ON_FAIL (
798+ _testInitClientIdBoundary (client , server , WH_CLIENT_ID_MAX , 1 ));
799+ /* Restore default client_id so the rest of the sequential test runs with
800+ * the expected per-client identity on both ends. */
801+ WH_TEST_RETURN_ON_FAIL (_testInitClientIdBoundary (
802+ client , server , WH_TEST_DEFAULT_CLIENT_ID , 1 ));
742803
743804 /* Send the comm info message */
744805 WH_TEST_RETURN_ON_FAIL (wh_Client_CommInfoRequest (client ));
0 commit comments