lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 6 Jan 2020 21:06:47 +0000
From:   Sargun Dhillon <sargun@...gun.me>
To:     Christian Brauner <christian.brauner@...ntu.com>
Cc:     linux-kernel@...r.kernel.org,
        containers@...ts.linux-foundation.org, linux-api@...r.kernel.org,
        linux-fsdevel@...r.kernel.org, tycho@...ho.ws, jannh@...gle.com,
        cyphar@...har.com, oleg@...hat.com, luto@...capital.net,
        viro@...iv.linux.org.uk, gpascutto@...illa.com,
        ealvarez@...illa.com, fweimer@...hat.com, jld@...illa.com,
        arnd@...db.de
Subject: Re: [PATCH v8 3/3] test: Add test for pidfd getfd

On Mon, Jan 06, 2020 at 06:19:41PM +0100, Christian Brauner wrote:
> On Sun, Jan 05, 2020 at 07:08:13PM +0000, Sargun Dhillon wrote:
> > On Sun, Jan 05, 2020 at 03:20:23PM +0100, Christian Brauner wrote:
> > > On Fri, Jan 03, 2020 at 08:29:28AM -0800, Sargun Dhillon wrote:
> > > > +static int sys_pidfd_getfd(int pidfd, int fd, int flags)
> > > > +{
> > > > +	return syscall(__NR_pidfd_getfd, pidfd, fd, flags);
> > > > +}
> > > 
> > > I think you can move this to the pidfd.h header as:
> > > 
> > > static inline int sys_pidfd_getfd(int pidfd, int fd, int flags)
> > > {
> > > 	return syscall(__NR_pidfd_getfd, pidfd, fd, flags);
> > > }
> > > 
> > > Note, this also needs an
> > > 
> > > #ifndef __NR_pidfd_getfd
> > > __NR_pidfd_getfd -1
> > > #endif
> > > so that compilation doesn't fail.
> > > 
> > I'll go ahead and move this into pidfd.h, and follow the pattern there. I
> > don't think it's worth checking if each time the return code is ENOSYS.
> > 
> > Does it make sense to add something like:
> > #ifdef __NR_pidfd_getfd
> > TEST_HARNESS_MAIN
> > #else
> > int main(void)
> > {
> > 	fprintf(stderr, "pidfd_getfd syscall not supported\n");
> > 	return KSFT_SKIP;
> > }
> > #endif
> > 
> > to short-circuit the entire test suite?
> 
> You mean the getfd testsuite? If so and that works, then sounds like a
> good idea to me.
> 
> > 
> > 
> > 
> > > 
> > > Hm, isn't it safer to do 65535 explicitly? Since USHRT_MAX can
> > > technically be greater than 65535.
> > > 
> > I borrowed this from the BPF tests. I can hardcode something like:
> > #define NOBODY_UID 65535
> > and setuid to that, if you think it's safer?
> 
> If you want to specifically seteuid() to 65535 then yes, using the
> hard-coded number or using a dedicated macro seems better.
> 
> > 
> > > > +
> > > > +	ASSERT_EQ(1, send(self->sk, "P", 1, 0));
> > > > +	ASSERT_EQ(1, recv(self->sk, &c, 1, 0));
> > > > +
> > > > +	fd = sys_pidfd_getfd(self->pidfd, self->remote_fd, 0);
> > > > +	EXPECT_EQ(-1, fd);
> > > > +	EXPECT_EQ(EPERM, errno);
> > > > +
> > > > +	if (uid == 0)
> > > > +		ASSERT_EQ(0, seteuid(0));
> > > > +}
> > > > +
> > > > +TEST_F(child, fetch_fd)
> > > > +{
> > > > +	int fd, ret;
> > > > +
> > > > +	fd = sys_pidfd_getfd(self->pidfd, self->remote_fd, 0);
> > > > +	ASSERT_GE(fd, 0);
> > > > +
> > > > +	EXPECT_EQ(0, sys_kcmp(getpid(), self->pid, KCMP_FILE, fd, self->remote_fd));
> > > 
> > > So most of these tests seem to take place when the child has already
> > > called exit() - or at least it's very likely that the child has already
> > > called exit() - and remains a zombie. That's not ideal because
> > > that's not the common scenario/use-case. Usually the task of which we
> > > want to get an fd will be alive. Also, if the child has already called
> > > exit(), by the time it returns to userspace it should have already
> > > called exit_files() and so I wonder whether this test would fail if it's
> > > run after the child has exited. Maybe I'm missing something here... Is
> > > there some ordering enforced by TEST_F()?
> > Yeah, I think perhaps I was being too clever.
> > The timeline roughly goes something like this:
> > 
> > # Fixture bringup
> > [parent] creates socket_pair
> > [parent] forks, and passes pair down to child
> > [parent] waits to read sizeof(int) from the sk_pair
> > [child] creates memfd 
> > [__child] sends local memfd number to parent via sk_pair
> > [__child] waits to read from sk_pair
> > [parent] reads remote memfd number from socket
> > # Test
> > [parent] performs tests
> > # Fixture teardown
> > [parent] closes sk_pair
> > [__child] reads 0 from recv on sk_pair, implies the other end is closed
> > [__child] Returns / exits 0
> > [parent] Reaps child / reads exit code
> > 
> > ---
> > The one case where this is not true, is if the parent sends 'P' to the sk pair,
> > it triggers setting PR_SET_DUMPABLE to 0, and then resumes waiting for the fd to 
> > close.
> > 
> > Maybe I'm being too clever? Instead, the alternative was to send explicit stop / 
> > start messages across the sk_pair, but that got kind of ugly. Do you have a 
> > better suggestion?
> 
> If I understand correctly you just need to block the child to stop it
> from exiting. Couldn't you do this by simply calling recv() on the
> socket in the child thereby blocking it? At the end you just send a
> final message to proceed and if that doesn't work SIGKILL it?
> 
This already exists in:
while ((ret = recv(sk, &buf, sizeof(buf), 0)) > 0) {
	if (buf == 'P') {
		ret = prctl(PR_SET_DUMPABLE, 0);
		if (ret < 0) {
			fprintf(stderr,
				"%s: Child failed to disable ptrace\n",
				strerror(errno));
			return -1;
		}
	} else {
		fprintf(stderr, "Child received unknown command %c\n",
			buf);
		return -1;
	}
	ret = send(sk, &buf, sizeof(buf), 0);
	if (ret != 1) {
		fprintf(stderr, "%s: Child failed to ack\n",
			strerror(errno));
		return -1;
	}
}
----
This will block until the close(self->sk) in the fixture teardown. Then ret
returns 0, and the child should exit. Maybe a comment like:
/*
 * The fixture setup is completed at this point. The tests will run.
 *
 * Either we will read 'P' off of the sk, indicating that we need
 * to disable ptrace, or if the other side of the socket is closed
 * recv will return 0-bytes. This indicates that the fixture teardown
 * has occured, and the child should exit.
 */
would be useful?

> > 
> > > 
> > > Also, what does self->pid point to? The fd of the already exited child?
> > It's just the pid of the child. pidfd is the fd of the (unexited) child.
I have no idea if it's pro / against the commenting style to blow up that
structure:
FIXTURE(child)
{
	/* pid points to the child which we are fetching FDs from */
	pid_t pid;
	/* pidfd is the pidfd of the child */
	int pidfd;
	/*
	 * sk is our side of the socketpair used to communicate with the child.
	 * When it is closed, the child will exit.
	 */
	int sk;
	/*
	 * remote_fd is the number of the FD which we are trying to retrieve
	 * from the child.
	 */
	int remote_fd;
};

> 
> Ah, thanks!
> Christian

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ