acdb: Fix memory-safety, locking and error-path bugs - #113
shijlin-1224 wants to merge 1 commit into
Conversation
Fix a heap buffer overflow in the diag transport, a double free during transport startup failure handling, and mutex locking bugs that could deadlock the TCP command and diagnostic servers. Also validate failure paths in AMDB command handlers and release locks correctly when the file manager encounters existing database files. Signed-off-by: Shijie Lin <shijlin@qti.qualcomm.com>
Qualcomm AI ReviewClick to expand Code ReviewReviewed Commits: d0cd3c0
Fix a heap buffer overflow in the diag transport, a Signed-off-by: Shijie Lin shijlin@qti.qualcomm.com Pull Request OverviewThis PR addresses multiple memory management and mutex handling issues across several files in the ACDB (Audio Calibration Database) codebase. The changes focus on fixing resource leaks, correcting mutex operations, and improving error handling paths. Files Changed Summary
Critical Issues Identified
Positive Changes
[SECURITY] High Severity - Buffer Overflow Prevention AddedThe PR adds a critical bounds check in the Issue: Without this check, if a malformed frame with an offset beyond the buffer length is received, it could lead to writing beyond allocated memory, causing memory corruption or crashes. Impact: This is a security vulnerability that could be exploited to cause denial of service or potentially execute arbitrary code. Fixed Code Snippet: if (frame_ptr->header.frame_offset >= buf_cntxt_ptr->buffer_length)
{
return FALSE;
}
/** Calculate destination location where to copy the frame*/
dest_loc_ptr = buf_cntxt_ptr->buffer_ptr + frame_ptr->header.frame_offset;Severity: High - This prevents a potential buffer overflow vulnerability. [FUNCTIONALITY] High Severity - Missing Mutex Unlock in Error PathsIn Issue: When Impact: After the first allocation failure, all subsequent connection attempts will hang indefinitely waiting for the mutex, effectively causing a denial of service. Fixed Code Snippet: message_buffer.buffer = (char_t*)ar_heap_malloc(message_buffer.buffer_size, &heap_inf);
if (NULL == message_buffer.buffer) {
ar_osal_mutex_unlock(connection_lock);
return 0;
}
recieve_buffer.buffer = (char_t*)ar_heap_malloc(recieve_buffer.buffer_size, &heap_inf);
if (NULL == recieve_buffer.buffer) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}
outbuf = (char_t*)ar_heap_malloc(maxsize, &heap_inf);
if (NULL == outbuf) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_heap_free(recieve_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}Severity: High - Causes deadlock and denial of service. [FUNCTIONALITY] Medium Severity - Incorrect Mutex Operation (Lock Instead of Unlock)In both Issue: The function acquires the mutex at the beginning, modifies Impact: The mutex remains locked after the function completes, causing any subsequent attempt to acquire the same mutex to deadlock. This prevents proper connection state management. Fixed Code Snippet: int32_t TcpipCmdServer::set_connected_lock(uint8_t value)
{
int32_t status = AR_EOK;
status = ar_osal_mutex_lock(connection_lock);
if (AR_FAILED(status))
{
return status;
}
is_connected = value;
status = ar_osal_mutex_unlock(connection_lock); // Fixed: was ar_osal_mutex_lock
if (AR_FAILED(status))
{
return status;
}
return status;
}Severity: Medium - Causes mutex deadlock in connection state management. [FUNCTIONALITY] Medium Severity - Missing Resource Cleanup in Error PathIn Issue: After accepting a connection (line 382), if Impact: Each failed thread creation leaks a socket file descriptor, eventually exhausting available file descriptors and preventing new connections. Fixed Code Snippet: status = ar_osal_thread_create(&thd_ats_transmit, &thd_attr,
routine, gateway_socket_ptr);
if (AR_FAILED(status))
{
ATS_ERR("Error[%d]: Failed to create transmission thread #%d.\n", status, i);
atsclosesocket(*gateway_socket_ptr); // Close the socket before freeing
ACDB_FREE(gateway_socket_ptr);
}Severity: Medium - Causes resource leak that can lead to denial of service over time. [FUNCTIONALITY] Medium Severity - Missing Mutex Unlock in Error PathIn Issue: When a duplicate database file is found, the function logs an error and returns Impact: After detecting a duplicate file, the mutex remains locked, causing any subsequent calls to Fixed Code Snippet: if (NULL == ar_strstr(&fm_db_info->database_file.path[0], &db_file->path[0]))
continue;
else
{
ACDB_ERR("Error[%d]: The database file %s already exists", AR_EALREADY,
db_file->path);
ACDB_MUTEX_UNLOCK(acdb_file_man_context.file_man_lock);
return AR_EALREADY;
}Severity: Medium - Causes mutex deadlock when duplicate files are detected. [FUNCTIONALITY] High Severity - Missing Resource Cleanup on Thread ExitIn Issue: The function allocates memory for Impact: Each client connection that completes normally leaks the memory allocated for Fixed Code Snippet: //Cleanup
TCPIP_CMD_SVR_DBG("%s: closing thread", thd_name.c_str());
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_heap_free(recieve_buffer.buffer, &heap_inf);
ar_heap_free(outbuf, &heap_inf); // Added: Free outbuf
is_connected = false;
ar_osal_mutex_unlock(connection_lock);
return 0;Severity: High - Causes memory leak on every client connection. |
| buf_cntxt_ptr->buffer_length = frame_ptr->header.buffer_length; | ||
| } | ||
|
|
||
| if (frame_ptr->header.frame_offset >= buf_cntxt_ptr->buffer_length) | ||
| { | ||
| return FALSE; | ||
| } | ||
|
|
||
| /** Calculate destination loaction where to copy the frame*/ | ||
| dest_loc_ptr = buf_cntxt_ptr->buffer_ptr + frame_ptr->header.frame_offset; | ||
| /** copy frame on to buffer */ |
There was a problem hiding this comment.
[SECURITY] High Severity - Buffer Overflow Prevention Added
The PR adds a critical bounds check in the copy_frame_to_buffer function to prevent potential buffer overflow. The check verifies that frame_ptr->header.frame_offset is within the bounds of buf_cntxt_ptr->buffer_length before performing memory copy operations.
Issue: Without this check, if a malformed frame with an offset beyond the buffer length is received, it could lead to writing beyond allocated memory, causing memory corruption or crashes.
Impact: This is a security vulnerability that could be exploited to cause denial of service or potentially execute arbitrary code.
Fixed Code Snippet
if (frame_ptr->header.frame_offset >= buf_cntxt_ptr->buffer_length)
{
return FALSE;
}
/** Calculate destination location where to copy the frame*/
dest_loc_ptr = buf_cntxt_ptr->buffer_ptr + frame_ptr->header.frame_offset;Severity: High - This prevents a potential buffer overflow vulnerability.
| recieve_buffer.buffer_size = TCPIP_CMD_SERVER_RECV_BUFFER_SIZE; | ||
| //Stores one message | ||
| message_buffer.buffer = (char_t*)ar_heap_malloc(message_buffer.buffer_size, &heap_inf); | ||
| if (NULL == message_buffer.buffer) | ||
| if (NULL == message_buffer.buffer) { | ||
| ar_osal_mutex_unlock(connection_lock); | ||
| return 0; | ||
| } | ||
|
|
||
| recieve_buffer.buffer = (char_t*)ar_heap_malloc(recieve_buffer.buffer_size, &heap_inf); | ||
| if (NULL == recieve_buffer.buffer) { |
There was a problem hiding this comment.
[FUNCTIONALITY] High Severity - Missing Mutex Unlock in Error Paths
In tcpip_cmd_server.cpp, the transmit_routine function acquires a mutex lock at line 461 but fails to release it in three error return paths (lines 468-469, 475-476, and 483-484). This causes a deadlock where the mutex remains locked if memory allocation fails, preventing any future connections from being processed.
Issue: When ar_heap_malloc fails for message_buffer.buffer, recieve_buffer.buffer, or outbuf, the function returns without calling ar_osal_mutex_unlock(connection_lock). This leaves the mutex in a locked state permanently.
Impact: After the first allocation failure, all subsequent connection attempts will hang indefinitely waiting for the mutex, effectively causing a denial of service.
Fixed Code Snippet
message_buffer.buffer = (char_t*)ar_heap_malloc(message_buffer.buffer_size, &heap_inf);
if (NULL == message_buffer.buffer) {
ar_osal_mutex_unlock(connection_lock);
return 0;
}
recieve_buffer.buffer = (char_t*)ar_heap_malloc(recieve_buffer.buffer_size, &heap_inf);
if (NULL == recieve_buffer.buffer) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}
outbuf = (char_t*)ar_heap_malloc(maxsize, &heap_inf);
if (NULL == outbuf) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_heap_free(recieve_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}Severity: High - Causes deadlock and denial of service.
| return 0; | ||
| } | ||
|
|
||
| recieve_buffer.buffer = (char_t*)ar_heap_malloc(recieve_buffer.buffer_size, &heap_inf); | ||
| if (NULL == recieve_buffer.buffer) { | ||
| ar_heap_free(message_buffer.buffer, &heap_inf); | ||
| ar_osal_mutex_unlock(connection_lock); | ||
| return 0; | ||
| } | ||
|
|
||
| outbuf = (char_t*)ar_heap_malloc(maxsize, &heap_inf); |
There was a problem hiding this comment.
[FUNCTIONALITY] High Severity - Missing Mutex Unlock in Error Paths
In tcpip_cmd_server.cpp, the transmit_routine function acquires a mutex lock at line 461 but fails to release it in three error return paths (lines 468-469, 475-476, and 483-484). This causes a deadlock where the mutex remains locked if memory allocation fails, preventing any future connections from being processed.
Issue: When ar_heap_malloc fails for message_buffer.buffer, recieve_buffer.buffer, or outbuf, the function returns without calling ar_osal_mutex_unlock(connection_lock). This leaves the mutex in a locked state permanently.
Impact: After the first allocation failure, all subsequent connection attempts will hang indefinitely waiting for the mutex, effectively causing a denial of service.
Fixed Code Snippet
message_buffer.buffer = (char_t*)ar_heap_malloc(message_buffer.buffer_size, &heap_inf);
if (NULL == message_buffer.buffer) {
ar_osal_mutex_unlock(connection_lock);
return 0;
}
recieve_buffer.buffer = (char_t*)ar_heap_malloc(recieve_buffer.buffer_size, &heap_inf);
if (NULL == recieve_buffer.buffer) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}
outbuf = (char_t*)ar_heap_malloc(maxsize, &heap_inf);
if (NULL == outbuf) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_heap_free(recieve_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}Severity: High - Causes deadlock and denial of service.
| return 0; | ||
| } | ||
|
|
||
| outbuf = (char_t*)ar_heap_malloc(maxsize, &heap_inf); | ||
| if (NULL == outbuf) { | ||
| ar_heap_free(message_buffer.buffer, &heap_inf); | ||
| ar_heap_free(recieve_buffer.buffer, &heap_inf); | ||
| ar_osal_mutex_unlock(connection_lock); | ||
| return 0; | ||
| } | ||
| ar_mem_set(outbuf, 0, 1); |
There was a problem hiding this comment.
[FUNCTIONALITY] High Severity - Missing Mutex Unlock in Error Paths
In tcpip_cmd_server.cpp, the transmit_routine function acquires a mutex lock at line 461 but fails to release it in three error return paths (lines 468-469, 475-476, and 483-484). This causes a deadlock where the mutex remains locked if memory allocation fails, preventing any future connections from being processed.
Issue: When ar_heap_malloc fails for message_buffer.buffer, recieve_buffer.buffer, or outbuf, the function returns without calling ar_osal_mutex_unlock(connection_lock). This leaves the mutex in a locked state permanently.
Impact: After the first allocation failure, all subsequent connection attempts will hang indefinitely waiting for the mutex, effectively causing a denial of service.
Fixed Code Snippet
message_buffer.buffer = (char_t*)ar_heap_malloc(message_buffer.buffer_size, &heap_inf);
if (NULL == message_buffer.buffer) {
ar_osal_mutex_unlock(connection_lock);
return 0;
}
recieve_buffer.buffer = (char_t*)ar_heap_malloc(recieve_buffer.buffer_size, &heap_inf);
if (NULL == recieve_buffer.buffer) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}
outbuf = (char_t*)ar_heap_malloc(maxsize, &heap_inf);
if (NULL == outbuf) {
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_heap_free(recieve_buffer.buffer, &heap_inf);
ar_osal_mutex_unlock(connection_lock);
return 0;
}Severity: High - Causes deadlock and denial of service.
|
|
||
| is_connected = value; | ||
|
|
||
| status = ar_osal_mutex_lock(connection_lock); | ||
| status = ar_osal_mutex_unlock(connection_lock); | ||
| if (AR_FAILED(status)) | ||
| { | ||
| return status; |
There was a problem hiding this comment.
[FUNCTIONALITY] Medium Severity - Incorrect Mutex Operation (Lock Instead of Unlock)
In both tcpip_cmd_server.cpp and tcpip_dls_server.cpp, the set_connected_lock function incorrectly calls ar_osal_mutex_lock instead of ar_osal_mutex_unlock after setting the is_connected value. This causes the mutex to remain locked after the function returns.
Issue: The function acquires the mutex at the beginning, modifies is_connected, and then attempts to release it. However, line 304 (tcpip_cmd_server.cpp) and line 116 (tcpip_dls_server.cpp) call ar_osal_mutex_lock again instead of ar_osal_mutex_unlock, resulting in a double-lock situation.
Impact: The mutex remains locked after the function completes, causing any subsequent attempt to acquire the same mutex to deadlock. This prevents proper connection state management.
Fixed Code Snippet
int32_t TcpipCmdServer::set_connected_lock(uint8_t value)
{
int32_t status = AR_EOK;
status = ar_osal_mutex_lock(connection_lock);
if (AR_FAILED(status))
{
return status;
}
is_connected = value;
status = ar_osal_mutex_unlock(connection_lock); // Fixed: was ar_osal_mutex_lock
if (AR_FAILED(status))
{
return status;
}
return status;
}Severity: Medium - Causes mutex deadlock in connection state management.
|
|
||
| is_connected = value; | ||
|
|
||
| status = ar_osal_mutex_lock(connection_lock); | ||
| status = ar_osal_mutex_unlock(connection_lock); | ||
| if (AR_FAILED(status)) | ||
| { | ||
| return status; |
There was a problem hiding this comment.
[FUNCTIONALITY] Medium Severity - Incorrect Mutex Operation (Lock Instead of Unlock)
In both tcpip_cmd_server.cpp and tcpip_dls_server.cpp, the set_connected_lock function incorrectly calls ar_osal_mutex_lock instead of ar_osal_mutex_unlock after setting the is_connected value. This causes the mutex to remain locked after the function returns.
Issue: The function acquires the mutex at the beginning, modifies is_connected, and then attempts to release it. However, line 304 (tcpip_cmd_server.cpp) and line 116 (tcpip_dls_server.cpp) call ar_osal_mutex_lock again instead of ar_osal_mutex_unlock, resulting in a double-lock situation.
Impact: The mutex remains locked after the function completes, causing any subsequent attempt to acquire the same mutex to deadlock. This prevents proper connection state management.
Fixed Code Snippet
int32_t TcpipCmdServer::set_connected_lock(uint8_t value)
{
int32_t status = AR_EOK;
status = ar_osal_mutex_lock(connection_lock);
if (AR_FAILED(status))
{
return status;
}
is_connected = value;
status = ar_osal_mutex_unlock(connection_lock); // Fixed: was ar_osal_mutex_lock
if (AR_FAILED(status))
{
return status;
}
return status;
}Severity: Medium - Causes mutex deadlock in connection state management.
| if (AR_FAILED(status)) | ||
| { | ||
| ATS_ERR("Error[%d]: Failed to create transmission thread #%d.\n", status, i); | ||
| ACDB_FREE(gateway_socket_ptr); | ||
| } | ||
| else | ||
| { |
There was a problem hiding this comment.
[FUNCTIONALITY] Medium Severity - Missing Resource Cleanup in Error Path
In ats_server.cpp, when thread creation fails at line 432-434, the function frees gateway_socket_ptr but doesn't close the accepted socket. This leaves the socket open and consumes system resources.
Issue: After accepting a connection (line 382), if ar_osal_thread_create fails, the code frees the gateway_socket_ptr memory but doesn't call atsclosesocket(*gateway_socket_ptr) to close the actual socket connection.
Impact: Each failed thread creation leaks a socket file descriptor, eventually exhausting available file descriptors and preventing new connections.
Fixed Code Snippet
status = ar_osal_thread_create(&thd_ats_transmit, &thd_attr,
routine, gateway_socket_ptr);
if (AR_FAILED(status))
{
ATS_ERR("Error[%d]: Failed to create transmission thread #%d.\n", status, i);
atsclosesocket(*gateway_socket_ptr); // Close the socket before freeing
ACDB_FREE(gateway_socket_ptr);
}Severity: Medium - Causes resource leak that can lead to denial of service over time.
| { | ||
| ACDB_ERR("Error[%d]: The database file %s already exists", AR_EALREADY, | ||
| db_file->path); | ||
| ACDB_MUTEX_UNLOCK(acdb_file_man_context.file_man_lock); | ||
| return AR_EALREADY; | ||
| } | ||
| } |
There was a problem hiding this comment.
[FUNCTIONALITY] Medium Severity - Missing Mutex Unlock in Error Path
In acdb_file_mgr.c, the AcdbFileManAddDatabase function acquires a mutex lock at line 633 but fails to release it before returning an error at line 651 when a duplicate database file is detected.
Issue: When a duplicate database file is found, the function logs an error and returns AR_EALREADY without calling ACDB_MUTEX_UNLOCK(acdb_file_man_context.file_man_lock). This leaves the mutex locked.
Impact: After detecting a duplicate file, the mutex remains locked, causing any subsequent calls to AcdbFileManAddDatabase or other functions using the same mutex to deadlock.
Fixed Code Snippet
if (NULL == ar_strstr(&fm_db_info->database_file.path[0], &db_file->path[0]))
continue;
else
{
ACDB_ERR("Error[%d]: The database file %s already exists", AR_EALREADY,
db_file->path);
ACDB_MUTEX_UNLOCK(acdb_file_man_context.file_man_lock);
return AR_EALREADY;
}Severity: Medium - Causes mutex deadlock when duplicate files are detected.
|
|
||
| ar_heap_free(message_buffer.buffer, &heap_inf); | ||
| ar_heap_free(recieve_buffer.buffer, &heap_inf); | ||
| ar_heap_free(outbuf, &heap_inf); | ||
|
|
||
| is_connected = false; | ||
| ar_osal_mutex_unlock(connection_lock); |
There was a problem hiding this comment.
[FUNCTIONALITY] High Severity - Missing Resource Cleanup on Thread Exit
In tcpip_cmd_server.cpp, the transmit_routine function allocates outbuf using ar_heap_malloc but doesn't free it before returning at line 553. This causes a memory leak every time a transmission thread completes.
Issue: The function allocates memory for outbuf at line 479 but only frees message_buffer.buffer and recieve_buffer.buffer in the cleanup section (lines 547-548). The outbuf allocation is never freed.
Impact: Each client connection that completes normally leaks the memory allocated for outbuf (up to maxsize bytes, which is ATS_BUFFER_LENGTH). Over time, this causes significant memory consumption.
Fixed Code Snippet
//Cleanup
TCPIP_CMD_SVR_DBG("%s: closing thread", thd_name.c_str());
ar_heap_free(message_buffer.buffer, &heap_inf);
ar_heap_free(recieve_buffer.buffer, &heap_inf);
ar_heap_free(outbuf, &heap_inf); // Added: Free outbuf
is_connected = false;
ar_osal_mutex_unlock(connection_lock);
return 0;Severity: High - Causes memory leak on every client connection.
Fix a heap buffer overflow in the diag transport, a double free during transport startup failure handling, and mutex locking bugs that could deadlock the TCP command and diagnostic servers. Also validate failure paths in AMDB command handlers and release locks
correctly when the file manager encounters existing database files.