0

I am writing a char device that takes as input with ioctl a function pointer and a buffer pointer.
I want to modify the user machine context so that back in user mode, that function is executed with a new stack pointed by that buffer pointer.

What I have done is the following :

    long ioctl_funcs(struct file *filp,unsigned int cmd, unsigned long arg)
    {
        int ret = 0;
        switch(cmd) {
        case IOCTL_SET_FUN:

            printk(KERN_INFO "start\n");
            struct myarg* a;
            a = (struct myarg*) arg;
            struct pt_regs* regs = task_pt_regs(current);
            regs->ip = a->func;// func is a function implemented in user space
            regs->sp = a->stack;// stack is the buffer allocated in user space with malloc
            break;
         }
       return ret;
      }

The good news is that the function is activated, the bad one is that the stack is the same (I have used gdb to test it).
In particular even if : regs->sp = 0; the new function is executed when it should crash since it should have no stack.
It seems the assignment of the stack pointer in this way is ineffective.

Why? How should I correctly assign the stack pointer?

The linux kernel version is : 3.18.106 and it is executed on Virtual Box.

Angelo
  • 334
  • 4
  • 14
  • 1
    And if you achieve what you describe, then what is supposed to happen when the called function returns? – John Bollinger Sep 06 '18 at 17:40
  • if the stack pointer is correctly assigned with that buffer, when the function:funct is activated and returns the program crashes but it is ok – Angelo Sep 06 '18 at 17:44
  • What you describe sounds a lot like the effect of delivering a signal to a process with the specified function registered as its handler and the `SA_ONSTACK` flag set, except that signal handling provides defined behavior when the handler exits. Perhaps you can raise a signal instead, or at least look at the kernel's signal-handling implementation for guidance. – John Bollinger Sep 06 '18 at 17:48
  • I have looked do_fork implementation and it calls copy_thread and it assigns the stack pointer in this way. (https://elixir.bootlin.com/linux/v3.18.106/source/arch/x86/kernel/process_64.c#L192). Anyway thank you for the advice I will look for the signal handling implementation. – Angelo Sep 06 '18 at 17:58
  • 1
    You may want `regs->usersp` instead of `regs->sp`. But, be careful, as this still may not produce the desired effect. You'd have to ensure that the stack is already set up correctly with regard to args, return address, etc. You may want to examine the prolog/epilog code for `ioctl` (i.e. syscalls) in the `arch` subdir (e.g. `entry.s`, etc.) – Craig Estey Sep 06 '18 at 18:22
  • Struct ptregs doesn t have usersp, current ->thread.usersp does but it has no effect. But why it does not work if a syscall returns to user space by restoring the previously stored user stack pointer ? The user stack pointer must be somewhere is the kernel stack and why it should not be the sp in sruct ptregs? Anyway thank you I will look ioctl code – Angelo Sep 06 '18 at 18:46
  • 1
    It's more involved than that (e.g. `arch/x86/entry/entry_64.S`). Since the thread has to issue the `ioctl` (i.e. it is _not_ asynchronous), you don't need to go to the kernel [unless this is an exercise]. You can just do a function in userspace that switches the stack pointer and invokes the function (e.g. switching the stack pointer is a two line inline asm block). Also, consider using `getcontext` and then `setcontext` [et. al.] from userspace. So, the bigger question is what are you trying to do (e.g. implement coroutines, cooperative threads (aka fibers))? – Craig Estey Sep 06 '18 at 19:04
  • I need to implement coroutines with kernel support so get context and similar are not allowed . To modify the sp with inline asm is required to be in kernel mode or not ? – Angelo Sep 06 '18 at 19:10
  • Could you tell me/give me some suggestion on how to do it with asm ? – Angelo Sep 06 '18 at 19:25
  • Doing it from the kernel, you may [already] be close (i.e. no two line asm block). Just a guess: Consider forcing the "slow path" syscall return by issuing `force_iret()`. You may wish to have userspace do a `printf` of `__builtin_stack_pointer()` and have the kernel do a `printk` of `regs->sp` _before_ you change it. You could add a dummy field to your struct that holds the `__builtin_stack_pointer` value and have `printk` print both. They [probably] won't match exactly, but they should be close. The target function could also `printf` of the stack pointer (vs. using `gdb`) – Craig Estey Sep 06 '18 at 20:40
  • I don t know if I have correctly understood but if the suggestion is to force the "slow path" I have seen that `force_iret()=set_thread_flag(TIF_NOTIFY_RESUME)` . But if I add `set_thread_flag(TIF_NOTIFY_RESUME)` after setting the sp inside ioctl nothing changes.Thank you for the second suggestion but I already use something similar for printing the stack address – Angelo Sep 06 '18 at 22:06
  • I ve solved by switching to kernel 4.14.68. However in this case a segfault is generated also when a correct buffer is assigned to the sp unless aslr is disabled – Angelo Sep 07 '18 at 23:45

0 Answers0