11#include <sys/syscall.h>
17 fprintf(stderr, "SESSION-CONTRACT: FAIL line=%d errno=%d\n", __LINE__, errno); \
22static int finish(pid_t child) {
24 return waitpid(child, &status, 0) == child && WIFEXITED(status) && WEXITSTATUS(status) == 0;
28 pid_t sid = getsid(0), pgid = getpgrp();
29 CHECK(sid > 0 && pgid > 0 && getsid(getpid()) == sid && getpgid(getpid()) == pgid);
30 int ready[2], release[2];
31 CHECK(pipe(ready) == 0 && pipe(release) == 0);
38 if (getsid(0) != sid || getpgrp() != pgid || getpgid(getppid()) != pgid ||
39 write(ready[1], &
byte, 1) != 1 || read(release[0], &
byte, 1) != 1)
46 CHECK(read(ready[0], &
byte, 1) == 1);
47 CHECK(getsid(child) == sid && getpgid(child) == pgid);
48 CHECK(setpgid(child, child) == 0 && getpgid(child) == child && getsid(child) == sid);
49 CHECK(write(release[1], &
byte, 1) == 1 && finish(child));
55 CHECK(getsid(-1) == -1 && errno == ESRCH);
57 CHECK(getsid(child) == -1 && errno == ESRCH);
58 puts(
"SESSION-CONTRACT: PASS inheritance");
62static int independent_session(
void) {
63 pid_t oldGroup = getpgrp();
68 if (setsid() != me || getsid(0) != me || getpgrp() != me)
71 if (setsid() != -1 || errno != EPERM)
74 if (setpgid(0, 0) != -1 || errno != EPERM)
76 pid_t member = fork();
80 if (getsid(0) != me || getpgrp() != me)
83 if (setpgid(0, oldGroup) != -1 || errno != EPERM)
85 if (setpgid(0, 0) || getpgrp() != getpid() || getsid(0) != me)
89 _exit(finish(member) ? 0 : 8);
92 puts(
"SESSION-CONTRACT: PASS independent-session");
96static int leader_exit(
void) {
97 int ready[2], release[2];
98 CHECK(pipe(ready) == 0 && pipe(release) == 0);
99 pid_t leader = fork();
104 if (setsid() != getpid())
106 pid_t member = fork();
112 if (write(ready[1], &me,
sizeof(me)) !=
sizeof(me) || read(release[0], &
byte, 1) != 1)
121 CHECK(read(ready[0], &member,
sizeof(member)) ==
sizeof(member));
122 CHECK(finish(leader));
123 CHECK(getsid(member) == leader && getpgid(member) == leader);
125 CHECK(write(release[1], &
byte, 1) == 1);
130 puts(
"SESSION-CONTRACT: PASS leader-exit");
134static int exec_boundary(
const char* program) {
135 int ready[2], release[2];
136 CHECK(pipe(ready) == 0 && pipe(release) == 0);
137 pid_t child = fork();
142 char send[24], receive[24];
143 snprintf(send,
sizeof(send),
"%d", ready[1]);
144 snprintf(receive,
sizeof(receive),
"%d", release[0]);
145 execl(program, program,
"--exec-child", send, receive, (
char*)0);
151 CHECK(read(ready[0], &
byte, 1) == 1);
153 CHECK(setpgid(child, child) == -1 && errno == EACCES);
154 CHECK(write(release[1], &
byte, 1) == 1 && finish(child));
159 puts(
"SESSION-CONTRACT: PASS exec-boundary");
163static int direct_child_only(
void) {
164 int ready[2], release[2];
165 CHECK(pipe(ready) == 0 && pipe(release) == 0);
166 pid_t child = fork();
171 pid_t grandchild = fork();
177 if (write(ready[1], &me,
sizeof(me)) !=
sizeof(me) || read(release[0], &
byte, 1) != 1)
181 _exit(finish(grandchild) ? 0 : 3);
186 CHECK(read(ready[0], &grandchild,
sizeof(grandchild)) ==
sizeof(grandchild));
188 CHECK(setpgid(grandchild, grandchild) == -1 && errno == ESRCH);
190 CHECK(write(release[1], &
byte, 1) == 1 && finish(child));
195 puts(
"SESSION-CONTRACT: PASS direct-child-only");
200static void* thread_identity(
void* unused) {
202 const pid_t tid = syscall(SYS_gettid);
203 if (tid == getpid() || getsid(tid) != getsid(0) || getpgid(tid) != getpgrp())
206 return setpgid(tid, 0) == -1 && errno == EINVAL ? NULL : (
void*)2;
210static int task_identity(
void) {
212 puts(
"SESSION-CONTRACT: SKIP Linux task IDs on native host");
216 CHECK(pthread_create(&thread, NULL, thread_identity, NULL) == 0);
217 CHECK(pthread_join(thread, &result) == 0 && result == NULL);
218 puts(
"SESSION-CONTRACT: PASS task-identity");
223int main(
int argc,
char** argv) {
224 setvbuf(stdout, NULL, _IONBF, 0);
226 if (argc == 4 && !strcmp(argv[1],
"--exec-child")) {
228 return write(atoi(argv[2]), &
byte, 1) != 1 || read(atoi(argv[3]), &
byte, 1) != 1;
230 const int portable = argc == 2 && !strcmp(argv[1],
"--portable-host");
232 puts(
"SESSION-CONTRACT: SKIP Linux direct-child-only rule on native host");
233 if (
inheritance() || independent_session() || leader_exit() || exec_boundary(argv[0]) ||
234 (!portable && direct_child_only()) || task_identity())
236 puts(
"SESSION-CONTRACT: END PASS");