Skip to content

Add mask command and modprobe test with blacklist #312

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libkmod/docs/libkmod-docs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,8 @@
<title>Index of new symbols in 33</title>
<xi:include href="xml/api-index-33.xml"></xi:include>
</chapter>
<chapter id="api-index-v35" role="35">
<title>Index of new symbols in 35</title>
<xi:include href="xml/api-index-35.xml"></xi:include>
</chapter>
</book>
1 change: 1 addition & 0 deletions libkmod/docs/libkmod-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ kmod_list_prev
kmod_config_iter
kmod_config_get_blacklists
kmod_config_get_install_commands
kmod_config_get_masks
kmod_config_get_remove_commands
kmod_config_get_aliases
kmod_config_get_options
Expand Down
57 changes: 55 additions & 2 deletions libkmod/libkmod-config.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ struct kmod_weakdep {
unsigned int n_weak;
};

const char *kmod_mask_get_modname(const struct kmod_list *l)
{
return l->data;
}

const char *kmod_blacklist_get_modname(const struct kmod_list *l)
{
return l->data;
Expand Down Expand Up @@ -231,6 +236,27 @@ static int kmod_config_add_blacklist(struct kmod_config *config, const char *mod
return 0;
}

static int kmod_config_add_mask(struct kmod_config *config, const char *modname)
{
_cleanup_free_ char *p;
struct kmod_list *list;

DBG(config->ctx, "modname=%s\n", modname);

_clang_suppress_alloc_ p = strdup(modname);
if (!p)
return -ENOMEM;

list = kmod_list_append(config->masks, p);
if (!list)
return -ENOMEM;

TAKE_PTR(p);
config->masks = list;

return 0;
}

static int kmod_config_add_softdep(struct kmod_config *config, const char *modname,
const char *line)
{
Expand Down Expand Up @@ -606,18 +632,24 @@ static char *weakdep_to_char(struct kmod_weakdep *dep)
static void kcmdline_parse_result(struct kmod_config *config, char *modname, char *param,
char *value)
{
bool is_blacklist, is_mask;
if (modname == NULL || param == NULL)
return;

DBG(config->ctx, "%s %s\n", modname, param);

if (streq(modname, "modprobe") && strstartswith(param, "blacklist=")) {
is_blacklist = strstartswith(param, "blacklist=");
is_mask = strstartswith(param, "mask=");
if (streq(modname, "modprobe") && (is_blacklist || is_mask)) {
for (;;) {
char *t = strsep(&value, ",");
if (t == NULL)
break;

kmod_config_add_blacklist(config, t);
if (is_blacklist)
kmod_config_add_blacklist(config, t);
else
kmod_config_add_mask(config, t);
}
} else {
if (underscores(modname) < 0) {
Expand Down Expand Up @@ -823,6 +855,13 @@ static int kmod_config_parse(struct kmod_config *config, int fd, const char *fil

kmod_config_add_command(config, modname, installcmd, cmd,
&config->install_commands);
} else if (streq(cmd, "mask")) {
char *modname = strtok_r(NULL, "\t ", &saveptr);

if (underscores(modname) < 0)
goto syntax_error;

kmod_config_add_mask(config, modname);
} else if (streq(cmd, "remove")) {
char *modname = strtok_r(NULL, "\t ", &saveptr);
char *removecmd = strtok_r(NULL, "\0", &saveptr);
Expand Down Expand Up @@ -872,6 +911,7 @@ void kmod_config_free(struct kmod_config *config)
kmod_list_release(config->blacklists, free);
kmod_list_release(config->options, free);
kmod_list_release(config->install_commands, free);
kmod_list_release(config->masks, free);
kmod_list_release(config->remove_commands, free);
kmod_list_release(config->softdeps, free);
kmod_list_release(config->weakdeps, free);
Expand Down Expand Up @@ -1094,6 +1134,7 @@ int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
enum config_type {
CONFIG_TYPE_BLACKLIST = 0,
CONFIG_TYPE_INSTALL,
CONFIG_TYPE_MASK,
CONFIG_TYPE_REMOVE,
CONFIG_TYPE_ALIAS,
CONFIG_TYPE_OPTION,
Expand Down Expand Up @@ -1144,6 +1185,10 @@ static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx *ctx,
iter->get_key = kmod_command_get_modname;
iter->get_value = kmod_command_get_command;
break;
case CONFIG_TYPE_MASK:
iter->list = config->masks;
iter->get_key = kmod_mask_get_modname;
break;
case CONFIG_TYPE_REMOVE:
iter->list = config->remove_commands;
iter->get_key = kmod_command_get_modname;
Expand Down Expand Up @@ -1194,6 +1239,14 @@ KMOD_EXPORT struct kmod_config_iter *kmod_config_get_install_commands(const stru
return kmod_config_iter_new(ctx, CONFIG_TYPE_INSTALL);
}

KMOD_EXPORT struct kmod_config_iter *kmod_config_get_masks(const struct kmod_ctx *ctx)
{
if (ctx == NULL)
return NULL;

return kmod_config_iter_new(ctx, CONFIG_TYPE_MASK);
}

// clang-format off
KMOD_EXPORT struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *ctx)
// clang-format on
Expand Down
2 changes: 2 additions & 0 deletions libkmod/libkmod-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct kmod_config {
struct kmod_list *aliases;
struct kmod_list *blacklists;
struct kmod_list *options;
struct kmod_list *masks;
struct kmod_list *remove_commands;
struct kmod_list *install_commands;
struct kmod_list *softdeps;
Expand All @@ -112,6 +113,7 @@ _nonnull_all_ void kmod_config_free(struct kmod_config *config);
_nonnull_all_ const char *kmod_blacklist_get_modname(const struct kmod_list *l);
_nonnull_all_ const char *kmod_alias_get_name(const struct kmod_list *l);
_nonnull_all_ const char *kmod_alias_get_modname(const struct kmod_list *l);
_nonnull_all_ const char *kmod_mask_get_modname(const struct kmod_list *l);
_nonnull_all_ const char *kmod_option_get_options(const struct kmod_list *l);
_nonnull_all_ const char *kmod_option_get_modname(const struct kmod_list *l);
_nonnull_all_ const char *kmod_command_get_command(const struct kmod_list *l);
Expand Down
54 changes: 48 additions & 6 deletions libkmod/libkmod-module.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,12 +734,30 @@ static bool module_is_blacklisted(const struct kmod_module *mod)
return false;
}

static bool module_is_masked(const struct kmod_module *mod)
{
const struct kmod_ctx *ctx = mod->ctx;
const struct kmod_config *config = kmod_get_config(ctx);
const struct kmod_list *bl = config->masks;
const struct kmod_list *l;

kmod_list_foreach(l, bl) {
const char *modname = kmod_mask_get_modname(l);

if (streq(modname, mod->name))
return true;
}

return false;
}

KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx,
enum kmod_filter filter_type,
const struct kmod_list *input,
struct kmod_list **output)
{
const struct kmod_list *li;
int err = 0;

if (ctx == NULL || output == NULL)
return -ENOENT;
Expand All @@ -758,9 +776,20 @@ KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx,
if ((filter_type & KMOD_FILTER_BUILTIN) && kmod_module_is_builtin(mod))
continue;

if ((filter_type & KMOD_FILTER_MASK) && module_is_masked(mod)) {
if (!mod->required)
continue;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nit:

if !required
   continue

ERR(...)
err = ...;
goto fail;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

ERR(mod->ctx, "module is masked: %s\n",
kmod_module_get_name(mod));
err = -EINVAL;
goto fail;
}

node = kmod_list_append(*output, mod);
if (node == NULL)
if (node == NULL) {
err = -ENOMEM;
goto fail;
}

*output = node;
kmod_module_ref(mod);
Expand All @@ -771,7 +800,7 @@ KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx,
fail:
kmod_module_unref_list(*output);
*output = NULL;
return -ENOMEM;
return err;
}

static int command_do(struct kmod_module *mod, const char *type, const char *cmd)
Expand Down Expand Up @@ -1005,7 +1034,7 @@ KMOD_EXPORT int kmod_module_probe_insert_module(
const void *data,
void (*print_action)(struct kmod_module *m, bool install, const char *options))
{
struct kmod_list *list = NULL, *l;
struct kmod_list *list = NULL, *l, *filtered = NULL;
struct probe_insert_cb cb;
int err;

Expand All @@ -1019,6 +1048,11 @@ KMOD_EXPORT int kmod_module_probe_insert_module(
return 0;
}

if (module_is_masked(mod)) {
ERR(mod->ctx, "module is masked: %s\n", kmod_module_get_name(mod));
return -EINVAL;
}

if (module_is_blacklisted(mod)) {
if (mod->alias != NULL && (flags & KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY))
return KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY;
Expand All @@ -1035,15 +1069,23 @@ KMOD_EXPORT int kmod_module_probe_insert_module(
if (err < 0)
return err;

if (flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) {
struct kmod_list *filtered = NULL;
err = kmod_module_apply_filter(mod->ctx, KMOD_FILTER_MASK, list, &filtered);
kmod_module_unref_list(list);
if (err < 0)
return err;

if (filtered == NULL)
return -EINVAL;

list = filtered;

if (flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) {
err = kmod_module_apply_filter(mod->ctx, KMOD_FILTER_BLACKLIST, list,
&filtered);
kmod_module_unref_list(list);
if (err < 0)
return err;

kmod_module_unref_list(list);
Copy link
Collaborator

@evelikov evelikov May 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely spotted. Let's split this one-line corner-case memory leak fix in its* own commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity (aka non-blocking question): What is the command which produces the Sanitizer leak report? Does one need to modify code or it happens out of the box?

Copy link
Contributor

@jspcki jspcki Jul 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had ASAN complaining primarily at 1072 when we had a similar sequence of statements: filter -> return -> unref. You'd need to hit any of the failing conditions of the filter: (ctx == NULL || output == NULL), mod->required, or kmod_list_append(*output, mod) == NULL. Other than required they are unlikely. Right now easiest way would be to inject error there manually and it's the way I generated sanitizer output for the commit.

if (filtered == NULL)
return KMOD_PROBE_APPLY_BLACKLIST_ALL;

Expand Down
18 changes: 18 additions & 0 deletions libkmod/libkmod.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,22 @@ struct kmod_list *kmod_list_prev(const struct kmod_list *list,
*/
struct kmod_config_iter;

/**
* kmod_config_get_masks:
* @ctx: kmod library context
*
* Retrieve an iterator to deal with the mask list maintained inside the
* library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
* kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
* be made to initialize the iterator and check if it's valid.
*
* Returns: a new iterator over the masks or NULL on failure. Free it
* with kmod_config_iter_free_iter().
*
* Since: 35
*/
struct kmod_config_iter *kmod_config_get_masks(const struct kmod_ctx *ctx);

/**
* kmod_config_get_blacklists:
* @ctx: kmod library context
Expand Down Expand Up @@ -905,12 +921,14 @@ int kmod_module_get_weakdeps(const struct kmod_module *mod, struct kmod_list **w
* kmod_filter:
* @KMOD_FILTER_BLACKLIST: filter modules in blacklist out
* @KMOD_FILTER_BUILTIN: filter builtin modules out
* @KMOD_FILTER_MASK: filter masked modules out
*
* Bitmask defining what gets filtered out, used by kmod_module_apply_filter().
*/
enum kmod_filter {
KMOD_FILTER_BLACKLIST = 0x00001,
KMOD_FILTER_BUILTIN = 0x00002,
KMOD_FILTER_MASK = 0x00004,
};

/**
Expand Down
5 changes: 5 additions & 0 deletions libkmod/libkmod.sym
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,8 @@ global:
kmod_config_get_weakdeps;
kmod_module_get_weakdeps;
} LIBKMOD_30;

LIBKMOD_35 {
global:
kmod_config_get_masks;
} LIBKMOD_33;
3 changes: 2 additions & 1 deletion man/modprobe.8.scd
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ that for convenience, there is no difference between \_ and - in module names
directory @DISTCONFDIR@/`uname -r` for all the modules and other files, except
for the optional configuration files (see *modprobe.d*(5)). *modprobe* will also
use module options specified on the kernel command line in the form of
<module>.<option> and blacklists in the form of modprobe.blacklist=<module>.
<module>.<option>, blacklists in the form of modprobe.blacklist=<module>
and masks in modprobe.mask=<module>.

Note that unlike in 2.4 series Linux kernels (which are not supported by this
tool) this version of *modprobe* does not do anything to the module itself: the
Expand Down
4 changes: 4 additions & 0 deletions man/modprobe.d.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ install _modulename_ _command..._
/sbin/modprobe barney; /sbin/modprobe --ignore-install fred
$CMDLINE_OPTS"

mask _modulename_
This command prevents _modulename_ from being loaded directly
or through any of its aliases.

options _modulename_ _option..._
This command allows you to add options to the module _modulename_ (which
might be an alias) every time it is inserted into the kernel: whether
Expand Down
14 changes: 14 additions & 0 deletions scripts/setup-rootfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ map=(
["test-modprobe/external/lib/modules/external/"]="mod-simple.ko"
["test-modprobe/module-from-abspath/home/foo/"]="mod-simple.ko"
["test-modprobe/module-from-relpath/home/foo/"]="mod-simple.ko"
["test-modprobe/blacklist$MODULE_DIRECTORY/4.4.4/kernel/"]="mod-simple.ko"
["test-modprobe/blacklist-dep$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-a.ko"]="mod-foo-a.ko"
["test-modprobe/blacklist-dep$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-b.ko"]="mod-foo-b.ko"
["test-modprobe/blacklist-dep$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-c.ko"]="mod-foo-c.ko"
["test-modprobe/blacklist-dep$MODULE_DIRECTORY/4.4.4/kernel/mod-foo.ko"]="mod-foo.ko"
["test-modprobe/blacklist-loaded$MODULE_DIRECTORY/4.4.4/kernel/"]="mod-simple.ko"
["test-modprobe/blacklist-softdep$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-a.ko"]="mod-foo-a.ko"
["test-modprobe/blacklist-softdep$MODULE_DIRECTORY/4.4.4/kernel/mod-simple.ko"]="mod-simple.ko"
["test-modprobe/mask$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-a.ko"]="mod-foo-a.ko"
["test-modprobe/mask$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-b.ko"]="mod-foo-b.ko"
["test-modprobe/mask$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-c.ko"]="mod-foo-c.ko"
["test-modprobe/mask$MODULE_DIRECTORY/4.4.4/kernel/mod-foo.ko"]="mod-foo.ko"
["test-modprobe/mask-softdep$MODULE_DIRECTORY/4.4.4/kernel/mod-foo-a.ko"]="mod-foo-a.ko"
["test-modprobe/mask-softdep$MODULE_DIRECTORY/4.4.4/kernel/mod-simple.ko"]="mod-simple.ko"
["test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/block/cciss.ko"]="mod-fake-cciss.ko"
["test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/hpsa.ko"]="mod-fake-hpsa.ko"
["test-depmod/modules-order-compressed$MODULE_DIRECTORY/4.4.4/kernel/drivers/scsi/scsi_mod.ko"]="mod-fake-scsi-mod.ko"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
blacklist mod-foo-b
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Aliases extracted from modules themselves.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
kernel/mod-foo-b.ko:
kernel/mod-foo-c.ko:
kernel/mod-foo-a.ko:
kernel/mod-foo.ko: kernel/mod-foo-b.ko kernel/mod-foo-a.ko kernel/mod-foo-c.ko
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Device nodes to trigger on-demand module loading.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Soft dependencies extracted from modules themselves.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Aliases for symbols, used by symbol_request().
alias symbol:print_fooA mod_foo_a
alias symbol:print_fooC mod_foo_c
alias symbol:print_fooB mod_foo_b
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
alias simple.* mod-simple
blacklist mod-simple
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Aliases extracted from modules themselves.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
kernel/mod-simple.ko:
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Device nodes to trigger on-demand module loading.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Soft dependencies extracted from modules themselves.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Aliases for symbols, used by symbol_request().
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
softdep mod-simple pre: mod-foo-a
blacklist mod-foo-a
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Aliases extracted from modules themselves.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
kernel/mod-foo-a.ko:
kernel/mod-simple.ko:
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Device nodes to trigger on-demand module loading.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Soft dependencies extracted from modules themselves.
Loading