All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tingmao Wang <m@maowtm.org>
To: "Mickaël Salaün" <mic@digikod.net>, "Günther Noack" <gnoack@google.com>
Cc: Tingmao Wang <m@maowtm.org>,
	linux-security-module@vger.kernel.org,
	Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>,
	Jann Horn <jannh@google.com>
Subject: [RFC PATCH v2 00/12] landlock: Use coalesced hashtable for merged domains
Date: Sun,  6 Jul 2025 16:16:41 +0100	[thread overview]
Message-ID: <cover.1751814658.git.m@maowtm.org> (raw)

Hi,

This implements the proposed data structure from the last discussion on
hashtable domains [1], in which we store all the rules and layers of a
domain in one flat array.  But instead of doing binary search, we use a
hashtable lookup, which has performance benefits.  The goal here is both
to improve performance and also reduce Landlock's memory footprint as much
as we reasonably can.


struct landlock_domain
----------------------

This patch set creates entirely new structs to use for a merged domain,
instead of using landlock_ruleset and landlock_rule (although we still use
landlock_layer).

Since we are no longer allocating individual struct landlock_rule for each
rule, and also because this representation is more compact and does not
use any rbtree pointers, this should reduce the memory usage for a merged
domain.  In the current implementation, even if each rule only has one
layer, the struct landlock_rule would effectively take up 64 bytes due to
kalloc placing it in the next power-of-2 bucket.

While this is not completely done in this series yet, the hope is that we
can remove all code that deals with the old landlock_ruleset domain, and
simplify the landlock_ruleset and landlock_rule struct since they now only
need to represent an unmerged ruleset, which will only contain one layer.
This series already removes some existing ruleset merging code.


Hashtable implementation
------------------------

This patch set implements a "coalesced hashtable", which uses an array
instead of linked lists, and uses the array itself to store collisions, by
storing them in "unused" slots (since the existence of collisions
necessarily mean that some hashes are not used).  A more detailed
explanation of this algorithm is included in the commit message for patch
2 and 5.

The hashtable we've implemented here is a immutable-after-construction
table (technically one could probably still append to it with some care,
but in principle it should be construct-once), which fits the use case for
landlock - merge a domain once, then we just need fast read-only query.

Some research has not led me to finding any existing implementation of
such a coalesced hashtable in the kernel, therefore this series introduces
new code for this algorithm.  Currently it's placed within
security/landlock, but it is written in a generic way that if somebody
else wants to use it (for example, current users of binary search on a
fixed array?), they can probably do so relatively easily (aside from
needing to move this header outside of landlock).

Testing
-------

All selftests pass under UML.

I plan to implement Kunit tests for the hashtable (and maybe also the
domain) in the next version.


Benchmark overview
------------------

I ran benchmark with before/after using two workloads, on the same machine
and setup:

    1. run_microbench with different depth and number of extra rules using
    code in [2]

    2. A more "typical" workload I put together quickly, with 18
    reasonably logical rules, calling git status and the like repeatedly
    [3].
    (I've consistently used this same workload for benchmarking previous
    performance improvements patches, to reduce the chances of accidental
    cherry-picking)

Results for the "typical" workload

Comparing:                    before    after
  landlock_overhead:    avg = 34        34
                     median = 35        34          (-1)
  landlock_hook:        avg = 878       856   (ns)  (-2.5%)
                     median = 854       831   (ns)  (-2.7%)
  open_syscall:         avg = 2517      2485  (ns)  (-1.3%)
                     median = 2457      2425  (ns)  (-1.3%)

Results for a 100 rules test with 10 dirs to walk upwards:

with landlock: d = /1/2/3/4/5/6/7/8/9/ nb_extra_rules = 100:
  landlock_overhead:    avg = 15     15
                     median = 17     16          (-1)
  landlock_hook:        avg = 832    785   (ns)  (-5.6%)
                     median = 826    776   (ns)  (-6.1%)
  open_syscall:         avg = 5163   5001  (ns)  (-3.1%)
                     median = 4763   4847  (ns)  (+1.8%)

Note that the 100 rules benchmark has quite variable performance, and the
testing method probably meant that most of the time is spent in VFS
lookup.  (Mickaël has gave some suggestions for improvement which I've yet
to do)

The full results and .config used (basically Debian) can be found at
https://fileshare.maowtm.org/landlock/20250706/index.html


Outstanding TODOs
-----------------

- selftests for the coalesced hash table, and maybe also for the domain
- simplify struct landlock_ruleset and struct landlock_rule since they now
  only need to deal with one layer,
- Using the name "layer" to refer to individual struct landlock_layers is
  very confusing especially with names like num_layers - the next version
  should probably find a better name for it.


[1]: https://lore.kernel.org/all/20250526.quec3Dohsheu@digikod.net/
[2]: https://github.com/landlock-lsm/landlock-test-tools/pull/17
[3]: https://github.com/micromaomao/linux-dev/commit/f1865ce970af97ac3b6f4edf580529b8cdc66371

Patch based on mic/next (v6.16-rc2+)

Closes: https://github.com/landlock-lsm/linux/issues/1

Changes since v1:
- Entirely replaced the hlist-based hashtable with an array-based one.
- This time added support for net rules too.

v1: https://lore.kernel.org/all/cover.1747836146.git.m@maowtm.org/

Tingmao Wang (12):
  landlock: Set the max rules limit in a domain to U16_MAX.
  landlock/domain: Define structure and macros for flat-array domains
  landlock: Define coalesced hashtable and implement finding
  landlock/domain: Implement finding rules
  landlock/coalesced_hash: Implement insert
  landlock/domain: Implement merging of a parent domain and a ruleset
  landlock/domain: Define alloc and free
  landlock/domain: Add landlock_domain_merge_ruleset
  landlock: Update various code to use landlock_domain
  landlock: Remove unused code
  landlock/task: Fix incorrect BUILD_BUG_ON() in domain_is_scoped
  landlock: Use a hash function that does not involve division

 security/landlock/audit.c          |   8 +-
 security/landlock/coalesced_hash.h | 399 +++++++++++++++++
 security/landlock/cred.c           |  12 +-
 security/landlock/cred.h           |  14 +-
 security/landlock/domain.c         | 681 +++++++++++++++++++++++++++++
 security/landlock/domain.h         | 342 ++++++++++++++-
 security/landlock/fs.c             |  34 +-
 security/landlock/limits.h         |   2 +-
 security/landlock/net.c            |  12 +-
 security/landlock/ruleset.c        | 319 +-------------
 security/landlock/ruleset.h        |  71 +--
 security/landlock/syscalls.c       |   8 +-
 security/landlock/task.c           |  31 +-
 13 files changed, 1499 insertions(+), 434 deletions(-)
 create mode 100644 security/landlock/coalesced_hash.h


base-commit: 86fdfbade8bb09ce2be2ff334c743fe19815ceb2
-- 
2.49.0

             reply	other threads:[~2025-07-06 15:17 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-06 15:16 Tingmao Wang [this message]
2025-07-06 15:16 ` [RFC PATCH v2 01/12] landlock: Set the max rules limit in a domain to U16_MAX Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 02/12] landlock/domain: Define structure and macros for flat-array domains Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 03/12] landlock: Define coalesced hashtable and implement finding Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 04/12] landlock/domain: Implement finding rules Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 05/12] landlock/coalesced_hash: Implement insert Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 06/12] landlock/domain: Implement merging of a parent domain and a ruleset Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 07/12] landlock/domain: Define alloc and free Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 08/12] landlock/domain: Add landlock_domain_merge_ruleset Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 09/12] landlock: Update various code to use landlock_domain Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 10/12] landlock: Remove unused code Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 11/12] landlock/task: Fix incorrect BUILD_BUG_ON() in domain_is_scoped Tingmao Wang
2025-07-06 15:16 ` [RFC PATCH v2 12/12] landlock: Use a hash function that does not involve division Tingmao Wang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cover.1751814658.git.m@maowtm.org \
    --to=m@maowtm.org \
    --cc=gnoack@google.com \
    --cc=ivanov.mikhail1@huawei-partners.com \
    --cc=jannh@google.com \
    --cc=linux-security-module@vger.kernel.org \
    --cc=mic@digikod.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.