int main()

in seccomp/user-trap.c [201:375]


int main(void)
{
	int sk_pair[2], ret = 1, status, listener;
	pid_t worker = 0 , tracer = 0;

	if (socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sk_pair) < 0) {
		perror("socketpair");
		return 1;
	}

	worker = fork();
	if (worker < 0) {
		perror("fork");
		goto close_pair;
	}

	if (worker == 0) {
		listener = user_trap_syscall(__NR_mount,
					     SECCOMP_FILTER_FLAG_NEW_LISTENER);
		if (listener < 0) {
			perror("seccomp");
			exit(1);
		}

		/*
		 * Drop privileges. We definitely can't mount as uid 1000.
		 */
		if (setuid(1000) < 0) {
			perror("setuid");
			exit(1);
		}

		/*
		 * Send the listener to the parent; also serves as
		 * synchronization.
		 */
		if (send_fd(sk_pair[1], listener) < 0)
			exit(1);
		close(listener);

		if (mkdir("/tmp/foo", 0755) < 0) {
			perror("mkdir");
			exit(1);
		}

		/*
		 * Try a bad mount just for grins.
		 */
		if (mount("/dev/sda", "/tmp/foo", NULL, 0, NULL) != -1) {
			fprintf(stderr, "huh? mounted /dev/sda?\n");
			exit(1);
		}

		if (errno != EPERM) {
			perror("bad error from mount");
			exit(1);
		}

		/*
		 * Ok, we expect this one to succeed.
		 */
		if (mount("/tmp/foo", "/tmp/foo", NULL, MS_BIND, NULL) < 0) {
			perror("mount");
			exit(1);
		}

		exit(0);
	}

	/*
	 * Get the listener from the child.
	 */
	listener = recv_fd(sk_pair[0]);
	if (listener < 0)
		goto out_kill;

	/*
	 * Fork a task to handle the requests. This isn't strictly necessary,
	 * but it makes the particular writing of this sample easier, since we
	 * can just wait ofr the tracee to exit and kill the tracer.
	 */
	tracer = fork();
	if (tracer < 0) {
		perror("fork");
		goto out_kill;
	}

	if (tracer == 0) {
		struct seccomp_notif *req;
		struct seccomp_notif_resp *resp;
		struct seccomp_notif_sizes sizes;

		if (seccomp(SECCOMP_GET_NOTIF_SIZES, 0, &sizes) < 0) {
			perror("seccomp(GET_NOTIF_SIZES)");
			goto out_close;
		}

		req = malloc(sizes.seccomp_notif);
		if (!req)
			goto out_close;

		resp = malloc(sizes.seccomp_notif_resp);
		if (!resp)
			goto out_req;
		memset(resp, 0, sizes.seccomp_notif_resp);

		while (1) {
			memset(req, 0, sizes.seccomp_notif);
			if (ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, req)) {
				perror("ioctl recv");
				goto out_resp;
			}

			if (handle_req(req, resp, listener) < 0)
				goto out_resp;

			/*
			 * ENOENT here means that the task may have gotten a
			 * signal and restarted the syscall. It's up to the
			 * handler to decide what to do in this case, but for
			 * the sample code, we just ignore it. Probably
			 * something better should happen, like undoing the
			 * mount, or keeping track of the args to make sure we
			 * don't do it again.
			 */
			if (ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, resp) < 0 &&
			    errno != ENOENT) {
				perror("ioctl send");
				goto out_resp;
			}
		}
out_resp:
		free(resp);
out_req:
		free(req);
out_close:
		close(listener);
		exit(1);
	}

	close(listener);

	if (waitpid(worker, &status, 0) != worker) {
		perror("waitpid");
		goto out_kill;
	}

	if (umount2("/tmp/foo", MNT_DETACH) < 0 && errno != EINVAL) {
		perror("umount2");
		goto out_kill;
	}

	if (remove("/tmp/foo") < 0 && errno != ENOENT) {
		perror("remove");
		exit(1);
	}

	if (!WIFEXITED(status) || WEXITSTATUS(status)) {
		fprintf(stderr, "worker exited nonzero\n");
		goto out_kill;
	}

	ret = 0;

out_kill:
	if (tracer > 0)
		kill(tracer, SIGKILL);
	if (worker > 0)
		kill(worker, SIGKILL);

close_pair:
	close(sk_pair[0]);
	close(sk_pair[1]);
	return ret;
}