Skip to content

fix: avoid following symlinks in kernel module completion - #1736

Open
LuckJC wants to merge 1 commit into
scop:mainfrom
LuckJC:fix/modprobe-completion-symlink-traversal
Open

LuckJC wants to merge 1 commit into
scop:mainfrom
LuckJC:fix/modprobe-completion-symlink-traversal

Conversation

@LuckJC

@LuckJC LuckJC commented Sep 15, 2026 •

Copy link
Copy Markdown

Problem

_comp_compgen_kernel_modules() uses ls -RL to recursively list module
files under /lib/modules/$(uname -r)/. The -L flag (added in 98fe471
to follow symlinks for distros that organize modules via symlinked
subdirectories) also follows the build and source symlinks created by
the kernel build system (scripts/Makefile.modinst), which point to the
full kernel build/source tree.

When the build tree is large (common with O= out-of-tree builds),
following these symlinks recursively opens hundreds of thousands of files.
This can exhaust the system file table (ENFILE) and cause shared library
loading failures for completion helper programs:

$ modprobe iok<TAB>
sed: error while loading shared libraries: libc.so.6:
cannot open shared object file: Error 23

Additionally, the old sed regex \.k\{0,1\}o (from 2003, for kernel 2.4
.o module backward compatibility) matches both .ko and .o files,
producing false positives from build tree object files.

Fix

Replace ls -RL | sed with find -L using -path to prune only the
top-level build/source symlinks:

find -L "$_modpath" \
    \( -path "$_modpath/build" -o -path "$_modpath/source" \) -prune -o \
    \( -name '*.ko' -o -name '*.ko.gz' -o -name '*.ko.xz' \
    -o -name '*.ko.zst' \) -print
  • -L follows symlinks (preserves 98fe471 behavior)
  • -path only prunes top-level build/source, not nested dirs
  • -name '*.ko' uses exact glob matching, no false .o positives
  • -print outside the -o group avoids short-circuit evaluation

Test results

10 scenarios + 3 benchmarks, all passing in a virtme-ng VM:

Scenario Description Result
1 Normal module dirs (no symlinks) ✅
2 build/source symlinks + .o false positive verification ✅
3 Symlinked module subdirectory (2011 scenario) ✅
4 All combined (build/source + symlinked subdir) ✅
5 Empty module directory ✅
6 Non-existent path ✅
7 Nested dirs named build/source (not pruned with -path) ✅
8 All compression formats (.ko/.ko.gz/.ko.xz/.ko.zst) ✅
9 Circular symlink (real kernel: build→buildtree, source→moddir) ✅
10 Nested build/source in subdirectories ✅
Benchmark old_impl (ls -RL) new_impl (find -L)
3 modules, no symlinks 11ms 12ms
build/source → 600 files 17ms 11ms
build/source → 5000 files 30ms 12ms

@akinomyoga

akinomyoga commented Sep 21, 2026 •

Copy link
Copy Markdown
Collaborator

Commit 98fe471 has started to follow the symbolic links to fix Alioth #313461. This PR doesn't seem to pay attention to that.

@LuckJC

LuckJC commented Sep 22, 2026

Copy link
Copy Markdown
Author

@akinomyoga Thanks for pointing out 98fe471. The fix has been updated to
address both concerns:

  1. Preserves -L (98fe471 behavior): The new code still uses
    find -L to follow symlinks, so symlinked module subdirectories
    (e.g. updates/) are still traversed.

  2. Uses -path instead of -name to avoid over-pruning: Changed
    from -name build to -path "$_modpath/build" so only the
    top-level build/source symlinks are pruned. Nested directories
    that happen to be named build/source are still traversed
    normally.

  3. Also fixes a regex bug from 2003: The old sed regex
    \.k\{0,1\}o was added in c0ebebe for kernel 2.4 backward
    compatibility. In modern kernels, .o files in the build tree
    are object files, not modules. find -name '*.ko' uses exact
    glob matching, eliminating false positives.

Tested with 10 scenarios + 3 benchmarks in a virtme-ng VM,
including the real kernel circular symlink pattern. All pass.

@LuckJC
LuckJC force-pushed the fix/modprobe-completion-symlink-traversal branch from 51a497e to 1ef1c87 Compare September 22, 2026 08:10
_comp_compgen_kernel_modules() used `ls -RL` to recursively list
module files. The -L flag (added in 98fe471 to follow symlinks
for distros that organize modules via symlinked subdirectories)
also follows the `build` and `source` symlinks created by the
kernel build system (scripts/Makefile.modinst), which point to
the full kernel build/source tree.

When the build tree is large (common with O= out-of-tree builds),
following these symlinks recursively opens hundreds of thousands
of files. This can exhaust the system file table (ENFILE) and
cause shared library loading failures for completion helpers:

    $ modprobe iok<TAB>
    sed: error while loading shared libraries: libc.so.6:
    cannot open shared object file: Error 23

Additionally, the old `sed` regex `\.k\{0,1\}o` (from 2003, for
kernel 2.4 `.o` module backward compat) matches both `.ko` and
`.o` files, producing false positives from build tree objects.

Replace `ls -RL | sed` with `find -L` that preserves symlink
following but prunes the top-level build/source entries:

    find -L "$_modpath" \
        \( -path "$_modpath/build" -o \
        -path "$_modpath/source" \) -prune -o \
        \( -name "*.ko" -o -name "*.ko.gz" -o -name "*.ko.xz" \
        -o -name "*.ko.zst" \) -print

- `-L` follows symlinks (preserves 98fe471 behavior)
- `-path` only prunes top-level build/source, not nested dirs
- `-name "*.ko"` uses glob matching (no false .o positives)
- `-print` outside the -o group avoids short-circuit evaluation

Signed-off-by: Chao Huang <huangchao@kylin.com>
@LuckJC
LuckJC force-pushed the fix/modprobe-completion-symlink-traversal branch from 1ef1c87 to 160a55a Compare September 28, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants