|
45 | 45 | #include <sys/socket.h>
|
46 | 46 | #include <sys/ioctl.h>
|
47 | 47 | #include <linux/kcmp.h>
|
| 48 | +#include <sys/resource.h> |
48 | 49 |
|
49 | 50 | #include <unistd.h>
|
50 | 51 | #include <sys/syscall.h>
|
@@ -173,7 +174,9 @@ struct seccomp_metadata {
|
173 | 174 |
|
174 | 175 | #ifndef SECCOMP_FILTER_FLAG_NEW_LISTENER
|
175 | 176 | #define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
|
| 177 | +#endif |
176 | 178 |
|
| 179 | +#ifndef SECCOMP_RET_USER_NOTIF |
177 | 180 | #define SECCOMP_RET_USER_NOTIF 0x7fc00000U
|
178 | 181 |
|
179 | 182 | #define SECCOMP_IOC_MAGIC '!'
|
@@ -209,6 +212,39 @@ struct seccomp_notif_sizes {
|
209 | 212 | };
|
210 | 213 | #endif
|
211 | 214 |
|
| 215 | +#ifndef SECCOMP_IOCTL_NOTIF_ADDFD |
| 216 | +/* On success, the return value is the remote process's added fd number */ |
| 217 | +#define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOW(3, \ |
| 218 | + struct seccomp_notif_addfd) |
| 219 | + |
| 220 | +/* valid flags for seccomp_notif_addfd */ |
| 221 | +#define SECCOMP_ADDFD_FLAG_SETFD (1UL << 0) /* Specify remote fd */ |
| 222 | + |
| 223 | +struct seccomp_notif_addfd { |
| 224 | + __u64 id; |
| 225 | + __u32 flags; |
| 226 | + __u32 srcfd; |
| 227 | + __u32 newfd; |
| 228 | + __u32 newfd_flags; |
| 229 | +}; |
| 230 | +#endif |
| 231 | + |
| 232 | +struct seccomp_notif_addfd_small { |
| 233 | + __u64 id; |
| 234 | + char weird[4]; |
| 235 | +}; |
| 236 | +#define SECCOMP_IOCTL_NOTIF_ADDFD_SMALL \ |
| 237 | + SECCOMP_IOW(3, struct seccomp_notif_addfd_small) |
| 238 | + |
| 239 | +struct seccomp_notif_addfd_big { |
| 240 | + union { |
| 241 | + struct seccomp_notif_addfd addfd; |
| 242 | + char buf[sizeof(struct seccomp_notif_addfd) + 8]; |
| 243 | + }; |
| 244 | +}; |
| 245 | +#define SECCOMP_IOCTL_NOTIF_ADDFD_BIG \ |
| 246 | + SECCOMP_IOWR(3, struct seccomp_notif_addfd_big) |
| 247 | + |
212 | 248 | #ifndef PTRACE_EVENTMSG_SYSCALL_ENTRY
|
213 | 249 | #define PTRACE_EVENTMSG_SYSCALL_ENTRY 1
|
214 | 250 | #define PTRACE_EVENTMSG_SYSCALL_EXIT 2
|
@@ -3761,6 +3797,199 @@ TEST(user_notification_filter_empty_threaded)
|
3761 | 3797 | EXPECT_GT((pollfd.revents & POLLHUP) ?: 0, 0);
|
3762 | 3798 | }
|
3763 | 3799 |
|
| 3800 | +TEST(user_notification_addfd) |
| 3801 | +{ |
| 3802 | + pid_t pid; |
| 3803 | + long ret; |
| 3804 | + int status, listener, memfd, fd; |
| 3805 | + struct seccomp_notif_addfd addfd = {}; |
| 3806 | + struct seccomp_notif_addfd_small small = {}; |
| 3807 | + struct seccomp_notif_addfd_big big = {}; |
| 3808 | + struct seccomp_notif req = {}; |
| 3809 | + struct seccomp_notif_resp resp = {}; |
| 3810 | + /* 100 ms */ |
| 3811 | + struct timespec delay = { .tv_nsec = 100000000 }; |
| 3812 | + |
| 3813 | + memfd = memfd_create("test", 0); |
| 3814 | + ASSERT_GE(memfd, 0); |
| 3815 | + |
| 3816 | + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); |
| 3817 | + ASSERT_EQ(0, ret) { |
| 3818 | + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); |
| 3819 | + } |
| 3820 | + |
| 3821 | + /* Check that the basic notification machinery works */ |
| 3822 | + listener = user_notif_syscall(__NR_getppid, |
| 3823 | + SECCOMP_FILTER_FLAG_NEW_LISTENER); |
| 3824 | + ASSERT_GE(listener, 0); |
| 3825 | + |
| 3826 | + pid = fork(); |
| 3827 | + ASSERT_GE(pid, 0); |
| 3828 | + |
| 3829 | + if (pid == 0) { |
| 3830 | + if (syscall(__NR_getppid) != USER_NOTIF_MAGIC) |
| 3831 | + exit(1); |
| 3832 | + exit(syscall(__NR_getppid) != USER_NOTIF_MAGIC); |
| 3833 | + } |
| 3834 | + |
| 3835 | + ASSERT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0); |
| 3836 | + |
| 3837 | + addfd.srcfd = memfd; |
| 3838 | + addfd.newfd = 0; |
| 3839 | + addfd.id = req.id; |
| 3840 | + addfd.flags = 0x0; |
| 3841 | + |
| 3842 | + /* Verify bad newfd_flags cannot be set */ |
| 3843 | + addfd.newfd_flags = ~O_CLOEXEC; |
| 3844 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd), -1); |
| 3845 | + EXPECT_EQ(errno, EINVAL); |
| 3846 | + addfd.newfd_flags = O_CLOEXEC; |
| 3847 | + |
| 3848 | + /* Verify bad flags cannot be set */ |
| 3849 | + addfd.flags = 0xff; |
| 3850 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd), -1); |
| 3851 | + EXPECT_EQ(errno, EINVAL); |
| 3852 | + addfd.flags = 0; |
| 3853 | + |
| 3854 | + /* Verify that remote_fd cannot be set without setting flags */ |
| 3855 | + addfd.newfd = 1; |
| 3856 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd), -1); |
| 3857 | + EXPECT_EQ(errno, EINVAL); |
| 3858 | + addfd.newfd = 0; |
| 3859 | + |
| 3860 | + /* Verify small size cannot be set */ |
| 3861 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD_SMALL, &small), -1); |
| 3862 | + EXPECT_EQ(errno, EINVAL); |
| 3863 | + |
| 3864 | + /* Verify we can't send bits filled in unknown buffer area */ |
| 3865 | + memset(&big, 0xAA, sizeof(big)); |
| 3866 | + big.addfd = addfd; |
| 3867 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD_BIG, &big), -1); |
| 3868 | + EXPECT_EQ(errno, E2BIG); |
| 3869 | + |
| 3870 | + |
| 3871 | + /* Verify we can set an arbitrary remote fd */ |
| 3872 | + fd = ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd); |
| 3873 | + /* |
| 3874 | + * The child has fds 0(stdin), 1(stdout), 2(stderr), 3(memfd), |
| 3875 | + * 4(listener), so the newly allocated fd should be 5. |
| 3876 | + */ |
| 3877 | + EXPECT_EQ(fd, 5); |
| 3878 | + EXPECT_EQ(filecmp(getpid(), pid, memfd, fd), 0); |
| 3879 | + |
| 3880 | + /* Verify we can set an arbitrary remote fd with large size */ |
| 3881 | + memset(&big, 0x0, sizeof(big)); |
| 3882 | + big.addfd = addfd; |
| 3883 | + fd = ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD_BIG, &big); |
| 3884 | + EXPECT_EQ(fd, 6); |
| 3885 | + |
| 3886 | + /* Verify we can set a specific remote fd */ |
| 3887 | + addfd.newfd = 42; |
| 3888 | + addfd.flags = SECCOMP_ADDFD_FLAG_SETFD; |
| 3889 | + fd = ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd); |
| 3890 | + EXPECT_EQ(fd, 42); |
| 3891 | + EXPECT_EQ(filecmp(getpid(), pid, memfd, fd), 0); |
| 3892 | + |
| 3893 | + /* Resume syscall */ |
| 3894 | + resp.id = req.id; |
| 3895 | + resp.error = 0; |
| 3896 | + resp.val = USER_NOTIF_MAGIC; |
| 3897 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0); |
| 3898 | + |
| 3899 | + /* |
| 3900 | + * This sets the ID of the ADD FD to the last request plus 1. The |
| 3901 | + * notification ID increments 1 per notification. |
| 3902 | + */ |
| 3903 | + addfd.id = req.id + 1; |
| 3904 | + |
| 3905 | + /* This spins until the underlying notification is generated */ |
| 3906 | + while (ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd) != -1 && |
| 3907 | + errno != -EINPROGRESS) |
| 3908 | + nanosleep(&delay, NULL); |
| 3909 | + |
| 3910 | + memset(&req, 0, sizeof(req)); |
| 3911 | + ASSERT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0); |
| 3912 | + ASSERT_EQ(addfd.id, req.id); |
| 3913 | + |
| 3914 | + resp.id = req.id; |
| 3915 | + resp.error = 0; |
| 3916 | + resp.val = USER_NOTIF_MAGIC; |
| 3917 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0); |
| 3918 | + |
| 3919 | + /* Wait for child to finish. */ |
| 3920 | + EXPECT_EQ(waitpid(pid, &status, 0), pid); |
| 3921 | + EXPECT_EQ(true, WIFEXITED(status)); |
| 3922 | + EXPECT_EQ(0, WEXITSTATUS(status)); |
| 3923 | + |
| 3924 | + close(memfd); |
| 3925 | +} |
| 3926 | + |
| 3927 | +TEST(user_notification_addfd_rlimit) |
| 3928 | +{ |
| 3929 | + pid_t pid; |
| 3930 | + long ret; |
| 3931 | + int status, listener, memfd; |
| 3932 | + struct seccomp_notif_addfd addfd = {}; |
| 3933 | + struct seccomp_notif req = {}; |
| 3934 | + struct seccomp_notif_resp resp = {}; |
| 3935 | + const struct rlimit lim = { |
| 3936 | + .rlim_cur = 0, |
| 3937 | + .rlim_max = 0, |
| 3938 | + }; |
| 3939 | + |
| 3940 | + memfd = memfd_create("test", 0); |
| 3941 | + ASSERT_GE(memfd, 0); |
| 3942 | + |
| 3943 | + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); |
| 3944 | + ASSERT_EQ(0, ret) { |
| 3945 | + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); |
| 3946 | + } |
| 3947 | + |
| 3948 | + /* Check that the basic notification machinery works */ |
| 3949 | + listener = user_notif_syscall(__NR_getppid, |
| 3950 | + SECCOMP_FILTER_FLAG_NEW_LISTENER); |
| 3951 | + ASSERT_GE(listener, 0); |
| 3952 | + |
| 3953 | + pid = fork(); |
| 3954 | + ASSERT_GE(pid, 0); |
| 3955 | + |
| 3956 | + if (pid == 0) |
| 3957 | + exit(syscall(__NR_getppid) != USER_NOTIF_MAGIC); |
| 3958 | + |
| 3959 | + |
| 3960 | + ASSERT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0); |
| 3961 | + |
| 3962 | + ASSERT_EQ(prlimit(pid, RLIMIT_NOFILE, &lim, NULL), 0); |
| 3963 | + |
| 3964 | + addfd.srcfd = memfd; |
| 3965 | + addfd.newfd_flags = O_CLOEXEC; |
| 3966 | + addfd.newfd = 0; |
| 3967 | + addfd.id = req.id; |
| 3968 | + addfd.flags = 0; |
| 3969 | + |
| 3970 | + /* Should probably spot check /proc/sys/fs/file-nr */ |
| 3971 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd), -1); |
| 3972 | + EXPECT_EQ(errno, EMFILE); |
| 3973 | + |
| 3974 | + addfd.newfd = 100; |
| 3975 | + addfd.flags = SECCOMP_ADDFD_FLAG_SETFD; |
| 3976 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd), -1); |
| 3977 | + EXPECT_EQ(errno, EBADF); |
| 3978 | + |
| 3979 | + resp.id = req.id; |
| 3980 | + resp.error = 0; |
| 3981 | + resp.val = USER_NOTIF_MAGIC; |
| 3982 | + |
| 3983 | + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0); |
| 3984 | + |
| 3985 | + /* Wait for child to finish. */ |
| 3986 | + EXPECT_EQ(waitpid(pid, &status, 0), pid); |
| 3987 | + EXPECT_EQ(true, WIFEXITED(status)); |
| 3988 | + EXPECT_EQ(0, WEXITSTATUS(status)); |
| 3989 | + |
| 3990 | + close(memfd); |
| 3991 | +} |
| 3992 | + |
3764 | 3993 | /*
|
3765 | 3994 | * TODO:
|
3766 | 3995 | * - expand NNP testing
|
|
0 commit comments