Donny9 opened a new pull request, #17745:
URL: https://github.com/apache/nuttx/pull/17745
## Summary
net/local: simplify file descriptor passing using shared filep references
This commit removes unnecessary file duplication and memory allocation when
passing file descriptors through Unix domain sockets, leveraging the new filep
reference counting framework.
Background:
With the new filep framework, file structures (filep) can be safely shared
across processes using reference counting. When passing file descriptors
through SCM_RIGHTS control messages on local sockets, the previous
implementation unnecessarily duplicated the entire file structure.
Changes:
1. In local_sendctl() (sender side):
- Removed allocation of filep2 structure (kmm_zalloc)
- Removed file_dup2() call that copied the file structure
- Now directly stores the original filep with its reference count
- Eliminates memory allocation overhead and potential failure points
2. In local_recvctl() (receiver side):
- Changed from file_close() + kmm_free() to file_put()
- file_put() properly decrements reference count and handles cleanup
- Consistent with the new filep reference counting model
3. In local_freectl() (cleanup on error):
- Changed from file_close() + kmm_free() to file_put()
- Ensures proper reference count management during error paths
Technical Details:
The new filep framework ensures that:
- Multiple file descriptors across different processes can reference the
same underlying filep structure safely
- Reference counting (via file_get/file_put) manages lifetime correctly
- The underlying file object is only released when all references are gone
This change is safe because file_dup() in the receiver already calls
file_get() internally to increment the reference count for the new fd, so the
filep remains valid after the sender calls file_put().
## Impact
Bug fix about SCM_RIGHTS
## Testing
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#define TEST_DATA "Test data for FD passing"
int main(void)
{
int sv[2]; /* Socket pair */
int test_fd;
int received_fd;
char buffer[256];
struct msghdr msg = {0};
struct iovec iov[1];
struct cmsghdr *cmsg;
char ctrl_buf[CMSG_SPACE(sizeof(int))];
char data[1] = {'X'};
ssize_t n;
printf("=== Unix Domain Socket FD Passing Test ===\n\n");
/* Create a socketpair (simpler than client/server) */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0)
{
printf("socketpair() failed: %s\n", strerror(errno));
return 1;
}
printf("Step 1: Created socket pair (sv[0]=%d, sv[1]=%d)\n", sv[0], sv[1]);
/* Create a test file */
test_fd = open("/tmp/test_fd.txt", O_CREAT | O_RDWR | O_TRUNC, 0644);
if (test_fd < 0)
{
printf("open() failed: %s\n", strerror(errno));
return 1;
}
write(test_fd, TEST_DATA, strlen(TEST_DATA));
printf("Step 2: Created test file with fd=%d\n", test_fd);
/* Send the file descriptor through socket */
iov[0].iov_base = data;
iov[0].iov_len = sizeof(data);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = ctrl_buf;
msg.msg_controllen = sizeof(ctrl_buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &test_fd, sizeof(int));
if (sendmsg(sv[0], &msg, 0) < 0)
{
printf("sendmsg() failed: %s\n", strerror(errno));
return 1;
}
printf("Step 3: Sent fd %d through socket sv[0]\n", test_fd);
/* Receive the file descriptor */
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = ctrl_buf;
msg.msg_controllen = sizeof(ctrl_buf);
if (recvmsg(sv[1], &msg, 0) < 0)
{
printf("recvmsg() failed: %s\n", strerror(errno));
return 1;
}
cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg && cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS)
{
memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
printf("Step 4: Received fd %d through socket sv[1]\n", received_fd);
}
else
{
printf("Failed to receive file descriptor\n");
return 1;
}
/* Verify the received fd works */
lseek(received_fd, 0, SEEK_SET);
n = read(received_fd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
printf("Step 5: Read from received fd: '%s'\n", buffer);
if (strcmp(buffer, TEST_DATA) == 0)
{
printf("✓ Data verification PASSED\n");
}
else
{
printf("✗ Data mismatch!\n");
}
}
else
{
printf("✗ Failed to read from received fd\n");
return 1;
}
/* Test reference counting: close original, verify received still works */
printf("\nStep 6: Closing original fd %d\n", test_fd);
close(test_fd);
lseek(received_fd, 0, SEEK_SET);
n = read(received_fd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
printf("Step 7: Received fd still works after closing original:
'%s'\n",
buffer);
printf("✓ Reference counting PASSED\n");
}
else
{
printf("✗ Received fd doesn't work after closing original\n");
return 1;
}
/* Write through received fd */
const char *new_data = "\nAdded by receiver";
if (write(received_fd, new_data, strlen(new_data)) > 0)
{
printf("Step 8: Write through received fd successful\n");
printf("✓ Write test PASSED\n");
}
else
{
printf("✗ Write through received fd failed\n");
}
/* Cleanup */
close(received_fd);
close(sv[0]);
close(sv[1]);
unlink("/tmp/test_fd.txt");
printf("\n=== All tests PASSED! ===\n");
return 0;
}
```
<img width="955" height="548" alt="image"
src="https://github.com/user-attachments/assets/090405c9-b2d8-4aed-a407-c0c8ab42bd77"
/>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]