Skip to content

Conversation

@yue-fred-gao
Copy link
Contributor

why I did it

unwrap() and expect function may crash at runtime. For the issues that may happen, we should catch the error and handle it gracefully instead of crashing.

This PR focuses unwrap calls in types.rs

How I did it

Return error if it fails to convert c-type string to rust str. For the case of take-style functions, such as take_cstr, it will continue the operation and free the memory then return the error. This is to avoid memory leak. Ideally, the functions should act atomically. If it fails, it leaves input intact. However, it requires copy memory or scan twice, which is not efficient for some very rare error and the caller needs to free the memory anyway.

- take_cstr
- take_string_array
- take_field_value_array
- take_key_op_field_values_array

Signed-off-by: Yue Gao <[email protected]>
 - from make_field_value_array
 - from make_key_op_field_values_array

Signed-off-by: Yue Gao <[email protected]>
@mssonicbld
Copy link
Collaborator

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@yue-fred-gao
Copy link
Contributor Author

hi @qiluo-msft and @saiarcot895, please review

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR systematically removes unwrap() and expect() calls from types.rs and propagates error handling changes throughout the codebase. The changes convert C string operations to return Result types, enabling graceful error handling instead of runtime panics when encountering null bytes or invalid UTF-8 sequences.

Key Changes:

  • Modified cstr() and take_cstr() to return Result types with descriptive error messages
  • Updated take_field_value_array(), take_key_op_field_values_array(), and take_string_array() to handle conversion errors while ensuring all C memory is freed
  • Propagated ? operator changes to ~80 call sites across 14 files

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
crates/swss-common/src/types.rs Core changes: converted string conversion functions to return Result, updated array processing functions with error handling and memory cleanup
crates/swss-common/src/types/zmqserver.rs Propagated error handling for endpoint parameter
crates/swss-common/src/types/zmqproducerstatetable.rs Updated table_name, key parameters and field value array creation to use ? operator
crates/swss-common/src/types/zmqconsumerstatetable.rs Updated table_name parameter and pops() return to propagate errors
crates/swss-common/src/types/zmqclient.rs Updated endpoint, db_name, table_name parameters and array creation to use ? operator
crates/swss-common/src/types/table.rs Updated key, field parameters and array functions to propagate errors
crates/swss-common/src/types/subscriberstatetable.rs Updated table_name parameter and pops() return to propagate errors
crates/swss-common/src/types/sonicv2connector.rs Updated multiple string parameters with good use of .transpose() for Optional parameters
crates/swss-common/src/types/producerstatetable.rs Updated table_name, key parameters and field value array creation to use ? operator
crates/swss-common/src/types/logger.rs Updated db_name and hardcoded string literals to use ? operator
crates/swss-common/src/types/events.rs Updated event_source and event_tag parameters; good use of .collect::<Result<Vec<_>>>() pattern
crates/swss-common/src/types/dbconnector.rs Updated connection parameters (hostname, sock_path, db_name, etc.) and data access methods to propagate errors
crates/swss-common/src/types/consumerstatetable.rs Updated table_name parameter and pops() return to propagate errors
crates/swss-common/src/types/configdbconnector.rs Updated netns, table, key parameters and array functions to propagate errors
crates/swss-common/src/lib.rs Updated path parameters in config initialization functions to use ? operator

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 309 to 310
err = Some(e);
break;
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory leak: When take_cstr fails during iteration, it frees the current string s but doesn't free the remaining strings in the array. The break statement exits the loop early, leaving unprocessed strings that are never freed before SWSSStringArray_free(arr) is called.

To fix this, continue iterating through all strings to free them even after an error occurs, similar to the pattern used in take_field_value_array.

Suggested change
err = Some(e);
break;
// Record the first error, if any
if err.is_none() {
err = Some(e);
}
// Continue to next entry to ensure all C resources are freed
``` #Closed

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid Memory leak bug.

let arr = SWSSFieldValueArray {
data: data.as_mut_ptr(),
len: data.len().try_into().unwrap(),
len: data.len().try_into().map_err(|_| Exception::new("field value array length doesn't fit target type".to_string()))?,
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message "field value array length doesn't fit target type" is vague. Consider making it more specific by indicating what the actual length was and what the target type's maximum is, e.g., format!("field value array length {} exceeds maximum for target type", data.len())

Copilot uses AI. Check for mistakes.
let arr = SWSSKeyOpFieldValuesArray {
data: data.as_mut_ptr(),
len: data.len().try_into().unwrap(),
len: data.len().try_into().map_err(|_| Exception::new("key-op field values array length doesn't fit target type".to_string()))?,
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message "key-op field values array length doesn't fit target type" is vague. Consider making it more specific by indicating what the actual length was and what the target type's maximum is, e.g., format!("key-op field values array length {} exceeds maximum for target type", data.len())

Suggested change
len: data.len().try_into().map_err(|_| Exception::new("key-op field values array length doesn't fit target type".to_string()))?,
len: data.len().try_into().map_err(|_| Exception::new(format!(
"key-op field values array length {} exceeds maximum for target type (max {})",
data.len(),
usize::MAX
)))?,

Copilot uses AI. Check for mistakes.
Comment on lines +51 to 70
pub(crate) fn cstr(s: impl AsRef<[u8]>) -> Result<CString> {
CString::new(s.as_ref())
.map_err(|e| Exception::new(format!("String contains null byte at position {}", e.nul_position())))
}

/// Take a malloc'd c string and convert it to a native String
pub(crate) unsafe fn take_cstr(p: *const libc::c_char) -> String {
let s = CStr::from_ptr(p)
.to_str()
.expect("C string being converted to Rust String contains invalid UTF-8")
.to_string();
/// If any string fails to convert, returns an error and frees all allocated memory.
pub(crate) unsafe fn take_cstr(p: *const libc::c_char) -> Result<String> {
let cstr = CStr::from_ptr(p);
// Convert to Rust String, capturing UTF-8 conversion errors.
let result: Result<String> = match cstr.to_str() {
Ok(s) => Ok(s.to_string()),
Err(_) => Err(Exception::new("C string being converted to Rust String contains invalid UTF-8".to_string())),
};

// Free the original C string in all cases to avoid leaks.
libc::free(p as *mut libc::c_void);
s

result
}
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new error handling in cstr() for null bytes and in take_cstr() for invalid UTF-8 lacks test coverage. Consider adding tests that verify:

  1. cstr() correctly returns an error when given a string containing a null byte
  2. take_cstr() correctly handles and returns an error for invalid UTF-8 sequences while still freeing the C string
  3. The take_* array functions correctly handle errors while freeing all allocated memory

These error paths are now part of the API contract and should be tested to prevent regressions.

Copilot uses AI. Check for mistakes.
@mssonicbld
Copy link
Collaborator

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@mssonicbld
Copy link
Collaborator

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@mssonicbld
Copy link
Collaborator

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@yue-fred-gao
Copy link
Contributor Author

/azpw run

@mssonicbld
Copy link
Collaborator

/AzurePipelines run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@mssonicbld
Copy link
Collaborator

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@qiluo-msft qiluo-msft merged commit 3df0382 into sonic-net:master Dec 29, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants