Dear andreas,

I figured out why CAP_SYS_MODULE is removed in my environment.
My /sbin/modprobe uses system(3) to create a sub process internally(*1),
and drops the capability.
According to man page (*2), I think this is expected behavior.
*1  kmod-25/libkmod/libkmod-module.c:996

I wrote small program to reproduce this issue.
Could you check and try the attached code?
The step is:
$ gcc check.c -lcap -omain
$ cp main sub # "sub" corresponds to insmod that causes EPERM
$ sudo chown 0.0 main
$ sudo chmod u+s main    # "main" corresponds to nvidia-modprobe
$ ./main

This is what I get:
./main  euid: 0                 # root privilege
CAP_SYS_MODULE: 1    # has capability
./sub  euid: 1000            # lost root privilege (1000 is my uid)
CAP_SYS_MODULE: 0    # the cap. is removed.

Strictly, I use dash as /bin/sh
but CAP_SYS_MODULE is dropped when system() is used.

Could you let me know the result on your system?


On 2018年02月06日 21:13, Hiromasa YOSHIMOTO wrote:
Dear andreas,

Thank you for your information. I've just checked CAP_SYS_MODULE capability.

First, EPERM is returned by finit_module syscall. It is defined
in linux-4.14/kernel/module.c and checks CAP_SYS_MODULE as below;
static int may_init_module(void)
         if (!capable(CAP_SYS_MODULE) || modules_disabled)
                 return -EPERM;
         return 0;
So, I think the required capability is CAP_SYS_MODULE only.

Next, I checked when and where CAP_SYS_MODULE is lost.
I added the code below to both nvidia-modprobe and /sbin/insmod (/bin/kmod),
which is used called by nvidia-modprobe internally.

       cap_t cap = cap_get_proc();
       cap_get_flag(cap, CAP_SYS_MODULE, CAP_SET, &value);
       fprintf(stderr, "CAP_SYS_MODULE: %d\n", (CAP_SET == value));

 From my observations,  setuid nvidia-modprobe has CAP_SYS_MODULE already;
additional capability was not needed for nvidia-modprobe.

However, the capability is lost in /bin/kmod.
I'm trying some tricks below, but still struggling.
- /sbin/setcap cap_sys_module+eip nvidia-modprobe
- Added prctl(PR_SET_KEEPCAPS,1) to nvidia-modprobe

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/capability.h>
#include <stdlib.h>

static void
    cap_t cap = cap_get_proc();
    cap_flag_value_t value;
    cap_get_flag(cap, CAP_SYS_MODULE, CAP_SET, &value);
    printf("CAP_SYS_MODULE: %d\n", (CAP_SET == value));

main(int argc, char *argv[])
    printf("%s  euid: %d\n", argv[0], geteuid());
    if (argc==1) {
	pid_t pid = fork();
	switch (pid) {
	case 0:
	    //execlp("./sub", "./sub", "arg1", NULL);
	    system("./sub 1");
	case -1:
	    waitpid(pid, NULL, 0);
    return 0;

Reply via email to