Note: This bug is displayed in read-only format because the product is no longer active in Red Hat Bugzilla.
RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.

Bug 1660142

Summary: SELinux policy contains a lot of type_transition rules, leading to high memory usage by kernel
Product: Red Hat Enterprise Linux 8 Reporter: Marko Myllynen <myllynen>
Component: selinux-policyAssignee: Zdenek Pytela <zpytela>
Status: CLOSED WONTFIX QA Contact: BaseOS QE Security Team <qe-baseos-security>
Severity: medium Docs Contact:
Priority: medium    
Version: 8.0CC: dwalsh, eparis, fweimer, lvrabec, mmalik, omosnace, plautrba, sdsmall, ssekidde, zpytela
Target Milestone: rcKeywords: Triaged
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2020-03-05 09:36:59 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: 1682526    
Bug Blocks:    

Description Marko Myllynen 2018-12-17 15:46:41 UTC
Description of problem:
According to https://github.com/myllynen/misc/ on minimal RHEL 7 installation disabling SELinux saves 3MB memory but on RHEL 8 the difference is 30MB(!).

It should be investigated whether SELinux memory usage on RHEL 8 could be reduced (provided that the above numbers are correct).

Version-Release number of selected component (if applicable):
RHEL 8.0 Beta

Comment 3 Ondrej Mosnacek 2018-12-18 11:45:51 UTC
Note that the size of (binary) policy grew quite a bit between RHEL-7 and RHEL-8:

RHEL-7 (x86_64):
# wc -c /sys/fs/selinux/policy
3851447 /sys/fs/selinux/policy

RHEL-8 (x86_64):
# wc -c /sys/fs/selinux/policy
8349112 /sys/fs/selinux/policy

When converted into the kernel's internal structures it likely takes up even more memory than that so that might explain why the difference is bigger than expected.

There might be also other factors influencing memory usage (e.g. AVC cache size), but this looks like the most likely suspect so far.

Comparing the output of seinfo on 7 and 8, the 'Type_trans' number particularly stands out (all the other ones are similar across both policies):

RHEL-7:
Type_trans: 18061

RHEL-8:
Type_trans: 234632

Lukas, any idea why the policy is so much larger on RHEL-8 compared to 7?

Comment 4 Ondrej Mosnacek 2018-12-18 14:07:44 UTC
So, I dumped all type transition rules on Fedora 29 (which has very similar seinfo numbers as RHEL-8) by running 'sesearch -T >typetrans.txt' and I got an ~18MB text file with 242229 lines (seinfo reports 'Type_trans: 242229' on F29). Turns out more than half of these rules are of the form:

type_transition <something>_t device_t:(chr|blk)_file <something>_device_t "<something>";

What I find strange is that a lot of these rules appear to be rather arbitrary, e.g.:
[...]
type_transition cloud_init_t device_t:chr_file v4l_device_t "em83006";
type_transition cloud_init_t device_t:chr_file sound_device_t "mixer9";
type_transition cloud_init_t device_t:blk_file fixed_disk_device_t "ram4";
type_transition cloud_init_t device_t:blk_file fixed_disk_device_t "sdd9";
[...]
type_transition devicekit_disk_t device_t:chr_file modem_device_t "noz6";
type_transition devicekit_disk_t device_t:chr_file v4l_device_t "radio0";
type_transition devicekit_disk_t device_t:chr_file tape_device_t "st00";
[...]

What the hell?!

The situation seems to be similar on RHEL-8:
# wc -l typetrans.txt 
234632 typetrans.txt
# grep -c 'device_t:' typetrans.txt 
147307

On RHEL-7 there are no such rules at all:
# wc -l typetrans.txt
66471 typetrans.txt
# grep -c 'device_t:' typetrans.txt 
0

Comment 5 Ondrej Mosnacek 2018-12-18 15:34:13 UTC
I just realized I was working over an unsorted file when I thought it had been already sorted... The rules are not arbitrary, there is just a *lot* of domains that are part of 'named_filetrans_domain' and this generates a type_transition rule for each device (of which there are apparently about 1700...) for each of these domains.

Still, this is a terrible situation and I wonder if we can do without these rules since they are apparently not enabled/needed on RHEL-7? Or at least reduce the number of domains that belong to 'named_filetrans_domain'? (Ideally we'd have some kind of domain groups support in the kernel, but that would be a really big RFE...)

Moving to selinux-policy, since I am pretty confident now that the root of the problem lies there.

Comment 6 Stephen Smalley 2018-12-31 15:45:01 UTC
devtmpfs created problems for SELinux; previously, there was just a regular tmpfs /dev and udev handled node creation and labeling, and was modified for SELinux to set the labels correctly when the nodes were first created based on file_contexts, which maps pathname regexes and file types to security contexts.  With devtmpfs, the kernel creates the device nodes initially and lacked sufficient information to label them correctly, leaving them temporarily in some default label (e.g. device_t) until udev gets around to labeling them correctly, and this was breaking things.  Red Hat added a named type transition rules to cover them all for all unconfined domains IIRC, but named type transition rules weren't really designed for this purpose - they were for distinguishing e.g. /etc/shadow vs /etc/passwd default labels and other corner cases where we needed to take the last component name into account since the creating process and the parent directory were the same for multiple files that needed different contexts.  In Android, we avoid the problem just by disabling devtmpfs entirely; I don't really understand the benefit when using udev (ueventd in Android) anyway.

Anyway, options would include:
- Being far more selective about what domains we define these named type transitions for,
- Introducing some degree of wildcarding or regex support to the named type transition rules (requires changes to libsepol and the kernel) rather than only doing exact matches,
- Disabling devtmpfs.

Maybe others I haven't thought of.

Comment 7 Stephen Smalley 2018-12-31 16:14:37 UTC
Not sure though why this differs for RHEL-7 since it also used devtmpfs?  Actually, I do see type_transition rules on device_t on RHEL-7 too, although not so many.  Maybe your grep isn't quite general enough to cover differences in output format for sesearch between setools3 (RHEL-7) and setools4 (RHEL-8)?

Comment 8 Ondrej Mosnacek 2019-01-02 08:54:19 UTC
(In reply to Stephen Smalley from comment #7)
> Not sure though why this differs for RHEL-7 since it also used devtmpfs? 
> Actually, I do see type_transition rules on device_t on RHEL-7 too, although
> not so many.  Maybe your grep isn't quite general enough to cover
> differences in output format for sesearch between setools3 (RHEL-7) and
> setools4 (RHEL-8)?

You're right, I was grepping for the wrong string on RHEL-7. The correct RHEL-7 count is:

# sesearch -T | egrep -c "device_t : (blk|chr)_file"
17402

Comment 9 Daniel Walsh 2019-01-02 12:13:16 UTC
Perhaps if we changed the transition rule policy to be for kernel_t, we could get the same results with a lot less rules?

Comment 12 Stephen Smalley 2019-01-03 14:02:08 UTC
(In reply to Daniel Walsh from comment #9)
> Perhaps if we changed the transition rule policy to be for kernel_t, we
> could get the same results with a lot less rules?

Yes, why do we need the rules for anything except for kernel_t in the first place?  The files are created from the kdevtmpfs kernel thread running in kernel_t, and before that was introduced, they were switching creds to the init cred so it was also kernel_t.  I don't understand why the rules were added for other domains.

Comment 13 Daniel Walsh 2019-01-03 14:26:22 UTC
Well they do make some sense for unconfined_t as well, but I would be willing to sacrifice this for the memory savings.

mknod /dev/foobar

Ending up with foobar_device_t is a nice feature.

Comment 14 Daniel Walsh 2019-01-03 14:27:20 UTC
Lukas lets get this change into Rawhide, and see how much it saves and if it causes any issues.

Comment 15 Stephen Smalley 2019-01-03 14:38:15 UTC
(In reply to Daniel Walsh from comment #13)
> Well they do make some sense for unconfined_t as well, but I would be
> willing to sacrifice this for the memory savings.
> 
> mknod /dev/foobar
> 
> Ending up with foobar_device_t is a nice feature.

mknod could/should be using selabel_lookup() or matchpathcon() or similar to label new device nodes from userspace, no need to pollute the kernel policy for that.

Comment 16 Stephen Smalley 2019-01-03 14:44:34 UTC
On the kernel side, extending the name-based type transitions to support some form of wildcarding or regex matching would obviously reduce the number of rules required per domain, e.g. instead of bcache0, bcache1, ..., we could have one rule for bcache[0-9] or bcache*.  Even just simple prefix match i.e. terminal wildcard would improve things.

Comment 17 Daniel Walsh 2019-01-03 22:14:20 UTC
man mknod
...
       -Z     set the SELinux security context to default type


But the user still needs to remember to execute the -Z.  Sadly touch to change the default.

We have similar features in mv, cp, install, but none are default on SELinux systems.  And I thought
from a security point of view for MLS systems that would be bad.

Comment 18 Daniel Walsh 2019-01-03 22:14:58 UTC
The kernel stuff we talked about years ago, but it was never implemented.

Comment 20 Ondrej Mosnacek 2019-05-06 11:14:54 UTC
I did some more digging in the kernel and userspace code and after some experiments I arrived at the following:

1. The kernel already *does* have support for type attributes (contrary to what I assumed initially), *but* they are only recognized for allow rules. The userspace has no other choice than fully expand type attributes in all other rules when generating the binary policy.
2. Adding kernel-side support for type attributes in type transition rules is relatively easy and I already have a preliminary patch for this. Making userspace not expand all type transition rules is going to be more tricky, but definitely doable.
3. After applying a proof-of-concept patch to libsepol that does not expand type attributes in named file transition rules, the size of Fedora Rawhide policy dropped from the current ~8.4 MiB to only ~2.8 MiB (!!). The total amount of type transition rules was reduced from ~250K to only ~28K. The RHEL-8 policy is similar to the Fedora one, so we can also expect a similar effect there.

I'll open separate bugs for this functionality for Fedora and RHEL-8 and link them to this one. I'll leave this one open for discussion about possible selinux-policy-side mitigation (removing some of the transition rules) in case we want to do that as well.

Comment 21 Lukas Vrabec 2019-05-06 15:58:17 UTC
Hi Ondrej, 

Numbers looks really great! Could you link new bugs to this one? 

I agree to keep this bug open for further investigation in policy. 

Thanks for help,
Lukas.

Comment 23 Ondrej Mosnacek 2020-01-13 14:22:49 UTC
Mildly related to this: We recently committed a change on Rawhide [1], which removes ~23 MB of redundant files from selinux-policy-{targeted|minimum|mls}. These files were not shipped on RHEL-7, but they are shipped on RHEL-8 (they were added in Fedora in an attempt to fix BZ 1290659, which was later incompletely reverted and the unused files leaked to RHEL-8).

Before:
$ du -sh /usr/share/selinux/
23M     /usr/share/selinux/

After:
$ du -sh /usr/share/selinux/
232K    /usr/share/selinux/

[1] https://src.fedoraproject.org/rpms/selinux-policy/c/e4f809196473459c18881b41a2bd93fbce579c25?branch=master

Comment 26 Ondrej Mosnacek 2020-02-12 11:50:26 UTC
FYI, I found a way to store the filename transition rules in memory more efficiently, see:

https://lore.kernel.org/selinux/20200212112255.105678-1-omosnace@redhat.com/T/

The above kernel patch series should resolve the issue of increased memory usage by kernel. Eventually I plan to follow up with patches to update the binary policy format so that on-disk usage is also reduced. The issue with Fedora/RHEL-8 policy overusing these rules/unconfined domains remains (that is a much more tricky problem), but at least the performance/memory impact should be mitigated in future kernel versions.

Comment 27 Zdenek Pytela 2020-03-05 09:36:59 UTC
We are looking for a solution in kernel in the upstream.

Closing this bz as WONTFIX, the footprint and performance impact is expected to be resolved as a part of the next RHEL major release.

Comment 28 Ondrej Mosnacek 2020-03-30 10:29:53 UTC
Just FYI, I posted some kernel + userspace patches that also reduce the binary policy size impact of too many filename transitions:
https://lore.kernel.org/selinux/20200327151941.95619-1-omosnace@redhat.com/T/
https://lore.kernel.org/selinux/20200328124550.199568-1-omosnace@redhat.com/T/

With these patches the binary policy size should get back to RHEL-7 numbers.