diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 397a0ecdc6..d40874f399 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -1379,8 +1379,8 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) // Sanitize a local copy rather than const_cast-ing the const input (UB if a truly-const // object is ever passed); the validated copy is assigned into moduleConfig below. auto beaconCfg = c.payload_variant.mesh_beacon; - // Hard cap at 100 chars. - beaconCfg.broadcast_message[100] = '\0'; + // Cap at the generated field size, so the limit follows the proto's max_size. + beaconCfg.broadcast_message[sizeof(beaconCfg.broadcast_message) - 1] = '\0'; // Enforce interval minimum (0 means unset/use default). if (beaconCfg.broadcast_interval_secs != 0 && beaconCfg.broadcast_interval_secs < default_mesh_beacon_min_broadcast_interval_secs) diff --git a/test/test_mesh_beacon/test_main.cpp b/test/test_mesh_beacon/test_main.cpp index bd55c85b07..2809452a09 100644 --- a/test/test_mesh_beacon/test_main.cpp +++ b/test/test_mesh_beacon/test_main.cpp @@ -423,24 +423,24 @@ static void test_adminValidation_targetValidPresetForRegion_isPreserved(void) } /** - * Verify broadcast_message is hard-capped at 100 characters (NUL forced at index 100). - * Important to prevent oversized beacon payloads from abusing airtime across the mesh. + * Verify AdminModule::handleSetModuleConfig() NUL-terminates broadcast_message within the generated field. + * The cap keeps oversized beacon payloads from abusing airtime across the mesh, and it must follow the + * proto's max_size: a hard-coded index 100 outlived the shrink of the field to 61 bytes (60 characters) + * and wrote past the array. The input here is unterminated, so the admin path itself must terminate it. */ -static void test_adminValidation_messageTooLong_isTruncatedAt100(void) +static void test_adminValidation_messageTooLong_isTruncatedToField(void) { resetConfig(); meshtastic_ModuleConfig_MeshBeaconConfig bcfg = meshtastic_ModuleConfig_MeshBeaconConfig_init_zero; - // Fill with 'A' up to the full array size; admin must enforce ≤100 chars. memset(bcfg.broadcast_message, 'A', sizeof(bcfg.broadcast_message)); - bcfg.broadcast_message[sizeof(bcfg.broadcast_message) - 1] = '\0'; // pb_decode guarantee testAdmin->handleSetModuleConfig(makeBeaconModuleConfig(bcfg)); - // Byte at index 100 must be NUL (length capped at 100). - TEST_ASSERT_EQUAL('\0', moduleConfig.mesh_beacon.broadcast_message[100]); - // Bytes before it should still be 'A'. - TEST_ASSERT_EQUAL('A', moduleConfig.mesh_beacon.broadcast_message[0]); + const size_t last = sizeof(moduleConfig.mesh_beacon.broadcast_message) - 1; + TEST_ASSERT_EQUAL('\0', moduleConfig.mesh_beacon.broadcast_message[last]); + TEST_ASSERT_EQUAL('A', moduleConfig.mesh_beacon.broadcast_message[last - 1]); + TEST_ASSERT_EQUAL(last, strlen(moduleConfig.mesh_beacon.broadcast_message)); } /** @@ -1677,7 +1677,7 @@ BEACON_TEST_ENTRY void setup() RUN_TEST(test_adminValidation_targetValidPresetForRegion_isPreserved); RUN_TEST(test_adminValidation_targetChannelIndexOutOfRange_isCleared); RUN_TEST(test_adminValidation_targetChannelIndexInRange_isPreserved); - RUN_TEST(test_adminValidation_messageTooLong_isTruncatedAt100); + RUN_TEST(test_adminValidation_messageTooLong_isTruncatedToField); RUN_TEST(test_adminValidation_intervalTooLow_isClamped); RUN_TEST(test_adminValidation_intervalTooHigh_isPreserved); RUN_TEST(test_adminValidation_intervalZero_isNotClamped);