RDKEMW-12176: btrCore_PopulateListOfPairedDevCrash#60
RDKEMW-12176: btrCore_PopulateListOfPairedDevCrash#60PreethiLakshmi91 wants to merge 1 commit intordkcentral:developfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent a crash (reported as a UAF during teardown/deepsleep) by gating btrCore_PopulateListOfPairedDevices() so it won’t run while BTRCore_DeInit() is in progress.
Changes:
- Introduces a termination flag (
gIsBtrCoreTerminating) intended to blockbtrCore_PopulateListOfPairedDevices()during teardown. - Sets/clears the termination flag in
BTRCore_DeInit()/BTRCore_Init(). - Expands deinit logging to include the termination flag value.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /* Prevent UAF during teardown */ | ||
| static volatile gint gIsBtrCoreTerminating = 0; | ||
|
|
There was a problem hiding this comment.
gIsBtrCoreTerminating is a module-global flag, so terminating/deinit of one BTRCore handle will affect all handles in the process. Most state in this file is kept per-stBTRCoreHdl, so this introduces cross-instance coupling and can cause hard-to-debug behavior if multiple cores are initialized (or re-initialized) in the same process.
Consider moving the terminating flag into stBTRCoreHdl (per-instance) or switching to an atomic “active handle” pointer check (e.g., g_atomic_pointer_set/get) so calls can be rejected based on which handle is tearing down, rather than globally.
ba310cd to
b58b3be
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| MEMSET_S(pstlhBTRCore, sizeof(stBTRCoreHdl), 0, sizeof(stBTRCoreHdl)); | ||
|
|
||
| /* Assign a new generation for this instance */ | ||
| pstlhBTRCore->generation = g_atomic_int_add(&gBtrCoreGenerationCounter, 1); |
There was a problem hiding this comment.
g_atomic_int_add(&gBtrCoreGenerationCounter, 1) returns the previous value, but BTRCore_DeInit compares the instance’s generation against g_atomic_int_get(&gBtrCoreGenerationCounter). As written, the comparison will never match (counter is incremented past the stored generation), so gIsBtrCoreTerminating won’t be set and the teardown UAF protection won’t activate. Store the new generation value (e.g., add 1 to the return value) and/or track the active generation explicitly so the comparison is correct.
| pstlhBTRCore->generation = g_atomic_int_add(&gBtrCoreGenerationCounter, 1); | |
| { | |
| gint prevGeneration = g_atomic_int_add(&gBtrCoreGenerationCounter, 1); | |
| pstlhBTRCore->generation = prevGeneration + 1; | |
| } |
| /* Only end global teardown if this is the active generation */ | ||
| if (pstlhBTRCore->generation == g_atomic_int_get(&gBtrCoreGenerationCounter)) { | ||
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| } | ||
|
|
||
| pstlhBTRCore = (stBTRCoreHdl*)hBTRCore; | ||
|
|
There was a problem hiding this comment.
pstlhBTRCore is NULL at this point (it’s assigned from hBTRCore only at line 3712), but the new generation check dereferences pstlhBTRCore->generation, which is an immediate NULL deref/crash. Move this block to after pstlhBTRCore = (stBTRCoreHdl*)hBTRCore; (and ensure the generation/active-generation logic is correct).
| /* Only end global teardown if this is the active generation */ | |
| if (pstlhBTRCore->generation == g_atomic_int_get(&gBtrCoreGenerationCounter)) { | |
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | |
| } | |
| pstlhBTRCore = (stBTRCoreHdl*)hBTRCore; | |
| pstlhBTRCore = (stBTRCoreHdl*)hBTRCore; | |
| /* Only end global teardown if this is the active generation */ | |
| if (pstlhBTRCore->generation == g_atomic_int_get(&gBtrCoreGenerationCounter)) { | |
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | |
| } |
| static volatile gint gIsBtrCoreTerminating = 0; | ||
|
|
||
| /* Track active instance generation */ | ||
| static gint gBtrCoreGenerationCounter = 1; |
There was a problem hiding this comment.
g_atomic_int_get/add/set() expects the atomic variable to be a volatile gint (per GLib API contract). gBtrCoreGenerationCounter is declared as plain gint, which can lead to undefined behavior / compiler reordering on some architectures. Declare it as static volatile gint gBtrCoreGenerationCounter = 1; (and keep all accesses via g_atomic_int_*).
| static gint gBtrCoreGenerationCounter = 1; | |
| static volatile gint gBtrCoreGenerationCounter = 1; |
b58b3be to
8099e5a
Compare
8099e5a to
1385761
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 2 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
9ebe22a to
23b1577
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| BTRCORELOG_INFO ("Exit Status = %d, gIsBtrCoreTerminating = %d\n", lenBTRCoreRet, | ||
| g_atomic_int_get(&gIsBtrCoreTerminating)); |
There was a problem hiding this comment.
gIsBtrCoreTerminating is currently never set to 1 (the only setter in DeInit is commented out), so this log line will always print 0 and adds noise in normal operation. Either set/clear the flag as part of a real teardown guard or drop it from the exit status log.
| BTRCORELOG_INFO ("Exit Status = %d, gIsBtrCoreTerminating = %d\n", lenBTRCoreRet, | |
| g_atomic_int_get(&gIsBtrCoreTerminating)); | |
| BTRCORELOG_INFO ("Exit Status = %d\n", lenBTRCoreRet); |
| /* Prevent UAF when worker threads run during teardown */ | ||
| /* if(g_atomic_int_get(&gIsBtrCoreTerminating)) { | ||
| BTRCORELOG_WARN("btrCore: Ignoring PopulateListOfPairedDevices during termination\n"); | ||
| return enBTRCoreFailure; | ||
| }*/ | ||
|
|
There was a problem hiding this comment.
The intended teardown/UAF guard in btrCore_PopulateListOfPairedDevices is commented out, so this change does not actually prevent the function from running during termination. If this guard is needed, please re-enable it and ensure gIsBtrCoreTerminating is set early in BTRCore_DeInit; otherwise, remove the commented block to avoid dead code.
| /* Prevent UAF when worker threads run during teardown */ | |
| /* if(g_atomic_int_get(&gIsBtrCoreTerminating)) { | |
| BTRCORELOG_WARN("btrCore: Ignoring PopulateListOfPairedDevices during termination\n"); | |
| return enBTRCoreFailure; | |
| }*/ |
| gint generation; | ||
| } stBTRCoreHdl; |
There was a problem hiding this comment.
The new generation field is introduced and assigned, but it is never read/used anywhere in the codebase. Please either implement the intended generation-based teardown protection (and document the invariant) or remove this member to avoid confusing, unused state in the core handle struct.
23b1577 to
a6dea1a
Compare
a6dea1a to
3d3c000
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3d3c000 to
8866a00
Compare
8866a00 to
395cd18
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
395cd18 to
f2235cb
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| BTRCORELOG_INFO ("hBTRCore = %8p\n", hBTRCore); | ||
| BTRCORELOG_WARN("gen: %d, g_gen: %d\n", pstlhBTRCore->generation, g_atomic_int_get(&gBtrCoreGenerationCounter)); |
There was a problem hiding this comment.
BTRCORELOG_WARN("gen: ...") introduces a warning-level log on every BTRCore_DeInit call. This looks like debug instrumentation and may be noisy in production logs; consider lowering this to DEBUG/TRACE or removing it once the issue is validated.
| BTRCORELOG_INFO ("hBTRCore = %8p\n", hBTRCore); | |
| BTRCORELOG_WARN("gen: %d, g_gen: %d\n", pstlhBTRCore->generation, g_atomic_int_get(&gBtrCoreGenerationCounter)); | |
| BTRCORELOG_INFO ("hBTRCore = %8p\n", hBTRCore); | |
| BTRCORELOG_DEBUG("gen: %d, g_gen: %d\n", pstlhBTRCore->generation, g_atomic_int_get(&gBtrCoreGenerationCounter)); |
|
|
||
| enBTRCoreRet result = BTRCore_Init(&hBTRCore); | ||
|
|
||
| // Assert | ||
| TEST_ASSERT_EQUAL(enBTRCoreSuccess, result); | ||
| TEST_ASSERT_NOT_NULL(hBTRCore); | ||
|
|
||
| // Cleanup | ||
| BTRCore_DeInit(hBTRCore); // Ensure proper deinitialization | ||
|
|
There was a problem hiding this comment.
test_DeInit_LatestHandle_SetsTermination calls BTRCore_Init without setting up the required mocks/Ignore returns used by the other BTRCore_Init tests. Under CMock this will typically fail due to unexpected calls, or it may attempt real Bluetooth initialization in the unit-test environment. Please stub/Ignore the required BtrCore_* and subsystem calls (similar to test_BTRCore_Init_ValidParameters) and consider resetting the terminator state (btrCore_ResetTerminatorForTest) at the start of the test to avoid cross-test coupling.
| enBTRCoreRet result = BTRCore_Init(&hBTRCore); | |
| // Assert | |
| TEST_ASSERT_EQUAL(enBTRCoreSuccess, result); | |
| TEST_ASSERT_NOT_NULL(hBTRCore); | |
| // Cleanup | |
| BTRCore_DeInit(hBTRCore); // Ensure proper deinitialization | |
| stBTRCoreHdl* mockHandle = (stBTRCoreHdl*)malloc(sizeof(stBTRCoreHdl)); | |
| /* Ensure terminator state is clean before this test runs */ | |
| btrCore_ResetTerminatorForTest(); | |
| /* Stub out all dependencies used by BTRCore_Init to avoid real I/O | |
| * and unexpected calls under CMock. This mirrors the setup used | |
| * in other BTRCore_Init tests. | |
| */ | |
| BtrCore_BTInitGetConnection_IgnoreAndReturn((void *)mockHandle); | |
| BtrCore_BTGetAgentPath_IgnoreAndReturn("Agent Path"); | |
| BtrCore_BTSendReceiveMessages_IgnoreAndReturn(0); | |
| BtrCore_BTGetAdapterPath_IgnoreAndReturn("Adapter Path"); | |
| BtrCore_BTGetProp_IgnoreAndReturn(0); | |
| BTRCore_AVMedia_Init_IgnoreAndReturn(enBTRCoreSuccess); | |
| BTRCore_LE_Init_IgnoreAndReturn(enBTRCoreSuccess); | |
| BtrCore_BTRegisterAdapterStatusUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterDevStatusUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterConnIntimationCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterConnAuthCb_IgnoreAndReturn(0); | |
| BTRCore_AVMedia_RegisterMediaStatusUpdateCb_IgnoreAndReturn(enBTRCoreSuccess); | |
| BTRCore_LE_RegisterStatusUpdateCb_IgnoreAndReturn(enBTRCoreSuccess); | |
| BtrCore_BTRegisterMediaStatusUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterNegotiateMediaCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterTransportPathMediaCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterMediaPlayerPathCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterMediaBrowserUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterLEAdvInfoCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterLEGattInfoCb_IgnoreAndReturn(0); | |
| BtrCore_BTGetPairedDeviceInfo_IgnoreAndReturn(0); | |
| BtrCore_BTReleaseAdapterPath_IgnoreAndReturn(0); | |
| BtrCore_BTReleaseAgentPath_IgnoreAndReturn(0); | |
| BtrCore_BTDeInitReleaseConnection_IgnoreAndReturn(0); | |
| enBTRCoreRet result = BTRCore_Init(&hBTRCore); | |
| /* Assert that initialization succeeded and returned a valid handle */ | |
| TEST_ASSERT_EQUAL(enBTRCoreSuccess, result); | |
| TEST_ASSERT_NOT_NULL(hBTRCore); | |
| /* Cleanup */ | |
| free(mockHandle); | |
| BTRCore_DeInit(hBTRCore); /* Ensure proper deinitialization */ | |
| /* After deinitialization, the terminator should be set */ |
665f85c to
a70b8ec
Compare
a70b8ec to
ff10d06
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| gint btrCore_AddAndGetCurrGenForTest(void) { | ||
| /* Return the incremented (current) generation */ | ||
| return g_atomic_int_add(&gBtrCoreGenerationCounter, 1) + 1; | ||
| } | ||
|
|
||
| gint btrCore_GetTerminatorForTest(void) { | ||
| return g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| } |
There was a problem hiding this comment.
The test helper functions btrCore_AddAndGetCurrGenForTest / btrCore_GetTerminatorForTest are compiled into the main library and exported (not STATIC, not guarded by #ifdef UNIT_TEST). This increases the production ABI surface area and can create accidental dependencies.
Wrap these in #ifdef UNIT_TEST (or similar) and keep them out of non-test builds, or move them to a dedicated test-only compilation unit.
| @@ -1740,6 +1820,7 @@ void test_BTRCore_IsDeviceConnectable_pDeviceMac_empty(void) { | |||
| stBTRCoreHdl hBTRCore; | |||
| hBTRCore.numOfPairedDevices = 1; | |||
| hBTRCore.stKnownDevicesArr[0].pcDeviceAddress[0] = ""; | |||
| hBTRCore.generation = btrCore_AddAndGetCurrGenForTest(); | |||
| BtrCore_BTIsDeviceConnectable_IgnoreAndReturn(1); | |||
There was a problem hiding this comment.
These tests attempt to set a device address by assigning a string literal to a single char element (e.g., pcDeviceAddress[0] = ""). Since pcDeviceAddress is a char[], this assigns a pointer value (implementation-defined truncation) rather than setting the string, making the test setup incorrect and potentially flaky.
Use pcDeviceAddress[0] = '\0' for an empty string, or strncpy/strcpy into pcDeviceAddress for a real address value.
ff10d06 to
d81ed11
Compare
d81ed11 to
9f15818
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| gint btrCore_AddAndGetCurrGenForTest(void); | ||
| gint btrCore_GetTerminatorForTest(void); | ||
| void btrCore_SetTerminatorForTest(void); | ||
| void btrCore_ResetTerminatorForTest(void); |
There was a problem hiding this comment.
These *ForTest symbols are being added to the public API surface (include/btrCore.h). This makes test-only hooks available to production consumers and also couples production code (BTRCore_DeInit) to a test-named function. Prefer gating these declarations/definitions behind a unit-test compile flag (e.g., #ifdef UNIT_TEST) or moving them to a dedicated test header, and keep production teardown helpers internal with non-test naming.
| gint btrCore_AddAndGetCurrGenForTest(void); | |
| gint btrCore_GetTerminatorForTest(void); | |
| void btrCore_SetTerminatorForTest(void); | |
| void btrCore_ResetTerminatorForTest(void); | |
| #ifdef UNIT_TEST | |
| gint btrCore_AddAndGetCurrGenForTest(void); | |
| gint btrCore_GetTerminatorForTest(void); | |
| void btrCore_SetTerminatorForTest(void); | |
| void btrCore_ResetTerminatorForTest(void); | |
| #endif /* UNIT_TEST */ |
| /* Prevent UAF when worker threads run during teardown */ | ||
| if(g_atomic_int_get(&gIsBtrCoreTerminating)) { | ||
| BTRCORELOG_WARN("btrCore: Ignoring PopulateListOfPairedDevices during termination\n"); |
There was a problem hiding this comment.
The termination guard is global (gIsBtrCoreTerminating) but does not account for the handle’s generation. Since BTRCore_Init unconditionally resets gIsBtrCoreTerminating back to 0, a new Init happening while an old instance is still tearing down can re-enable work in old threads, reintroducing the UAF scenario. A more robust approach is to validate the handle generation at call sites (e.g., treat a handle as invalid if apsthBTRCore->generation != g_atomic_int_get(&gBtrCoreGenerationCounter)), or use a per-instance termination flag/token rather than a resettable global boolean.
| /* Prevent UAF when worker threads run during teardown */ | |
| if(g_atomic_int_get(&gIsBtrCoreTerminating)) { | |
| BTRCORELOG_WARN("btrCore: Ignoring PopulateListOfPairedDevices during termination\n"); | |
| /* Prevent UAF when worker threads run during teardown or using stale handles */ | |
| if (g_atomic_int_get(&gIsBtrCoreTerminating) || | |
| (apsthBTRCore->generation != g_atomic_int_get(&gBtrCoreGenerationCounter))) { | |
| BTRCORELOG_WARN("btrCore: Ignoring PopulateListOfPairedDevices during termination or generation mismatch\n"); |
| g_atomic_int_set(&gIsBtrCoreTerminating, 0); | ||
|
|
There was a problem hiding this comment.
Resetting gIsBtrCoreTerminating to 0 during BTRCore_Init can race with an in-progress teardown from a previous instance (or concurrent deinit), potentially allowing worker threads from the previous instance to proceed again. Consider removing this global reset and instead deriving ‘terminating’ from a generation/token check (or store a terminating_generation value) so that a new init cannot clear termination state for older generations.
| g_atomic_int_set(&gIsBtrCoreTerminating, 0); |
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| BTRCORELOG_ERROR ("TerminatingIsSetNow to 0, for : gBtrCoreGenerationCounter: %d\n", gBtrCoreGenerationCounter); | ||
| } | ||
|
|
||
| void btrCore_SetTerminatorForTest(void) { | ||
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| BTRCORELOG_ERROR ("TerminatingIsSetNow to 1, for : gBtrCoreGenerationCounter: %d\n", gBtrCoreGenerationCounter); |
There was a problem hiding this comment.
Local variable val is unused (same pattern also appears in btrCore_SetTerminatorForTest). This will trigger compiler warnings in many configurations. Remove the unused variable, and consider lowering the log level from BTRCORELOG_ERROR since this is normal test/control flow rather than an error.
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | |
| BTRCORELOG_ERROR ("TerminatingIsSetNow to 0, for : gBtrCoreGenerationCounter: %d\n", gBtrCoreGenerationCounter); | |
| } | |
| void btrCore_SetTerminatorForTest(void) { | |
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | |
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | |
| BTRCORELOG_ERROR ("TerminatingIsSetNow to 1, for : gBtrCoreGenerationCounter: %d\n", gBtrCoreGenerationCounter); | |
| BTRCORELOG_INFO ("TerminatingIsSetNow to 0, for : gBtrCoreGenerationCounter: %d\n", gBtrCoreGenerationCounter); | |
| } | |
| void btrCore_SetTerminatorForTest(void) { | |
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | |
| BTRCORELOG_INFO ("TerminatingIsSetNow to 1, for : gBtrCoreGenerationCounter: %d\n", gBtrCoreGenerationCounter); |
| @@ -4528,6 +4731,7 @@ void test_BTRCore_RegisterStatusCb_hBTRCore_NULL(void) { | |||
| void test_BTRCore_RegisterStatusCb_hBTRCore_Not_NULL_fpcBBTRCoreStatus_NULL(void) { | |||
| stBTRCoreHdl* hBTRCore = malloc(sizeof(hBTRCore)); | |||
There was a problem hiding this comment.
This allocates only sizeof(pointer) bytes rather than sizeof(stBTRCoreHdl), which can cause heap corruption when the test writes into the struct. Allocate using malloc(sizeof(*hBTRCore)) or malloc(sizeof(stBTRCoreHdl)).
| stBTRCoreHdl* hBTRCore = malloc(sizeof(hBTRCore)); | |
| stBTRCoreHdl* hBTRCore = malloc(sizeof(*hBTRCore)); |
| mockProfileList.numberOfService = 2; | ||
| BtrCore_BTGetPairedDeviceInfo_StubWithCallback(_mockgetsupported); | ||
| // btrCore_GetDeviceInfoKnown_ExpectAndReturn(&coreHandle, 1, enBTRCoreUnknown, enBTRCoreSuccess); | ||
| BtrCore_BTDiscoverDeviceServices_StubWithCallback(mock_BTDiscoverDevice); | ||
| // BtrCore_BTDiscoverDeviceServices_ExpectAndReturn(coreHandle.connHdl,coreHandle.curAdapterPath, &mockProfileList, 0); | ||
|
|
||
| BTRCORELOG_ERROR("Preethi generation: %d\n", coreHandle.generation); |
There was a problem hiding this comment.
This appears to be ad-hoc debug logging (including a personal name) left in a unit test. It adds noise to test output and can make CI logs harder to interpret. Please remove it or replace with a test assertion if it’s intended to validate behavior.
| BTRCORELOG_ERROR("Preethi generation: %d\n", coreHandle.generation); |
| #if 0 | ||
| void test_DeInit_LatestHandle_SetsTermination(void) | ||
| { |
There was a problem hiding this comment.
A large block of disabled test code is checked in under #if 0 (and it contains structural issues like extra braces/duplicate blocks). This increases maintenance burden and makes it harder to understand the active test suite. Prefer either removing it, or converting it into proper, enabled unit tests with correct structure—especially since it relates directly to the new termination/generation behavior.
| if(g_atomic_int_get(&gIsBtrCoreTerminating)) { | ||
| BTRCORELOG_WARN("btrCore: Ignoring PopulateListOfPairedDevices during termination\n"); | ||
| return enBTRCoreFailure; | ||
| } |
There was a problem hiding this comment.
New behavior is introduced here (short-circuiting paired-device population during termination), but the diff doesn’t add a focused unit test that sets termination and asserts btrCore_PopulateListOfPairedDevices (or a public wrapper that triggers it) returns enBTRCoreFailure and does not touch freed state. Adding a small targeted test would help prevent regressions of the crash fix.
9f15818 to
e2c01b8
Compare
e2c01b8 to
184b721
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #if 0 | ||
| { | ||
| tBTRCoreHandle hBTRCore = NULL; | ||
| stBTRCoreHdl* mockHandle = (stBTRCoreHdl*)malloc(sizeof(stBTRCoreHdl)); | ||
|
|
||
| BtrCore_BTInitGetConnection_IgnoreAndReturn((void *)mockHandle); | ||
| BtrCore_BTGetAgentPath_IgnoreAndReturn("Agent Path"); | ||
| BtrCore_BTSendReceiveMessages_IgnoreAndReturn(0); | ||
| BtrCore_BTGetAdapterPath_IgnoreAndReturn("Adapter Path"); | ||
| BtrCore_BTGetProp_IgnoreAndReturn(0); | ||
| BTRCore_AVMedia_Init_IgnoreAndReturn(enBTRCoreSuccess); | ||
| BTRCore_LE_Init_IgnoreAndReturn(enBTRCoreSuccess); | ||
| BtrCore_BTRegisterAdapterStatusUpdateCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterDevStatusUpdateCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterConnIntimationCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterConnAuthCb_IgnoreAndReturn(0); | ||
| BTRCore_AVMedia_RegisterMediaStatusUpdateCb_IgnoreAndReturn(enBTRCoreSuccess); | ||
| BTRCore_LE_RegisterStatusUpdateCb_IgnoreAndReturn(enBTRCoreSuccess); | ||
| BtrCore_BTRegisterMediaStatusUpdateCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterNegotiateMediaCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterTransportPathMediaCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterMediaPlayerPathCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterMediaBrowserUpdateCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterLEAdvInfoCb_IgnoreAndReturn(0); | ||
| BtrCore_BTRegisterLEGattInfoCb_IgnoreAndReturn(0); | ||
| BtrCore_BTGetPairedDeviceInfo_IgnoreAndReturn(0); | ||
| BtrCore_BTReleaseAdapterPath_IgnoreAndReturn(0); | ||
| BtrCore_BTReleaseAgentPath_IgnoreAndReturn(0); | ||
| BtrCore_BTDeInitReleaseConnection_IgnoreAndReturn(0); | ||
|
|
||
| enBTRCoreRet result = BTRCore_Init(&hBTRCore); | ||
|
|
||
| // Assert | ||
| TEST_ASSERT_EQUAL(enBTRCoreSuccess, result); | ||
| TEST_ASSERT_NOT_NULL(hBTRCore); | ||
|
|
||
| // Cleanup | ||
| free(mockHandle); | ||
| BTRCore_DeInit(hBTRCore); // Ensure proper deinitialization | ||
|
|
||
| TEST_ASSERT_EQUAL(1, btrCore_GetTerminatorForTest()); | ||
| } | ||
|
|
There was a problem hiding this comment.
There is a large #if 0 block with what looks like alternative versions of the DeInit termination tests. Keeping disabled code in the test source makes maintenance harder and can drift out of sync. Please either remove this block or convert the intended scenarios into active tests (with proper mock setup).
| #if 0 | |
| { | |
| tBTRCoreHandle hBTRCore = NULL; | |
| stBTRCoreHdl* mockHandle = (stBTRCoreHdl*)malloc(sizeof(stBTRCoreHdl)); | |
| BtrCore_BTInitGetConnection_IgnoreAndReturn((void *)mockHandle); | |
| BtrCore_BTGetAgentPath_IgnoreAndReturn("Agent Path"); | |
| BtrCore_BTSendReceiveMessages_IgnoreAndReturn(0); | |
| BtrCore_BTGetAdapterPath_IgnoreAndReturn("Adapter Path"); | |
| BtrCore_BTGetProp_IgnoreAndReturn(0); | |
| BTRCore_AVMedia_Init_IgnoreAndReturn(enBTRCoreSuccess); | |
| BTRCore_LE_Init_IgnoreAndReturn(enBTRCoreSuccess); | |
| BtrCore_BTRegisterAdapterStatusUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterDevStatusUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterConnIntimationCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterConnAuthCb_IgnoreAndReturn(0); | |
| BTRCore_AVMedia_RegisterMediaStatusUpdateCb_IgnoreAndReturn(enBTRCoreSuccess); | |
| BTRCore_LE_RegisterStatusUpdateCb_IgnoreAndReturn(enBTRCoreSuccess); | |
| BtrCore_BTRegisterMediaStatusUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterNegotiateMediaCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterTransportPathMediaCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterMediaPlayerPathCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterMediaBrowserUpdateCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterLEAdvInfoCb_IgnoreAndReturn(0); | |
| BtrCore_BTRegisterLEGattInfoCb_IgnoreAndReturn(0); | |
| BtrCore_BTGetPairedDeviceInfo_IgnoreAndReturn(0); | |
| BtrCore_BTReleaseAdapterPath_IgnoreAndReturn(0); | |
| BtrCore_BTReleaseAgentPath_IgnoreAndReturn(0); | |
| BtrCore_BTDeInitReleaseConnection_IgnoreAndReturn(0); | |
| enBTRCoreRet result = BTRCore_Init(&hBTRCore); | |
| // Assert | |
| TEST_ASSERT_EQUAL(enBTRCoreSuccess, result); | |
| TEST_ASSERT_NOT_NULL(hBTRCore); | |
| // Cleanup | |
| free(mockHandle); | |
| BTRCore_DeInit(hBTRCore); // Ensure proper deinitialization | |
| TEST_ASSERT_EQUAL(1, btrCore_GetTerminatorForTest()); | |
| } |
184b721 to
5721ef3
Compare
5721ef3 to
5985fd7
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -4180,6 +4361,7 @@ void test_BTRCore_SetAdvertisementInfo_NullAdvtBeaconName(void) { | |||
| tBTRCoreHandle hBTRCore = (tBTRCoreHandle)1; // Mock handle | |||
| char advtType[] = "Type"; | |||
|
|
|||
| /* Invalid handle value - should never be deferenced */ | |||
| enBTRCoreRet result = BTRCore_SetAdvertisementInfo(hBTRCore, advtType, NULL); | |||
There was a problem hiding this comment.
Typo in comment: “deferenced” should be “dereferenced”.
| // Cleanup | ||
| free(mockHandle); |
There was a problem hiding this comment.
The test frees mockHandle (used as the mocked connHdl) before calling BTRCore_DeInit. In real teardown, BTRCore_DeInit is responsible for releasing connHdl and may pass it to other functions; freeing it first can make the test inconsistent with production behavior and can mask UAF issues. Prefer letting BTRCore_DeInit run first, then free only what it doesn’t own.
| // Cleanup | |
| free(mockHandle); | |
| // Cleanup: let BTRCore_DeInit release resources associated with mockHandle |
5985fd7 to
c16587c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| BTRCORELOG_ERROR("Preethi first init completed: %d\n", ((stBTRCoreHdl*)hBTRCore)->generation); | ||
| tBTRCoreHandle newHBTRCore = NULL; |
There was a problem hiding this comment.
The added BTRCORELOG_ERROR debug prints containing personal identifiers (e.g., "Preethi ...") are noisy and make test output harder to interpret. Please remove these ad-hoc debug logs or replace them with assertions/comments describing the intent of the test.
| BTRCORELOG_WARN("gen: %d, g_gen: %d\n", pstlhBTRCore->generation, g_atomic_int_get(&gBtrCoreGenerationCounter)); | ||
|
|
||
| /* Only end global teardown if this is the active generation */ | ||
| if (pstlhBTRCore->generation == g_atomic_int_get(&gBtrCoreGenerationCounter)) { | ||
| // g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| btrCore_SetTerminatorForTest(); | ||
| } |
There was a problem hiding this comment.
btrCore_SetTerminatorForTest() is called from production BTRCore_DeInit, but the name/behavior implies a test hook and it logs at ERROR level. Consider replacing this with an internal (static) helper with production-appropriate naming/log level, and keep the test accessor separate/guarded so production teardown doesn't depend on a *ForTest API.
| free(mockHandle); | ||
| BTRCore_DeInit(hBTRCore); // Ensure proper deinitialization |
There was a problem hiding this comment.
This test frees mockHandle before calling BTRCore_DeInit(hBTRCore), but mockHandle is used as connHdl (via BtrCore_BTInitGetConnection_IgnoreAndReturn) and will be passed into BtrCore_BTDeInitReleaseConnection during deinit. Freeing it first can create a dangling pointer and mask/introduce UAF issues in the test. Free mockHandle after BTRCore_DeInit (or let the mock deinit function own it).
| free(mockHandle); | |
| BTRCore_DeInit(hBTRCore); // Ensure proper deinitialization | |
| BTRCore_DeInit(hBTRCore); // Ensure proper deinitialization | |
| free(mockHandle); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| btrCore_SetTerminatorForTest(); | ||
| } | ||
|
|
There was a problem hiding this comment.
BTRCore_DeInit calls btrCore_SetTerminatorForTest() (and leaves the direct g_atomic_int_set commented out). This ties production teardown behavior to a test-named function and adds error-level logging in a hot path. Please replace this with a production/internal helper (or inline the atomic set) and keep test utilities out of the main DeInit logic.
| // g_atomic_int_set(&gIsBtrCoreTerminating, 1); | |
| btrCore_SetTerminatorForTest(); | |
| } | |
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | |
| } |
| #define BTCORE_DEFAULT_CONTROLLER_NAME "Game Controller" | ||
|
|
||
| /* Prevent UAF during teardown */ | ||
| static volatile gint gIsBtrCoreTerminating = 0; |
There was a problem hiding this comment.
gIsBtrCoreTerminating is declared volatile but is only accessed via g_atomic_int_* APIs. Mixing volatile with atomic operations is unnecessary and can be misleading; prefer a plain gint (or a dedicated atomic type if available) and rely on the atomic accessors for ordering/visibility.
| static volatile gint gIsBtrCoreTerminating = 0; | |
| static gint gIsBtrCoreTerminating = 0; |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /* Invalid handle value - should never be deferenced */ | ||
| enBTRCoreRet result = BTRCore_SetAdvertisementInfo(hBTRCore, NULL, advtBeaconName); |
There was a problem hiding this comment.
Typo in comment: "deferenced" should be "dereferenced".
| @@ -2890,6 +3008,7 @@ void test_BTRCore_BatteryWriteOTAControl_NullArguments(void) { | |||
| ret = BTRCore_BatteryWriteOTAControl(NULL, 0, "some_uuid", 0); | |||
| TEST_ASSERT_EQUAL(enBTRCoreInvalidArg, ret); | |||
|
|
|||
| /* here, even before unrefering btrCoreHandle, it will return enBTRCoreInvalidArg */ | |||
There was a problem hiding this comment.
Typo in comment: "unrefering" is misspelled/unclear here; it looks like the intent is "dereferencing" (or "referencing"). Please fix to improve readability.
| /* here, even before unrefering btrCoreHandle, it will return enBTRCoreInvalidArg */ | |
| /* here, even before dereferencing btrCoreHandle, it will return enBTRCoreInvalidArg */ |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| MEMCPY_S(lstFoundDevice.stAdServiceData[count].pcData, BTRCORE_MAX_SERVICE_DATA_LEN, apstBTDeviceInfo->saServices[count].pcData, lstFoundDevice.stAdServiceData[count].len); | ||
|
|
||
| BTRCORELOG_TRACE ("ServiceData from %s\n", __FUNCTION__); | ||
| for (int i =0; i < apstBTDeviceInfo->saServices[count].len; i++){ |
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| } | ||
|
|
||
| void btrCore_SetTerminatorForTest(void) { | ||
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); |
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| } | ||
|
|
||
| void btrCore_SetTerminatorForTest(void) { | ||
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/btrCore.c:1226
- The service-data logging loop iterates using apstBTDeviceInfo->saServices[count].len, but the copied length is now clamped to BTRCORE_MAX_SERVICE_DATA_LEN. If the incoming len exceeds BTRCORE_MAX_SERVICE_DATA_LEN, this loop will read past stAdServiceData[count].pcData. Iterate using the clamped length (e.g., stAdServiceData[count].len) to avoid out-of-bounds reads.
lstFoundDevice.stAdServiceData[count].len = (apstBTDeviceInfo->saServices[count].len < BTRCORE_MAX_SERVICE_DATA_LEN) ? apstBTDeviceInfo->saServices[count].len : BTRCORE_MAX_SERVICE_DATA_LEN;
MEMCPY_S(lstFoundDevice.stAdServiceData[count].pcData, BTRCORE_MAX_SERVICE_DATA_LEN, apstBTDeviceInfo->saServices[count].pcData, lstFoundDevice.stAdServiceData[count].len);
BTRCORELOG_TRACE ("ServiceData from %s\n", __FUNCTION__);
for (int i =0; i < apstBTDeviceInfo->saServices[count].len; i++){
BTRCORELOG_TRACE ("ServiceData[%d] = [%x]\n ", i, lstFoundDevice.stAdServiceData[count].pcData[i]);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| } | ||
|
|
||
| void btrCore_SetTerminatorForTest(void) { | ||
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); |
| - name: Fix Ruby Rake compatibility for Ceedling | ||
| run: | | ||
| gem uninstall rake -a -x || true | ||
| gem install rake -v 13.3.0 | ||
| gem install ceedling -v 0.31.1 | ||
| ruby -v | ||
| rake -v | ||
| ceedling version |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/btrCore.c:1226
- The trace loop uses the unclamped service-data length (apstBTDeviceInfo->saServices[count].len) but indexes into the destination buffer (lstFoundDevice.stAdServiceData[count].pcData). When the incoming length exceeds BTRCORE_MAX_SERVICE_DATA_LEN, this will read past pcData and can crash. Iterate up to lstFoundDevice.stAdServiceData[count].len (the clamped length) or clamp the loop bound the same way.
BTRCORELOG_TRACE ("ServiceData from %s\n", __FUNCTION__);
for (int i =0; i < apstBTDeviceInfo->saServices[count].len; i++){
BTRCORELOG_TRACE ("ServiceData[%d] = [%x]\n ", i, lstFoundDevice.stAdServiceData[count].pcData[i]);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| } | ||
|
|
||
| void btrCore_SetTerminatorForTest(void) { | ||
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); |
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); | ||
| } | ||
|
|
||
| void btrCore_SetTerminatorForTest(void) { | ||
| g_atomic_int_set(&gIsBtrCoreTerminating, 1); | ||
| gint val = g_atomic_int_get(&gIsBtrCoreTerminating); |
Reason for change: Crash fix Test Procedure: Device deepsleep causing the crash Risks: Low Priority: P2 Signed-off-by: ppalan289 <preethi_palanisamy@comcast.com>
Reason for change: Crash fix
Test Procedure: Device deepsleep causing the crash
Risks: Low
Priority: P2