Skip to content

Conversation

@realsdx
Copy link

@realsdx realsdx commented Feb 3, 2025

This PR adds a new API to enable addfd support for seccomp notifiers. It introduces changes to the seccomp_notif and seccomp_notif_resp structures to handle the addfd operation, allowing file descriptors to be passed through a seccomp notifier.

It also adds supporting man page entry and unit regression tests for both C and Python APIs.

@pcmoore
Copy link
Member

pcmoore commented Feb 3, 2025

Hi @realsdx, thanks for the PR :)

Unfortunately, it looks like the the CI test are failing because of a missing seccomp_notif_addfd struct declaration due to an older userspace/headers on the build/CI system. Look at how we handle seccomp_notif and seccomp_notif_resp in the "include/seccomp.h.in" file for an example on how to fix this:

/* SECCOMP_RET_USER_NOTIF was added in kernel v5.0. */
#ifndef SECCOMP_RET_USER_NOTIF
#define SECCOMP_RET_USER_NOTIF 0x7fc00000U
struct seccomp_notif {
__u64 id;
__u32 pid;
__u32 flags;
struct seccomp_data data;
};
struct seccomp_notif_resp {
__u64 id;
__s64 val;
__s32 error;
__u32 flags;
};
#endif

@pcmoore
Copy link
Member

pcmoore commented Feb 4, 2025

FWIW, if I made the following changes on top of your PR I was able to get everything building:

diff --git a/include/seccomp.h.in b/include/seccomp.h.in
index 024aca537f64..568eabaad949 100644
--- a/include/seccomp.h.in
+++ b/include/seccomp.h.in
@@ -406,6 +406,11 @@ struct seccomp_notif_resp {
        __u32 flags;
 };
 
+#endif
+
+/* seccomp_notif_addfd and ADDFD_FLAG_SETFD was added in kernel v5.10 */
+#ifndef SECCOMP_ADDFD_FLAG_SETFD
+#define SECCOMP_ADDFD_FLAG_SETFD       (1UL << 0)
 struct seccomp_notif_addfd {
        __u64 id;
        __u32 flags;
diff --git a/src/system.h b/src/system.h
index 495050fb2e75..87bf8a6e832d 100644
--- a/src/system.h
+++ b/src/system.h
@@ -180,14 +180,6 @@ struct seccomp_notif_resp {
        __u32 flags;
 };
 
-struct seccomp_notif_addfd {
-       __u64 id;
-       __u32 flags;
-       __u32 srcfd;
-       __u32 newfd;
-       __u32 newfd_flags;
-};
-
 #define SECCOMP_IOC_MAGIC               '!'
 #define SECCOMP_IO(nr)                  _IO(SECCOMP_IOC_MAGIC, nr)
 #define SECCOMP_IOR(nr, type)           _IOR(SECCOMP_IOC_MAGIC, nr, type)
@@ -201,6 +193,24 @@ struct seccomp_notif_addfd {
 #define SECCOMP_IOCTL_NOTIF_ID_VALID    SECCOMP_IOW(2, __u64)
 #endif /* SECCOMP_RET_USER_NOTIF */
 
+/* seccomp_notif_addfd and ADDFD_FLAG_SETFD was added in kernel v5.10 */
+#ifndef SECCOMP_ADDFD_FLAG_SETFD
+#define SECCOMP_ADDFD_FLAG_SETFD       (1UL << 0)
+struct seccomp_notif_addfd {
+       __u64 id;
+       __u32 flags;
+       __u32 srcfd;
+       __u32 newfd;
+       __u32 newfd_flags;
+};
+#endif
+
+/* SECCOMP_IOCTL_NOTIF_ADDFD was added in kernel v5.10 */
+#ifndef SECCOMP_IOCTL_NOTIF_ADDFD
+#define SECCOMP_IOCTL_NOTIF_ADDFD      SECCOMP_IOW(3, \
+                                                   struct seccomp_notif_addfd)
+#endif
+
 /* non-public ioctl number for backwards compat (see system.c) */
 #define SECCOMP_IOCTL_NOTIF_ID_VALID_WRONG_DIR SECCOMP_IOR(2, __u64)

@pcmoore
Copy link
Member

pcmoore commented Feb 4, 2025

Another quick comment, we'll likely want to add a new API level to seccomp_api_get() to indicate ADDFD support.

@realsdx
Copy link
Author

realsdx commented Feb 5, 2025

Another quick comment, we'll likely want to add a new API level to seccomp_api_get() to indicate ADDFD support.

The current API levels are based on different Seccomp actions and filter flags. If we add a new level for addfd support, it would be based on IOCTL support.

What would be the best way to determine the addfd support at runtime? One issue with probing the IOCTL with notifyfd is that we need a valid notify fd first. Since we cannot uninstall the seccomp filter once it's loaded, is it a good idea to probe for ADDFD IOCTL?

@coveralls
Copy link

coveralls commented Feb 9, 2025

Coverage Status

coverage: 88.845% (-0.2%) from 89.046%
when pulling 3de75bb on realsdx:dev/addfd
into 2bc7189 on seccomp:main.

@pcmoore
Copy link
Member

pcmoore commented Mar 18, 2025

What would be the best way to determine the addfd support at runtime? One issue with probing the IOCTL with notifyfd is that we need a valid notify fd first. Since we cannot uninstall the seccomp filter once it's loaded, is it a good idea to probe for ADDFD IOCTL?

My apologies, I suspect I was stuck in the thinking that we were doing this through the seccomp() syscall somehow ... but yes, I see what you mean about the ioctl issue. I'm looking at the associated kernel code to see if there something in there for us to use to query for the functionality without actually doing anything, and nothing is jumping out at me. The closest I can think of would be to pass a bogus file descriptor and see if -EBADF is returned; if the kernel support is missing we should see -EINVAL. However, I'm not sure we want to risk doing that in a library, that seems like a Bad Thing.

@drakenclimber do you have any thoughts on this?

@pcmoore
Copy link
Member

pcmoore commented Apr 8, 2025

Okay, I'm not seeing any good ideas around the runtime detection so let's just go ahead and skip that. Let me take a closer look at the rest of the PR ...

Copy link
Member

@pcmoore pcmoore left a comment

Choose a reason for hiding this comment

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

Added some thoughts on the PR, overall it looks good, just a few minor things to polish up :)

@pcmoore
Copy link
Member

pcmoore commented Apr 8, 2025

@realsdx mentioned SECCOMP_ADDFD_FLAG_SEND in a separate chat, and I think it might be a nice thing to add both SECCOMP_ADDFD_FLAG_SETFD and SECCOMP_ADDFD_FLAG_SEND to this PR, thoughts?

For reference, SECCOMP_ADDFD_FLAG_SEND injects a file descriptor into the target's file descriptor table while also atomically clearing the target's errno and setting the syscall return value equal to the value of the newly injected file descriptor. More information can be found in the manpage.

Copy link

@timscharfenort8 timscharfenort8 left a comment

Choose a reason for hiding this comment

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

Good work

@pcmoore
Copy link
Member

pcmoore commented May 9, 2025

This is looking good @realsdx, did you want to do another push to this PR branch with all of the changes discussed and remove the draft flag on this PR?

@realsdx realsdx marked this pull request as ready for review May 15, 2025 21:17
@realsdx realsdx requested a review from pcmoore May 15, 2025 21:17
src/system.h Outdated
__u32 newfd;
__u32 newfd_flags;
};
#endif
Copy link
Member

Choose a reason for hiding this comment

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

Since we're defining this in include/seccomp.h we shouldn't need it here, unless I'm misunderstanding something?

src/system.h Outdated
/* Addfd and return it, atomically. ADDFD_FLAG_SEND was added in kernel 5.14 */
#ifndef SECCOMP_ADDFD_FLAG_SEND
#define SECCOMP_ADDFD_FLAG_SEND (1UL << 1)
#endif
Copy link
Member

Choose a reason for hiding this comment

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

Same thing here, we already define this in include/seccomp.h if the system headers don't define it for us.

Copy link
Author

Choose a reason for hiding this comment

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

This part I am not quite sure yet, as this whole thing only gets loaded when the kernel's seccomp headers are not available. But why would we need to keep the SECCOMP_IOCTL_NOTIF_ADDFD then?

Copy link
Member

Choose a reason for hiding this comment

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

In the early days of libseccomp it was not uncommon to build the library on a system that was running an older kernel without libseccomp support and the necessary kernel header files. Including the relevant kernel header bits (macros, structure definitions) allows us to build the libseccomp library on a system without libseccomp support, but still have it run as one would expect on a modern system with a Linux kernel that fully supports seccomp.

Does that help?

Copy link
Author

Choose a reason for hiding this comment

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

Okay makes sense. I will remove the duplicate flags and structs. Thanks for clarifying.

@pcmoore
Copy link
Member

pcmoore commented Aug 18, 2025

@drakenclimber aside from the two things mentioned above, what do you think of this?

@drakenclimber
Copy link
Member

@drakenclimber aside from the two things mentioned above, what do you think of this?

I'll look into it this week. Thanks for reminding me :)

Copy link
Member

@drakenclimber drakenclimber left a comment

Choose a reason for hiding this comment

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

I think this is a really good first cut. I had a few comments that will require some minor rework, but overall it's solid.

Thanks for the contribution 👍

/* seccomp_notif_addfd and ADDFD_FLAG_SETFD was added in kernel v5.10 */
#ifndef SECCOMP_ADDFD_FLAG_SETFD
#define SECCOMP_ADDFD_FLAG_SETFD (1UL << 0)
struct seccomp_notif_addfd {
Copy link
Member

Choose a reason for hiding this comment

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

Why is the struct seccomp_notif_addfd() definition inside of the #ifndef SECCOMP_ADDFD_FLAG_SETFD? Seems strange to me.

Copy link
Member

Choose a reason for hiding this comment

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

That's a fair question. The seccomp_notif_addfd struct is only used when SECCOMP_ADDFD_FLAG_SETFD is present and supported, so using the presence of SECCOMP_ADDFD_FLAG_SETFD to serve as a proxy for the definition of seccomp_notif_addfd seems like a reasonable thing to do.

I'm definitely open to other ideas and I'm sure @realsdx would be as well.

https://man7.org/linux/man-pages/man2/seccomp_unotify.2.html

src/system.c Outdated
if (state.sup_user_notif <= 0)
return -EOPNOTSUPP;

int rc = ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD, addfd);
Copy link
Member

Choose a reason for hiding this comment

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

As a convention, we define variables at the start of the function in libseccomp. (Aside - this is a relic of the C89 standard, but it's generally good practice today even though it's no longer required.)

close(sock_pair[1]);
exit(bytes_read); // bytes_read should be 0, as it's reading /dev/null
} else {
close(sock_pair[1]); // close the child's end
Copy link
Member

Choose a reason for hiding this comment

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

I would recommend moving the parent code to its own function. This will improve readability and better scope the variables that are only needed by the parent process.

And, as outlined above, it will fix the erroneous goto logic in the child, as you'll have to handle that properly in its own function

goto out;
}

new_fd = openat(AT_FDCWD, "/dev/null", O_RDONLY);
Copy link
Member

Choose a reason for hiding this comment

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

Does reading /dev/null provide a good test (since it always results in 0 bytes read)? Or would reading /dev/zero be a better test? Reading it could provide a non-zero length.

I don't have a strong preference here - just wondering

Copy link
Author

Choose a reason for hiding this comment

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

The intent is to simulate a behavior, where the supervisee wants to access a 'restricted' file but gets nothing. Let me know if that makes sense.

@drakenclimber
Copy link
Member

What would be the best way to determine the addfd support at runtime? One issue with probing the IOCTL with notifyfd is that we need a valid notify fd first. Since we cannot uninstall the seccomp filter once it's loaded, is it a good idea to probe for ADDFD IOCTL?

My apologies, I suspect I was stuck in the thinking that we were doing this through the seccomp() syscall somehow ... but yes, I see what you mean about the ioctl issue. I'm looking at the associated kernel code to see if there something in there for us to use to query for the functionality without actually doing anything, and nothing is jumping out at me. The closest I can think of would be to pass a bogus file descriptor and see if -EBADF is returned; if the kernel support is missing we should see -EINVAL. However, I'm not sure we want to risk doing that in a library, that seems like a Bad Thing.

@drakenclimber do you have any thoughts on this?

I don't have any better ideas here either. Bummer.

@realsdx realsdx force-pushed the dev/addfd branch 3 times, most recently from 24c9882 to 805517e Compare October 4, 2025 00:29
@realsdx
Copy link
Author

realsdx commented Oct 4, 2025

First of all, apologies for the delay. I was on a long vacation, and then some work piled up. I should’ve replied much sooner!
Thanks a lot for the thorough review. I definitely made a few silly mistakes and some poor choices, really appreciate you pointing those out.

I’ve made several updates to the C test code (refactored the parent and child functions) and cleaned up some of the debug code.
A couple of things are still pending:

  • Refactoring the changes into 3 commits, as suggested by @drakenclimber
  • Fixing the duplicate macros in the system.h file

I still have a few doubts regarding the duplicate macros @pcmoore

@pcmoore
Copy link
Member

pcmoore commented Oct 7, 2025

I still have a few doubts regarding the duplicate macros @pcmoore

I think I just answered those in the code review/comment section, but please let me know if that still doesn't make sense.

@realsdx
Copy link
Author

realsdx commented Oct 8, 2025

Thank you, @pcmoore, for the explanation. It's much clearer to me now. I'll update the code with the remaining changes.

Add support for the seccomp notify addfd feature in the core API. It allows a caller to install fd in target's fd table.

Signed-off-by: Sudipta Pandit <sudpandit@microsoft.com>
Add seccomp notify addfd support to the Python API and update the receive_notify and respond_notify methods to accept user-provided notify fd's as an optional argument.

Signed-off-by: Sudipta Pandit <sudpandit@microsoft.com>
Add live test for seccomp_notify_addfd() that verifies fd injection capabilities by intercepting openat() syscalls and replacing the target's file descriptor for it's C and Python API.

Signed-off-by: Sudipta Pandit <sudpandit@microsoft.com>
Signed-off-by: Sudipta Pandit <sudpandit@microsoft.com>
Signed-off-by: Sudipta Pandit <sudpandit@microsoft.com>
@pcmoore
Copy link
Member

pcmoore commented Nov 25, 2025

What do you think @drakenclimber ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants