Bug 2186233

Summary: systemd's SIGPIPE masking is not inherited by services
Product: Red Hat Enterprise Linux 9 Reporter: Milos Malik <mmalik>
Component: selinux-policyAssignee: Nobody <nobody>
Status: CLOSED ERRATA QA Contact: Milos Malik <mmalik>
Severity: medium Docs Contact:
Priority: medium    
Version: 9.2CC: apeetham, levon, lvrabec, mmalik, zpytela
Target Milestone: rcKeywords: AutoVerified, Triaged
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: selinux-policy-38.1.18-1.el9 Doc Type: No Doc Update
Doc Text:
Story Points: ---
Clone Of: 2177254 Environment:
Last Closed: 2023-11-07 08:52:21 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 2177254    
Bug Blocks:    

Description Milos Malik 2023-04-12 13:51:49 UTC
+++ This bug was initially created as a clone of Bug #2177254 +++

When in selinux enforcing mode, SIGPIPE masking is not inherited by services started by systemd without an selinux policy defined. This can cause systemd services to behave unexpectedly on receipt of SIGPIPE.

This behaviour contradicts systemd documentation and the intention of the code:

1. In https://www.freedesktop.org/software/systemd/man/systemd.exec.html the definition of IgnoreSIGPIPE is "causes SIGPIPE to be ignored in the executed process" and states that the default is true.

2. In systemd's src/core/execute.c, the function exec_child does the following

        if (context->ignore_sigpipe)
                (void) ignore_signals(SIGPIPE);

before the execve().

How reproducible: always.

Steps to Reproduce:

cat > show-signals.c <<EOF
#include <stdio.h>
#include <string.h>

int
main(void)
{
        FILE *fd = fopen("/proc/self/status", "r");
        char buf[256];

        while (fgets(buf, sizeof(buf), fd) != NULL) {
                if (strstr(buf, "SigIgn:"))
                        printf(buf);
        }

        return 0;
}
EOF

gcc -o /usr/libexec/show-signals show-signals.c

cat > /etc/systemd/system/show-signals.service <<EOF
[Service]
Type=oneshot
ExecStart=/usr/libexec/show-signals
EOF

systemctl daemon-reload
systemctl start show-signals.service
journalctl -u show-signals.service | fgrep SigIgn | tail -n 1

Actual results:

  Feb 22 10:00:38 test-host show-signals[30527]: SigIgn:        0000000000000000

Expected results:

  Feb 22 10:00:38 test-host show-signals[30527]: SigIgn:        0000000000001000

Note that bit 13 corresponds to SIGPIPE.

Additional info:

The problem is with RHEL 8's selinux-policy-targeted restricting signal inheritance when systemd spawns processes. (I was using selinux-policy-targeted-3.14.3-108.el8_7.1.noarch.)

Because show-signals does not have an associated selinux-policy it is started by systemd in the "unconfined_service_t" context. systemd runs in context init_t which is now allowed to transition to target "unconfined_service_t" with the "process" class's "siginh" permission:

  # sesearch -s init_t -t unconfined_service_t -c process -p siginh -A
  # 

On RHEL 7, that permission existed because init_t had the typeattribute unconfined_domain_type. On RHEL 8, unconfined_domain_type does have the siginh permission:

  # sesearch -s unconfined_domain_type -t unconfined_service_t -c process -p siginh -A
  allow unconfined_domain_type domain:process { fork getattr getcap getpgid getrlimit getsched getsession noatsecure rlimitinh setcap setcurrent setexec setfscreate setkeycreate setpgid setrlimit setsched setsockcreate share sigchld siginh sigkill signal signull sigstop };

init_t lost the unconfined_domain_type typeattribute as a consequence of the following patch: https://github.com/fedora-selinux/selinux-policy/commit/015047e1d962173e3789af3fad86198a3b5e3ac2

The RHEL 7 behaviour is restored if the systemd service is given an selinux-policy using the init_daemon_domain interface or equivalent.

Since this change in selinux-policy behaviour on RHEL 8 was not documented (to the best of my knowledge), my guess is that a lot of systemd services are assuming that SIGPIPE is ignored on RHEL 8, as it was on RHEL 7 and as the systemd docs state, leading to unexpected behaviour.

--- Additional comment from Milos Malik on 2023-03-20 14:36:54 UTC ---

After removing the dontaudit rules (semodule -DB) from active policy, a SELinux denial appears with each "systemctl start show-signals.service" command.

Caught in enforcing mode:
----
type=PROCTITLE msg=audit(03/20/2023 10:31:32.593:313) : proctitle=/usr/libexec/show-signals 
type=PATH msg=audit(03/20/2023 10:31:32.593:313) : item=1 name=/lib64/ld-linux-x86-64.so.2 inode=12666 dev=fd:01 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:ld_so_t:s0 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0 
type=PATH msg=audit(03/20/2023 10:31:32.593:313) : item=0 name=/usr/libexec/show-signals inode=1251250 dev=fd:01 mode=file,755 ouid=root ogid=root rdev=00:00 obj=unconfined_u:object_r:bin_t:s0 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0 
type=CWD msg=audit(03/20/2023 10:31:32.593:313) : cwd=/ 
type=EXECVE msg=audit(03/20/2023 10:31:32.593:313) : argc=1 a0=/usr/libexec/show-signals 
type=SYSCALL msg=audit(03/20/2023 10:31:32.593:313) : arch=x86_64 syscall=execve success=yes exit=0 a0=0x5627ac432240 a1=0x5627ac537e20 a2=0x5627ac60b220 a3=0x7 items=2 ppid=1 pid=5529 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset comm=show-signals exe=/usr/libexec/show-signals subj=system_u:system_r:unconfined_service_t:s0 key=(null) 
type=AVC msg=audit(03/20/2023 10:31:32.593:313) : avc:  denied  { siginh } for  pid=5529 comm=show-signals scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:unconfined_service_t:s0 tclass=process permissive=0 
----

Caught in permissive mode:
----
type=PROCTITLE msg=audit(03/20/2023 10:32:24.349:318) : proctitle=/usr/libexec/show-signals 
type=PATH msg=audit(03/20/2023 10:32:24.349:318) : item=1 name=/lib64/ld-linux-x86-64.so.2 inode=12666 dev=fd:01 mode=file,755 ouid=root ogid=root rdev=00:00 obj=system_u:object_r:ld_so_t:s0 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0 
type=PATH msg=audit(03/20/2023 10:32:24.349:318) : item=0 name=/usr/libexec/show-signals inode=1251250 dev=fd:01 mode=file,755 ouid=root ogid=root rdev=00:00 obj=unconfined_u:object_r:bin_t:s0 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0 
type=CWD msg=audit(03/20/2023 10:32:24.349:318) : cwd=/ 
type=EXECVE msg=audit(03/20/2023 10:32:24.349:318) : argc=1 a0=/usr/libexec/show-signals 
type=SYSCALL msg=audit(03/20/2023 10:32:24.349:318) : arch=x86_64 syscall=execve success=yes exit=0 a0=0x5627ac410720 a1=0x5627ac43bc50 a2=0x5627ac5871f0 a3=0x7 items=2 ppid=1 pid=5537 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset comm=show-signals exe=/usr/libexec/show-signals subj=system_u:system_r:unconfined_service_t:s0 key=(null) 
type=AVC msg=audit(03/20/2023 10:32:24.349:318) : avc:  denied  { siginh } for  pid=5537 comm=show-signals scontext=system_u:system_r:init_t:s0 tcontext=system_u:system_r:unconfined_service_t:s0 tclass=process permissive=1 
----

--- Additional comment from Milos Malik on 2023-03-20 14:46:52 UTC ---

# sestatus 
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      33
# cat testpolicy.cil 
( allow init_t unconfined_service_t ( process ( siginh )))

# semodule -i testpolicy.cil 
# semodule -lfull | grep test
400 testpolicy                   cil         
# systemctl start show-signals.service
# systemctl status show-signals.service
● show-signals.service
   Loaded: loaded (/etc/systemd/system/show-signals.service; static; vendor preset: disabled)
   Active: inactive (dead)

Mar 20 10:43:35 removed systemd[1]: Starting show-signals.service...
Mar 20 10:43:35 removed show-signals[5599]: SigIgn:        0000000000001000
Mar 20 10:43:35 removed systemd[1]: show-signals.service: Succeeded.
Mar 20 10:43:35 removed systemd[1]: Started show-signals.service.
#

I believe that the testpolicy module fixes the issue described in comment#0.

--- Additional comment from Milos Malik on 2023-03-20 14:56:44 UTC ---

The same issue is reproducible on RHEL-9.2.

--- Additional comment from Zdenek Pytela on 2023-03-24 09:44:19 UTC ---

The permissions is allowed for 2 attributes, but unconfined_service_t is not a part of any of them:

# sesearch -A -s init_t -c process -p siginh
allow init_t daemon:process siginh;
allow init_t systemprocess:process { dyntransition siginh };

Comment 5 Zdenek Pytela 2023-06-23 09:35:51 UTC
Commit to backport:
a4f0d7c3f Allow unconfined service inherit signal state from init

Comment 14 errata-xmlrpc 2023-11-07 08:52:21 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory (selinux-policy bug fix and enhancement update), and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2023:6617