FUN WITH LINUX

Kernel-Programming: execute call_usermodehelper() within a systemcall

5 May 2017

In kernel-programming we should avoid doing call_usermodehelper() which allows to execute a command from kernel-space. And sometimes we even want to call this function within a systemcall. Normally, we really don’t wanna do this. But, desperate times require extraordinary methods.

When I first tried to execute call_usermodehelper() within a systemcall() I got a kernel failure. So I googled and what I found was:

Are you calling call_usermodehelper() from within an interrupt handler ?

I believe call_usermodehelper() must be called from a context that can
wait.

Seems like I need a context that can wait. So I created a worker_queue and inside the systemcall I just schedule a worker:

struct work_cont {
                struct work_struct real_work;
                        char cmd[MAX_STRING_LEN];
};

struct work_cont *execwq;

void cmdexec_worker(struct work_struct *work)
{
        struct work_cont *c_ptr = container_of(work, struct work_cont, real_work);
        set_current_state(TASK_INTERRUPTIBLE);

        char *argv[] = { "/bin/sh", "-c", c_ptr->cmd, NULL };
        static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };

        call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC);

        return;
}

DEFINE_MUTEX(cmd_mutex);

/*
   Please note that this code is just an incomplete example to give
   an idea how to call call_usermodehelper from a systemcall. You have
   to include/implement my_own_systemcall() by yourself
 */
asmlinkage long my_own_systemcall(const char __user *filename, const char__ user *const __user *argv, const char__ user *const __user *envp)
{
        mutex_lock(cmd_mutex);
        strncpy(execwq->cmd,argv[1],MAX_STRING_LEN);
        mutex_unlock(cmd_mutex);

        schedule_work(&execwq->real_work);
}

int __init loadlkm(void)
{
        execwq = kmalloc(sizeof(*execwq),GFP_KERNEL);
        INIT_WORK(&execwq->real_work, cmdexec_worker);

        return 0;
}

void __exit clean_up(void)
{
        flush_work(&execwq->real_work);
        kfree(execwq);
}
[ Programming  C  Kernel  ]
Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution 3.0 Unported License.

Copyright 2015-present Hoti