Revert "Fix risky unwrap(), expect(), and casting"#1118
Conversation
This reverts commit d6ccff7.
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
This PR reverts changes from PR #1113 that fixed risky unwrap(), expect(), and casting operations. The revert reintroduces several problematic patterns that can cause panics and prevent proper error handling:
- Reverts file descriptor types from signed (
int32_t) back to unsigned (uint32_t) in C API, preventing proper error handling when FDs are -1 - Reintroduces
.unwrap()and.expect()calls in Rust code where proper error handling was previously added - Replaces error handling in Drop implementations with
.expect()which can cause double panics - Removes the
Exception::new()helper method that was added for creating Rust-side exceptions
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 24 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/c_api_ut.cpp | Changes file descriptor variables from int32_t to uint32_t in three test functions |
| crates/swss-common/src/types/zmqconsumerstatetable.rs | Reverts error handling for file descriptors and timeouts, reintroduces .unwrap() and .expect() in Drop |
| crates/swss-common/src/types/zmqclient.rs | Replaces safe Drop error logging with .expect() that can panic |
| crates/swss-common/src/types/table.rs | Replaces safe Drop error logging with .expect() that can panic |
| crates/swss-common/src/types/subscriberstatetable.rs | Reverts error handling for file descriptors and timeouts, reintroduces .expect() in Drop |
| crates/swss-common/src/types/producerstatetable.rs | Replaces safe Drop error logging with .expect() that can panic |
| crates/swss-common/src/types/exception.rs | Removes the Exception::new() helper method and its documentation |
| crates/swss-common/src/types/dbconnector.rs | Adds Clone impl that panics on failure and replaces safe Drop error logging with .expect() |
| crates/swss-common/src/types/consumerstatetable.rs | Reverts error handling for file descriptors and timeouts, reintroduces .expect() in Drop |
| common/c-api/zmqconsumerstatetable.h | Changes file descriptor parameter type from int32_t to uint32_t |
| common/c-api/zmqconsumerstatetable.cpp | Adds numeric_cast<uint32_t>() that throws on negative file descriptors |
| common/c-api/subscriberstatetable.h | Changes file descriptor parameter type from int32_t to uint32_t |
| common/c-api/subscriberstatetable.cpp | Adds numeric_cast<uint32_t>() that throws on negative file descriptors |
| common/c-api/consumerstatetable.h | Changes file descriptor parameter type from int32_t to uint32_t |
| common/c-api/consumerstatetable.cpp | Adds numeric_cast<uint32_t>() that throws on negative file descriptors |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| eprintln!("Error dropping SubscriberStateTable: {}", e); | ||
| } | ||
| } | ||
| unsafe { swss_try!(SWSSSubscriberStateTable_free(self.ptr)).expect("Dropping SubscriberStateTable") }; |
There was a problem hiding this comment.
Using .expect() in a Drop implementation will panic if the free operation fails. The removed error handling that logged the error with eprintln! was more appropriate, as panicking in Drop can cause double panics and program aborts.
| unsafe { swss_try!(SWSSSubscriberStateTable_free(self.ptr)).expect("Dropping SubscriberStateTable") }; | |
| if let Err(e) = unsafe { swss_try!(SWSSSubscriberStateTable_free(self.ptr)) } { | |
| eprintln!("Failed to free SubscriberStateTable: {:?}", e); | |
| } |
| pub fn read_data(&self, timeout: Duration, interrupt_on_signal: bool) -> Result<SelectResult> { | ||
| let timeout_ms: u32 = timeout.as_millis().try_into() | ||
| .map_err(|_| Exception::new("Invalid timeout value"))?; | ||
| let timeout_ms = timeout.as_millis().try_into().unwrap(); |
There was a problem hiding this comment.
Using .try_into().unwrap() will panic if timeout conversion fails (e.g., if timeout is longer than u32::MAX milliseconds). The removed error handling with map_err was more robust.
| // After the fd becomes readable, SWSSZmqConsumerStateTable_readData must be used to | ||
| // reset the fd and read data into internal data structures. | ||
| SWSSResult SWSSZmqConsumerStateTable_getFd(SWSSZmqConsumerStateTable tbl, int32_t *outFd); | ||
| SWSSResult SWSSZmqConsumerStateTable_getFd(SWSSZmqConsumerStateTable tbl, uint32_t *outFd); |
There was a problem hiding this comment.
Changing from int32_t to uint32_t for file descriptors is incorrect. File descriptors are represented as int (signed) in POSIX where -1 indicates an error. Using uint32_t prevents proper error handling.
| SWSSResult SWSSZmqConsumerStateTable_getFd(SWSSZmqConsumerStateTable tbl, uint32_t *outFd); | |
| SWSSResult SWSSZmqConsumerStateTable_getFd(SWSSZmqConsumerStateTable tbl, int32_t *outFd); |
| eprintln!("Error dropping ZmqClient: {}", e); | ||
| } | ||
| } | ||
| unsafe { swss_try!(SWSSZmqClient_free(self.ptr)).expect("Dropping ZmqClient") }; |
There was a problem hiding this comment.
Using .expect() in a Drop implementation will panic if the free operation fails. The removed error handling that logged the error with eprintln! was more appropriate, as panicking in Drop can cause double panics and program aborts.
| unsafe { swss_try!(SWSSZmqClient_free(self.ptr)).expect("Dropping ZmqClient") }; | |
| if let Err(e) = unsafe { swss_try!(SWSSZmqClient_free(self.ptr)) } { | |
| eprintln!("Error dropping ZmqClient: {:?}", e); | |
| } |
| return Err(Exception::new("Invalid file descriptor: -1")); | ||
| } | ||
| let fd = BorrowedFd::borrow_raw(fd); | ||
| let fd = BorrowedFd::borrow_raw(fd.try_into().unwrap()); |
There was a problem hiding this comment.
Using .try_into().unwrap() to convert from a signed file descriptor to an unsigned type will panic if the FD is -1 (error indicator), instead of properly handling the error. The removed error handling code that checked for -1 was correct.
| // SAFETY: This fd represents the underlying ZMQ socket, which should stay alive | ||
| // as long as this object does. | ||
| // SAFETY: This fd represents the underlying zmq socket, which should remain alive as long as there | ||
| // is a listener (i.e. a ZmqConsumerStateTable) |
There was a problem hiding this comment.
The comment has a grammatical error: "should remain alive as long as there is a listener" should be "should remain alive as long as there is a listener (i.e., a ZmqConsumerStateTable)." The removed comment was clearer and more grammatically correct.
| // is a listener (i.e. a ZmqConsumerStateTable) | |
| // is a listener (i.e., a ZmqConsumerStateTable). |
| // After the fd becomes readable, SWSSConsumerStateTable_readData must be used to | ||
| // reset the fd and read data into internal data structures. | ||
| SWSSResult SWSSConsumerStateTable_getFd(SWSSConsumerStateTable tbl, int32_t *outFd); | ||
| SWSSResult SWSSConsumerStateTable_getFd(SWSSConsumerStateTable tbl, uint32_t *outFd); |
There was a problem hiding this comment.
Changing from int32_t to uint32_t for file descriptors is problematic. File descriptors in POSIX are represented as int (signed) where valid FDs are >= 0 and -1 is used to indicate errors. The C++ getFd() method returns int, which can be negative.
Using numeric_cast<uint32_t>() on a potentially negative value will throw an exception, which would prevent proper error handling if the underlying C++ code returns -1 to indicate an error condition. The previous code using int32_t was more appropriate for handling file descriptors.
| SWSSResult SWSSConsumerStateTable_getFd(SWSSConsumerStateTable tbl, uint32_t *outFd); | |
| SWSSResult SWSSConsumerStateTable_getFd(SWSSConsumerStateTable tbl, int32_t *outFd); |
| SWSSResult SWSSConsumerStateTable_getFd(SWSSConsumerStateTable tbl, uint32_t *outFd) { | ||
| SWSSTry(*outFd = numeric_cast<uint32_t>(((ConsumerStateTable *)tbl)->getFd())); |
There was a problem hiding this comment.
Using numeric_cast<uint32_t>() to convert from int (which can be negative) to uint32_t will throw an exception if the file descriptor is -1 (which indicates an error). This prevents proper error handling. File descriptors should remain as signed integers to handle the -1 error case.
| SWSSResult SWSSConsumerStateTable_getFd(SWSSConsumerStateTable tbl, uint32_t *outFd) { | |
| SWSSTry(*outFd = numeric_cast<uint32_t>(((ConsumerStateTable *)tbl)->getFd())); | |
| SWSSResult SWSSConsumerStateTable_getFd(SWSSConsumerStateTable tbl, int32_t *outFd) { | |
| SWSSTry(*outFd = ((ConsumerStateTable *)tbl)->getFd()); |
| eprintln!("Error dropping ConsumerStateTable: {}", e); | ||
| } | ||
| } | ||
| unsafe { swss_try!(SWSSConsumerStateTable_free(self.ptr)).expect("Dropping ConsumerStateTable") }; |
There was a problem hiding this comment.
Using .expect() in a Drop implementation will panic if the free operation fails. The removed error handling that logged the error with eprintln! was more appropriate, as panicking in Drop can cause double panics and program aborts.
| unsafe { swss_try!(SWSSConsumerStateTable_free(self.ptr)).expect("Dropping ConsumerStateTable") }; | |
| if let Err(e) = unsafe { swss_try!(SWSSConsumerStateTable_free(self.ptr)) } { | |
| eprintln!("Error dropping ConsumerStateTable: {:?}", e); | |
| } |
| // After the fd becomes readable, SWSSSubscriberStateTable_readData must be used to | ||
| // reset the fd and read data into internal data structures. | ||
| SWSSResult SWSSSubscriberStateTable_getFd(SWSSSubscriberStateTable tbl, int32_t *outFd); | ||
| SWSSResult SWSSSubscriberStateTable_getFd(SWSSSubscriberStateTable tbl, uint32_t *outFd); |
There was a problem hiding this comment.
Changing from int32_t to uint32_t for file descriptors is incorrect. File descriptors are represented as int (signed) in POSIX where -1 indicates an error. Using uint32_t prevents proper error handling.
| SWSSResult SWSSSubscriberStateTable_getFd(SWSSSubscriberStateTable tbl, uint32_t *outFd); | |
| SWSSResult SWSSSubscriberStateTable_getFd(SWSSSubscriberStateTable tbl, int32_t *outFd); |
|
I will force merge to unblock sonic-buildimage PR build and master branch build. There is no bug in the reverted commit, and we will bring it back after fixing other Rust repos. |
…onic-net#1118) This reverts commit 7682785.
…1124) This reverts commit 7682785. Following the proper fix sonic-swss (sonic-net/sonic-swss#4028). Now we could bring back the commit. What I did in the PR: Fix several potential crash issues: unwrap() function may crash in runtime. Drop should not crash. Clone should not crash, so remove it. Protect casting Duration
…onic-net#1118) Following the proper fix sonic-swss (sonic-net/sonic-swss#4028). Now we could bring back the commit. What I did in the PR: Fix several potential crash issues: 1. unwrap() function may crash in runtime. 2. Drop should not crash. 3. Clone should not crash, so remove it. 4. Protect casting Duration
…1136) Following the proper fix sonic-swss (sonic-net/sonic-swss#4028). Now we could bring back the commit. What I did in the PR: Fix several potential crash issues: 1. unwrap() function may crash in runtime. 2. Drop should not crash. 3. Clone should not crash, so remove it. 4. Protect casting Duration Co-authored-by: Vineet Mittal <46945843+vmittal-msft@users.noreply.github.com>
|
Cherry-pick PR to 202511: #1172 |
|
@liushilongbuaa cherry pick PR didn't pass PR checker after retry. Please help check! Thanks. ---Powered by SONiC BuildBot
|
Reverts #1113
https://github.com/sonic-net/sonic-swss/blob/eae91a23c01bcc0b1e915ab46c5228a854a6e42e/Cargo.toml#L67
sonic-swss refers to sonic-swss-common's latest commit.
So changes in swss-common breaks SONiC's build.