Is this a bug in the linux kernel regarding writing to / proc / self / loginuid?

There is a chance that I found an error in the linux kernel. Consider an application that writes to / proc / self / loginuid from the main stream and one auxiliary stream. Code below:

#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void write_loginuid(char *str)
{
    int fd;

    printf("%s\n", str);

    fd = open("/proc/self/loginuid", O_RDWR);

    if (fd < 0) {
        perror(str);
        return;
    }

    if (write(fd, "0", 2) != 2) {
        printf("write\n");
        perror(str);
    }

    close(fd);
}

void *thread_function(void *arg)
{
    fprintf(stderr, "Hello from thread! my pid = %u, tid = %u, parent pid = %u\n", getpid(), syscall(SYS_gettid), getppid());

    write_loginuid("thread");

    return NULL;
}

int main()
{
    pthread_t thread;

    pthread_create(&thread, NULL, thread_function, NULL);

    write_loginuid("main process");

    fprintf(stderr, "test my pid = %u, tid = %u, parent pid = %u\n", getpid(), syscall(SYS_gettid), getppid());

    pthread_join(thread, NULL);
    return 0;
}

After executing this application we will get:

main process
test my pid = 3487, tid = 3487, parent pid = 3283
Hello from thread! my pid = 3487, tid = 3488, parent pid = 3283
thread
write
thread: Operation not permitted

This indicates that the thread was written in error using -EPERM.

Looking at the kernel file fs / proc / base.c and the proc_loginuid_write () function, we see a check at the beginning:

static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
                   size_t count, loff_t *ppos)
{
    struct inode * inode = file_inode(file);
    uid_t loginuid;
    kuid_t kloginuid;
    int rv;

    /* this is the probably buggy check */
    rcu_read_lock();
    if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) {
        rcu_read_unlock();
        return -EPERM;
    }
    rcu_read_unlock();

So, looking at the code above, we see that only for the exact PID (checked by me using printks) we go through. Labor does not satisfy the condition because the comparison of pids is different.

, : ? loginuid? , PAM.

+4
1

, , :

rcu_read_lock();
/*
 * I changed the condition that it checks now the tgid as returned in sys_getpid()
 * rather than task_struct pointers
 */
if (task_tgid_vnr(current) != task_tgid_vnr(pid_task(proc_pid(inode), PIDTYPE_PID))) {
    rcu_read_unlock();
    return -EPERM;
}
rcu_read_unlock();

? ?

+1

Source: https://habr.com/ru/post/1668370/


All Articles