Bug 225360 - Sudo can hang with SELinux because sesh lacks signal handling
Sudo can hang with SELinux because sesh lacks signal handling
Status: CLOSED WONTFIX
Product: Red Hat Enterprise Linux 4
Classification: Red Hat
Component: sudo (Show other bugs)
4.0
i386 Linux
medium Severity medium
: ---
: ---
Assigned To: Daniel Kopeček
:
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2007-01-29 23:39 EST by Richard Basch
Modified: 2012-06-20 09:23 EDT (History)
2 users (show)

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2012-06-20 09:23:07 EDT
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)
another patch proposed by sgrubb@redhat.com (1.77 KB, patch)
2007-03-07 12:02 EST, Peter Vrabec
no flags Details | Diff

  None (edit)
Description Richard Basch 2007-01-29 23:39:50 EST
From Bugzilla Helper:
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)

Description of problem:
When SELinux is enabled, sudo invokes "sesh".  However, because "sesh" lacks some signal handling, it can hang.
If SELinux is enabled (even in permissive mode), the following will cause sudo to apparently hang:
$ sudo bash
# suspend
This hang occurs because bash will send a SIGTSTP to itself, but sesh (which is invoked by sudo when SELinux is enabled) only looks for termination signals, not all signals of its children.

The following patch seems to remediate the problem:
# cat /usr/src/redhat/SOURCES/sudo-1.6.7p5-sesh-stopsig.patch
--- sudo-1.6.7p5/sesh.c.sigstop 2005-09-19 17:38:15.000000000 +0200
+++ sudo-1.6.7p5/sesh.c 2005-09-19 17:41:48.000000000 +0200
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <limits.h>
 #include <sys/types.h>
@@ -22,19 +23,33 @@
     int status;
     int ret;

-    do {
-      if ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+    while (1) {
+      if ((ret = waitpid(pid, &status, WUNTRACED)) < 0 && errno == EINTR)
         continue;
       else if (ret < 0) {
         perror("waitpid failed");
         exit(1);
       }
-    } while (0);
+
+      if (!WIFSTOPPED(status))
+       break;
+
+      /* Reset the handler in case it was inherited ignored,
+        but the child reset it and stopped anyway.  */
+      signal(WSTOPSIG(status), SIG_DFL);
+      raise(WSTOPSIG(status));
+
+      /* Now we stop until continued ourselves.  */
+      kill(getpgid(pid) == pid ? -pid : pid, SIGCONT);
+    }

     if (WIFEXITED(status))
       exit(WEXITSTATUS(status));
+    else if (WIFSIGNALED(status))
+      /* XXX print here like the shell would? */
+      exit(128 + WTERMSIG(status));
     else
-      exit(1);
+      exit(127);               /* Should never happen.  */
   } else {
     /* Child */
     execv(argv[1], &argv[1]);


Version-Release number of selected component (if applicable):
sudo-1.6.7p5-30.1.3

How reproducible:
Always


Steps to Reproduce:
1. Ensure SELinux is enabled (set SELINUX=permissive in /etc/sysconfig/selinux and reboot).
2. sudo bash
3. suspend

Actual Results:
The bash shell is suspended, but the sesh process that sudo invoked is still running, causing an apparent hang.  No tty characters (ctrl-\, ctrl-c, etc) will restore functionality.  If you have another root shell on the system, you can kill the appropriate processes.

Expected Results:
sesh should have caught the SIGTSTP the child bash shell sent to itself and suspended itself.  When resumed, sesh should send a SIGCONT to the child.

If SELinux is not enabled, there is no issue because "sesh" is not invoked, and sudo merely executes the command line explicitly.  However, sudo's design to use a common process that fields subcommands when used in a SELinux environment means that "middle" process should watch for signals of its children and act upon them accordingly rather than ignoring them.  I presume the reason for "sesh" is to provide a common executable that can be authorized for context authorization changes within SELinux, rather than having to register every sudo command that might be executed as being authorized to allow its uid context to be switched from its parent.

Additional info:
See patch...

# cat /usr/src/redhat/SOURCES/sudo-1.6.7p5-sesh-stopsig.patch
--- sudo-1.6.7p5/sesh.c.sigstop 2005-09-19 17:38:15.000000000 +0200
+++ sudo-1.6.7p5/sesh.c 2005-09-19 17:41:48.000000000 +0200
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <limits.h>
 #include <sys/types.h>
@@ -22,19 +23,33 @@
     int status;
     int ret;

-    do {
-      if ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+    while (1) {
+      if ((ret = waitpid(pid, &status, WUNTRACED)) < 0 && errno == EINTR)
         continue;
       else if (ret < 0) {
         perror("waitpid failed");
         exit(1);
       }
-    } while (0);
+
+      if (!WIFSTOPPED(status))
+       break;
+
+      /* Reset the handler in case it was inherited ignored,
+        but the child reset it and stopped anyway.  */
+      signal(WSTOPSIG(status), SIG_DFL);
+      raise(WSTOPSIG(status));
+
+      /* Now we stop until continued ourselves.  */
+      kill(getpgid(pid) == pid ? -pid : pid, SIGCONT);
+    }

     if (WIFEXITED(status))
       exit(WEXITSTATUS(status));
+    else if (WIFSIGNALED(status))
+      /* XXX print here like the shell would? */
+      exit(128 + WTERMSIG(status));
     else
-      exit(1);
+      exit(127);               /* Should never happen.  */
   } else {
     /* Child */
     execv(argv[1], &argv[1]);
Comment 2 Peter Vrabec 2007-03-07 12:02:19 EST
Created attachment 149466 [details]
another patch proposed by sgrubb@redhat.com
Comment 3 Richard Basch 2007-03-07 13:22:27 EST
The only issue I see with the proposed patch is the need to pass on signals to 
the child... I haven't actually tried to apply the patch, but I am not sure the 
child will resume.

Scenario:
$ sudo bash
# suspend
$ fg

While I agree the new patch should handle the suspend properly, will it handle 
the re-foregrounding properly?  Not only does sesh have to catch the SIGTSTP or 
SIGSTOP that was fielded by the child, it also has to resume the child with a 
SIGCONT after it is resumed.

Therefore, the logic for sesh should be:
1. check child status (stopped or exited) -- following steps if child is stopped
2. raise stop signal to myself (sesh)
3. send CONT signal to child (will only be reached if I am continued)

Comment 4 Peter Vrabec 2007-03-07 13:49:31 EST
The scenario above was tested and it worked as expected.
Comment 6 RHEL Product and Program Management 2008-02-01 14:10:09 EST
This request was evaluated by Red Hat Product Management for
inclusion, but this component is not scheduled to be updated in
the current Red Hat Enterprise Linux release. If you would like
this request to be reviewed for the next minor release, ask your
support representative to set the next rhel-x.y flag to "?".
Comment 7 James E. Leinweber 2008-09-26 21:35:05 EDT
This is slightly annoying and we'd like it to be fixed.  It hits us on redhat es4.7 servers with SeLinux either enforcing or permissive.  Sending both the sesh and the child bash process SIGTSTP to suspend and SIGCONT to resume is a kludgy workaround.
Comment 8 Jiri Pallich 2012-06-20 09:23:07 EDT
Thank you for submitting this issue for consideration in Red Hat Enterprise Linux. The release for which you requested us to review is now End of Life. 
Please See https://access.redhat.com/support/policy/updates/errata/

If you would like Red Hat to re-consider your feature request for an active release, please re-open the request via appropriate support channels and provide additional supporting details about the importance of this issue.

Note You need to log in before you can comment on or make changes to this bug.