[PATCH wayland v5] tests: Fix FAIL in sanity-test (*timeout*) when Yama LSM enabled

Derek Foreman derekf at osg.samsung.com
Fri Feb 6 08:15:57 PST 2015


On 05/02/15 07:39 PM, Bryce Harrington wrote:
> This fixes a regression in the testsuite since c3653f7f, where four of
> the timeout tests fail with "Timeouts suppressed" messages.
> 
> The timeouts are being suppressed because the testsuite is erroneously
> detecting that a debugger is attached.  This detection mechanism
> (adopted from libinput) uses ptrace to test if there is a debugger
> parent process that can be attached.  Unfortunately, this is an
> unreliable test: Kernel security policies exist to restrict the scope of
> ptrace to prevent processes from snooping on one another.[1] This
> security policy is set as the default on Ubuntu, and potentially other
> Linux distributions.[2]
> 
> The Yama documentation suggests, "For software that has defined
> application-specific relationships between a debugging process and its
> inferior (crash handlers, etc), prctl(PR_SET_PTRACER, pid, ...) can be
> used.  An inferior can declare which other process (and its descendents)
> are allowed to call PTRACE_ATTACH against it."  This prctl call has no
> effect if Yama LSM is not loaded.
> 
> The child needs to be synchronized to the client to prevent a race
> condition where the child might try to operate before the parent has
> finished its prctl call.  This synchronization is done via pipes.
> 
> This patch can be tested by running sanity-test with
> /proc/sys/kernel/yama/ptrace_scope set to 0 or 1; the test must pass for
> either value.
> 
> 1: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2d514487faf188938a4ee4fb3464eeecfbdcf8eb
> 2: https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace_Protection
> 
> Signed-off-by: Bryce Harrington <bryce at osg.samsung.com>
> Reviewed-by: Marek Chalupa <mchqwerty at gmail.com>
> 
> v4: Allow parent to communicate error state to child to prevent leaving
> child in zombie state if parent hits an error.
> 
> v5: Check errno instead of rc for error.  Don't waitpid on ppid.
> (I guess these two coding errors canceled out for me or something.)

FWIW, the waitpid thing was never a bug, I errantly called it one in an
off list conversation.  It'll work fine the way it is now, or the way it
was.

Either way you choose:
Reviewed-by: Derek Foreman <derekf at osg.samsung.com>


> Signed-off-by: Bryce Harrington <bryce at osg.samsung.com>
> ---
>  tests/test-runner.c | 62 +++++++++++++++++++++++++++++++++++++++++++----------
>  1 file changed, 51 insertions(+), 11 deletions(-)
> 
> diff --git a/tests/test-runner.c b/tests/test-runner.c
> index 48e9a22..f031a42 100644
> --- a/tests/test-runner.c
> +++ b/tests/test-runner.c
> @@ -34,6 +34,10 @@
>  #include <errno.h>
>  #include <limits.h>
>  #include <sys/ptrace.h>
> +#include <sys/prctl.h>
> +#ifndef PR_SET_PTRACER
> +# define PR_SET_PTRACER 0x59616d61
> +#endif
>  
>  #include "test-runner.h"
>  
> @@ -249,29 +253,65 @@ stderr_reset_color(void)
>  
>  /* this function is taken from libinput/test/litest.c
>   * (rev 028513a0a723e97941c39)
> + *
> + * Returns: 1 if a debugger is confirmed present; 0 if no debugger is
> + * present or if it can't be determined.
>   */
>  static int
>  is_debugger_attached(void)
>  {
>  	int status;
>  	int rc;
> -	pid_t pid = fork();
> +	pid_t pid;
> +	int pipefd[2];
>  
> -	if (pid == -1)
> +	if (pipe(pipefd) == -1) {
> +		perror("pipe");
>  		return 0;
> +	}
>  
> -	if (pid == 0) {
> +	pid = fork();
> +	if (pid == -1) {
> +		perror("fork");
> +		close(pipefd[0]);
> +		close(pipefd[1]);
> +		return 0;
> +	} else if (pid == 0) {
> +		char buf;
>  		pid_t ppid = getppid();
> -		if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0) {
> -			waitpid(ppid, NULL, 0);
> -			ptrace(PTRACE_CONT, NULL, NULL);
> -			ptrace(PTRACE_DETACH, ppid, NULL, NULL);
> -			rc = 0;
> +
> +		/* Wait until parent is ready */
> +		close(pipefd[1]);  /* Close unused write end */
> +		read(pipefd[0], &buf, 1);
> +		close(pipefd[0]);
> +		if (buf == '-')
> +			_exit(1);
> +		if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) != 0)
> +			_exit(1);
> +		if (!waitpid(-1, NULL, 0))
> +			_exit(1);
> +		ptrace(PTRACE_CONT, NULL, NULL);
> +		ptrace(PTRACE_DETACH, ppid, NULL, NULL);
> +		_exit(0);
> +	} else {
> +		close(pipefd[0]);
> +
> +		/* Enable child to ptrace the parent process */
> +		rc = prctl(PR_SET_PTRACER, pid);
> +		if (rc != 0 && errno != EINVAL) {
> +			/* An error prevents us from telling if a debugger is attached.
> +			 * Instead of propagating the error, assume no debugger present.
> +			 * But note the error to the log as a clue for troubleshooting.
> +			 * Then flag the error state to the client by sending '-'.
> +			 */
> +			perror("prctl");
> +			write(pipefd[1], "-", 1);
>  		} else {
> -			rc = 1;
> +			/* Signal to client that parent is ready by passing '+' */
> +			write(pipefd[1], "+", 1);
>  		}
> -		_exit(rc);
> -	} else {
> +		close(pipefd[1]);
> +
>  		waitpid(pid, &status, 0);
>  		rc = WEXITSTATUS(status);
>  	}
> 



More information about the wayland-devel mailing list