Load and unload operations

Modules can be deployed through special tools that are part of an application package called modutils, of which insmod and rmmod are widely used. insmod is used to deploy the module into kernel address space and rmmod is used for unloading a live module. These tools initiate load/unload operations by invoking appropriate system calls:

int finit_module(int fd, const char *param_values, int flags);
int delete_module(const char *name, int flags);

Here, finit_module() is invoked (by insmod) with the file descriptor of the specified module binary file (.ko) and other relevant arguments. This function steps into kernel mode by invoking the underlying system call:

SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
{
struct load_info info = { };
loff_t size;
void *hdr;
int err;

err = may_init_module();
if (err)
return err;

pr_debug("finit_module: fd=%d, uargs=%p, flags=%i ", fd, uargs, flags);

if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS
|MODULE_INIT_IGNORE_VERMAGIC))
return -EINVAL;

err = kernel_read_file_from_fd(fd, &hdr, &size, INT_MAX,
READING_MODULE);
if (err)
return err;
info.hdr = hdr;
info.len = size;

return load_module(&info, uargs, flags);
}

Here, may_init_module() is called to verify the CAP_SYS_MODULE privilege of the calling context; this function returns a negative number on failure and zero on success. If the caller has the required privilege, a specified module image is accessed through fd using the kernel_read_file_from_fd() routine that returns address of the module image, which is populated into an instance of struct load_info. Finally, the load_module() core kernel routine is invoked with address to instance of load_info and other user arguments passed down from the finit_module() call:

static int load_module(struct load_info *info, const char __user *uargs,int flags)
{
struct module *mod;
long err;
char *after_dashes;

err = module_sig_check(info, flags);
if (err)
goto free_copy;

err = elf_header_check(info);
if (err)
goto free_copy;

/* Figure out module layout, and allocate all the memory. */
mod = layout_and_allocate(info, flags);
if (IS_ERR(mod)) {
err = PTR_ERR(mod);
goto free_copy;
}

....
....
....

}

Here, load_module() is a core kernel routine that attempts to link module image into kernel address space. This function initiates a series of sanity checks, and finally commits the module by initializing module parameters to values provided by the caller, and invokes the init function of the module. The following steps detail these operations with names of the relevant helper functions invoked:

  • Checking for the signature (module_sig_check())
  • Checking for the ELF header (elf_header_check())
  • Checking the module layout and allocate the necessary memory (layout_and_allocate())
  • Appending the module to the modules list (add_unformed_module())
  • Allocate per-cpu areas used in the module (percpu_modalloc())
  • As module is in final location, finding the optional sections (find_module_sections())
  • Checking for module license and versions (check_module_license_and_versions())
  • Resolving the symbols (simplify_symbols())
  • Setting up the module parameters as per values passed in the args list
  • Checking for duplication of symbols (complete_formation())
  • Setting up the sysfs (mod_sysfs_setup())
  • Freeing the copy in the load_info structure (free_copy())
  • Calling to the init function of the module (do_init_module())

The unloading process is quite similar to the loading process; the only thing different is that there are certain sanity checks to ensure the safe removal of the module from kernel without affecting the system stability. A module's unloading is initialized with the call to the rmmod utility, which calls the delete_module() routine, which steps into the underlying system call:

SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
                unsigned int, flags)
{
        struct module *mod;
        char name[MODULE_NAME_LEN];
        int ret, forced = 0;

        if (!capable(CAP_SYS_MODULE) || modules_disabled)
                return -EPERM;

        if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
                return -EFAULT;
        name[MODULE_NAME_LEN-1] = '';

        audit_log_kern_module(name);

        if (mutex_lock_interruptible(&module_mutex) != 0)
                return -EINTR;

        mod = find_module(name);
        if (!mod) {
                ret = -ENOENT;
                goto out;
        }

        if (!list_empty(&mod->source_list)) {
                /* Other modules depend on us: get rid of them first. */
                ret = -EWOULDBLOCK;
                goto out;
        }

        /* Doing init or already dying? */
        if (mod->state != MODULE_STATE_LIVE) {
                /* FIXME: if (force), slam module count damn the torpedoes */
                pr_debug("%s already dying
", mod->name);
                ret = -EBUSY;
                goto out;
        }

        /* If it has an init func, it must have an exit func to unload */
        if (mod->init && !mod->exit) {
                forced = try_force_unload(flags);
                if (!forced) {
                        /* This module can't be removed */
                        ret = -EBUSY;
                        goto out;
                }
        }

        /* Stop the machine so refcounts can't move and disable module. */
        ret = try_stop_module(mod, flags, &forced);
        if (ret != 0)
                goto out;

        mutex_unlock(&module_mutex);
        /* Final destruction now no one is using it. */
        if (mod->exit != NULL)
                mod->exit();
        blocking_notifier_call_chain(&module_notify_list,
                                     MODULE_STATE_GOING, mod);
        klp_module_going(mod);
        ftrace_release_mod(mod);

        async_synchronize_full();

        /* Store the name of the last unloaded module for diagnostic purposes */
        strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));

        free_module(mod);
        return 0;
out:
        mutex_unlock(&module_mutex);
        return ret;
}

On invocation, the system call checks whether the caller has the requisite permissions, then it checks for any module dependencies. If there are none, the module is good to be removed (else, an error is returned). After this, the module state is verified (live). Finally, the exit routine of the module is called and at last the free_module() routine is called:

/* Free a module, remove from lists, etc. */
static void free_module(struct module *mod)
{
        trace_module_free(mod);

        mod_sysfs_teardown(mod);

        /* We leave it in list to prevent duplicate loads, but make sure
        * that no one uses it while it's being deconstructed. */
        mutex_lock(&module_mutex);
        mod->state = MODULE_STATE_UNFORMED;
        mutex_unlock(&module_mutex);

        /* Remove dynamic debug info */
        ddebug_remove_module(mod->name);

        /* Arch-specific cleanup. */
        module_arch_cleanup(mod);

        /* Module unload stuff */
        module_unload_free(mod);

        /* Free any allocated parameters. */
        destroy_params(mod->kp, mod->num_kp);

        if (is_livepatch_module(mod))
                free_module_elf(mod);

        /* Now we can delete it from the lists */
        mutex_lock(&module_mutex);
        /* Unlink carefully: kallsyms could be walking list. */
        list_del_rcu(&mod->list);
        mod_tree_remove(mod);
        /* Remove this module from bug list, this uses list_del_rcu */
        module_bug_cleanup(mod);
        /* Wait for RCU-sched synchronizing before releasing mod->list and buglist. */
        synchronize_sched();
        mutex_unlock(&module_mutex);

        /* This may be empty, but that's OK */
        disable_ro_nx(&mod->init_layout);
        module_arch_freeing_init(mod);
        module_memfree(mod->init_layout.base);
        kfree(mod->args);
        percpu_modfree(mod);

        /* Free lock-classes; relies on the preceding sync_rcu(). */
        lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size);

        /* Finally, free the core (containing the module structure) */
        disable_ro_nx(&mod->core_layout);
        module_memfree(mod->core_layout.base);

#ifdef CONFIG_MPU
        update_protections(current->mm);
#endif
}

This call removes the module from the various lists where it was placed during loading (sysfs, module list, and so on) to initiate the cleanup. An architecture-specific cleanup routine is invoked (can be found in </linux/arch/<arch>/kernel/module.c>). All dependent modules are iterated and the module is removed from their lists. As soon as the cleanup is over, all resources and the memory that was allocated to the module are freed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.143.181