-
Notifications
You must be signed in to change notification settings - Fork 361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
libselinux: rework selabel_file(5) database #406
Closed
Closed
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cgzones
force-pushed
the
label_lookup_perf
branch
3 times, most recently
from
August 14, 2023 13:13
5f14b69
to
90929c4
Compare
cgzones
force-pushed
the
label_lookup_perf
branch
3 times, most recently
from
December 20, 2023 18:15
414c040
to
1b106eb
Compare
cgzones
force-pushed
the
label_lookup_perf
branch
3 times, most recently
from
January 30, 2024 14:09
fa2f731
to
3d57563
Compare
cgzones
force-pushed
the
label_lookup_perf
branch
2 times, most recently
from
March 11, 2024 17:19
f07e4c3
to
090af7e
Compare
Introduce a helper to remove SELinux file security contexts. Mainly for testing label operations, and only for SELinux disabled systems, since removing file contexts is not supported by SELinux. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: move from libselinux/utils to policycoreutils and rename
Add a utility around selabel_cmp(3). Can be used by users to compare a pre-compiled fcontext file to an original text-based file context definition file. Can be used for development to verify compilation and parsing of the pre-compiled fcontext format works correctly. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: split nested block into own function
Use type unsigned for hash values, as returned by sidtab_hash(). Use size_t for buffer length and counting variables. Constify stats parameter. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: add patch
Reinterpret the currently unused - and always initialized to 1 - member refcnt of the struct security_id to hold a unique number identifying the sidtab entry. This identifier can be used instead of the full context string within other data structures to minimize memory usage. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: add patch
Add sidtab_context_lookup() to just lookup a context, not inserting non-existent ones. Tweak sidtab_destroy() to accept a zero'ed struct sidtab. Remove redundant lookup in sidtab_context_to_sid() after insertion by returning the newly created node directly from sidtab_insert(). Drop declaration of only internal used sidtab_insert(). Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v3: use sidtab_context_lookup() in sidtab_context_to_sid() v2: add patch
cgzones
force-pushed
the
label_lookup_perf
branch
2 times, most recently
from
November 5, 2024 17:10
afd6484
to
c235075
Compare
Currently the database for file backend of selabel stores the file context specifications in a single long array. This array is sorted by special precedence rules, e.g. regular expressions without meta character first, ordered by length, and the remaining regular expressions ordered by stem (the prefix part of the regular expressions without meta characters) length. This results in suboptimal lookup performance for two reasons; File context specifications without any meta characters (e.g. '/etc/passwd') are still matched via an expensive regular expression match operation. All such trivial regular expressions are matched against before any non- trivial regular expression, resulting in thousands of regex match operations for lookups for paths not matching any of the trivial ones. Rework the internal representation of the database in two ways: Convert regular expressions without any meta characters and containing only supported escaped characters (e.g. '/etc/rc\.d/init\.d') into literal strings, which get compared via strcmp(3) later on. Store the specifications in a tree structure to reduce the to number of specifications that need to be checked. Since the internal representation is completely rewritten introduce a new compiled file context file format mirroring the tree structure. The new format also stores all multi-byte data in network byte-order, so that such compiled files can be cross-compiled, e.g. for embedded devices with read-only filesystems (except for the regular expressions, which are still architecture-dependent, but ignored on architecture mis- match). The improved lookup performance will also benefit SELinux aware daemons, which create files with their default context, e.g. systemd. Fedora 41 (pre-compiled regular expressions are omitted on Fedora): file_contexts.bin: 567248 -> 413191 (bytes) file_contexts.homedirs.bin: 20677 -> 13107 (bytes) Debian Sid (pre-compiled regular expressions are included): file_contexts.bin: 7790690 -> 3646256 (bytes) file_contexts.homedirs.bin: 835950 -> 708793 (bytes) (selabel_lookup -b file -k /bin/bash) Fedora 41 in VM: text: time: 7.2 ms -> 3.5 ms peak heap: 2.33M -> 1.81M peak rss: 6.64M -> 6.37M compiled: time: 5.9 ms -> 1.6 ms peak heap: 2.14M -> 1.23M peak rss: 6.76M -> 5.91M Debian Sid on Raspberry Pi 3: text: time: 33.4 ms -> 21.2 ms peak heap: 10.59M -> 607.32K peak rss: 6.55M -> 4.46M compiled: time: 38.3 ms -> 23.5 ms peak heap: 13.28M -> 2.00M peak rss: 12.21M -> 7.60M (restorecon -vRn /) Fedora 41 in VM: 9.6 s -> 1.3 s Debian Sid on Raspberry Pi 3: 94.6 s -> 12.1 s (restorecon -vRn -T0 /) Fedora 39 in VM (8 cores): 10.9 s -> 1.0 s Debian Sid on Raspberry Pi 3 (4 cores): 58.9 s -> 12.6 s (note: I am unsure why the parallel runs on Fedora are slower) There might be subtle differences in lookup results which evaded my testing, because some precedence rules are oblique. For example `/usr/(.*/)?lib(/.*)?` has to have a higher precedence than `/usr/(.*/)?bin(/.*)?` to match the current Fedora behavior. Please report any behavior changes. The maximum node depth in the database is set to 3, which seems to give the best performance to memory usage ratio. Might be tweaked for systems with different filesystem hierarchies (Android?). I am not that familiar with the selabel_partial_match(3), selabel_get_digests_all_partial_matches(3) and selabel_hash_all_partial_matches(3) related interfaces, so I only did some rudimentary tests for them. CC: Petr Lautrbach <plautrba@redhat.com> CC: James Carter <jwcart2@gmail.com> CC: Stephen Smalley <stephen.smalley.work@gmail.com> Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v3: - set errno to EINVAL on old compiled fcontext format file input - correctly compare regular expression specifications by considering their prefix-length - reorder calloc(3) arguments v2: misc issues revealed via fuzzing: - free root node via data pointer in sefcontext_compile - drop reachable assert in regex_simplify() - fix crash on unexpected NUL byte in read_spec_entries() - more strict checks when loading compiled format - do not return <<none>> context for literal specs - add missing partial support for literal specs - set errno on load_mmap() failure - support SELABEL_OPT_SUBSET - de-duplicate context strings into a separate array for the binary format - store an internal file kind instead of the whole mode_t
Due to the selabel_file(5) rework this code is no longer used. Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Add two fuzzers reading and performing lookup on selabel_file(5) databases. One fuzzer takes input in form of a textual fcontext definition, the other one takes compiled fcontexts definitions. The lookup key and whether to lookup any or a specific file type is also part of the generated input. CC: Evgeny Vereshchagin <evverx@gmail.com> Signed-off-by: Christian Göttsche <cgzones@googlemail.com> --- v2: add patch
Support the parallel usage of the translated label lookup via selabel_lookup(3) in multi threaded applications by locking the step of computing the translated context and the validation state. A potential use case might can usage from a Rust application via FFI. Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
cgzones
force-pushed
the
label_lookup_perf
branch
from
November 5, 2024 18:26
c235075
to
c47d813
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Currently the database for file backend of selabel stores the file
context specifications in a single long array. This array is sorted by
special precedence rules, e.g. regular expressions without meta
character first, ordered by length, and the remaining regular
expressions ordered by stem (the prefix part of the regular expressions
without meta characters) length.
This results in suboptimal lookup performance for two reasons;
File context specifications without any meta characters (e.g.
'/etc/passwd') are still matched via an expensive regular expression
match operation.
All such trivial regular expressions are matched against before any non-
trivial regular expression, resulting in thousands of regex match
operations for lookups for paths not matching any of the trivial ones.
Rework the internal representation of the database in two ways:
Convert regular expressions without any meta characters and containing
only supported escaped characters (e.g. '/etc/rc.d/init.d') into
literal strings, which get compared via strcmp(3) later on.
Store the specifications in a tree structure (since the filesystem is a
tree) to reduce the to number of specifications that need to be checked.
Since the internal representation is completely rewritten introduce a
new compiled file context file format mirroring the tree structure.
The new format also stores all multi-byte data in network byte-order, so
that such compiled files can be cross-compiled, e.g. for embedded
devices with read-only filesystems (except for the regular expressions,
which are still architecture-dependent).
The improved lookup performance will also benefit SELinux aware daemons,
which create files with their default context, e.g. systemd.
Performance data
Compiled file context sizes
Fedora 38 (regular expressions are omitted on Fedora):
file_contexts.bin: 596783 -> 575284 (bytes)
file_contexts.homedirs.bin: 21219 -> 18185 (bytes)
Debian Sid (regular expressions are included):
file_contexts.bin: 2580704 -> 1428354 (bytes)
file_contexts.homedirs.bin: 130946 -> 96884 (bytes)
Single lookup
(selabel -b file -k /bin/bash)
Fedora 38 in VM:
text: time: 3.6 ms -> 4.7 ms
peak heap: 2.32M -> 1.44M
peak rss: 5.61M -> 6.03M
compiled: time: 1.5 ms -> 1.5 ms
peak heap: 2.14M -> 917.93K
peak rss: 5.33M -> 5.47M
Debian Sid on Raspberry Pi 3:
text: time: 33.9 ms -> 19.9 ms
peak heap: 10.46M -> 468.72K
peak rss: 9.44M -> 4.98M
compiled: time: 39.3 ms -> 22.8 ms
peak heap: 13.09M -> 1.86M
peak rss: 12.57M -> 7.86M
Full filesystem relabel
(restorecon -vRn /)
Fedora 38 in VM:
27.445 s -> 3.293 s
Debian Sid on Raspberry Pi 3:
86.734 s -> 10.810 s
(restorecon -vRn -T0 /)
Fedora 38 in VM (8 cores):
29.205 s -> 2.521 s
Debian Sid on Raspberry Pi 3 (4 cores):
46.974 s -> 10.728 s
(note: I am unsure why the parallel runs on Fedora are slower)
TODO
There might be subtle differences in lookup results which evaded my
testing, because some precedence rules are oblique. For example
/usr/(.*/)?lib(/.*)?
has to have a higher precedence than/usr/(.*/)?bin(/.*)?
to match the current Fedora behavior. Pleasereport any behavior changes.
If any code section is unclear I am happy to add some inline comments.
The maximum node depth in the database is set to 3, which seems to give
the best performance to memory usage ratio. Might be tweaked for
systems with different filesystem hierarchies (Android?).
I am not that familiar with the selabel_partial_match(3),
selabel_get_digests_all_partial_matches(3) and
selabel_hash_all_partial_matches(3) related interfaces, so I only did
some rudimentary tests for them.