Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protecting shared memory segment between kernel and user space

Tags:

c

linux-kernel

I have shared memory segment created in kernel using mmap. I need to access this mapped memory from both kernel and user space. What mechanism should I use to protect the memory from concurrent access ? I want to have something like:

Kernel module:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/mm.h> 

#ifndef VM_RESERVED
# define  VM_RESERVED   (VM_DONTEXPAND | VM_DONTDUMP)
#endif

struct dentry  *file;

struct mmap_info
{
    char *data;            
    int reference;      
};

void mmap_open(struct vm_area_struct *vma)
{
    struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
    info->reference++;
}

void mmap_close(struct vm_area_struct *vma)
{
    struct mmap_info *info = (struct mmap_info *)vma->vm_private_data;
    info->reference--;
}

static int mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
    struct page *page;
    struct mmap_info *info;    

    info = (struct mmap_info *)vma->vm_private_data;
    if (!info->data)
    {
        printk("No data\n");
        return 0;    
    }
    page = virt_to_page(info->data);    
    get_page(page);
    vmf->page = page;            
    return 0;
}

struct vm_operations_struct mmap_vm_ops =
{
    .open =     mmap_open,
    .close =    mmap_close,
    .fault =    mmap_fault,    
};

int op_mmap(struct file *filp, struct vm_area_struct *vma)
{
    vma->vm_ops = &mmap_vm_ops;
    vma->vm_flags |= VM_RESERVED;    
    vma->vm_private_data = filp->private_data;
    mmap_open(vma);
    return 0;
}

int mmapfop_close(struct inode *inode, struct file *filp)
{
    struct mmap_info *info = filp->private_data;
    free_page((unsigned long)info->data);
    kfree(info);
    filp->private_data = NULL;
    return 0;
}

int mmapfop_open(struct inode *inode, struct file *filp)
{
    struct mmap_info *info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);    
    info->data = (char *)get_zeroed_page(GFP_KERNEL);
    memcpy(info->data, "hello from kernel this is file: ", 32);
    memcpy(info->data + 32, filp->f_dentry->d_name.name, strlen(filp->f_dentry->d_name.name));
    /* assign this info struct to the file */
    filp->private_data = info;
    return 0;
}

static const struct file_operations mmap_fops = {
    .open = mmapfop_open,
    .release = mmapfop_close,
    .mmap = op_mmap,
};

 static int __init mmapexample_module_init(void)
{
    file = debugfs_create_file("mmap_example", 0644, NULL, NULL, &mmap_fops);
    return 0;
}

static void __exit mmapexample_module_exit(void)
{
    debugfs_remove(file);
}

module_init(mmapexample_module_init);
module_exit(mmapexample_module_exit);
MODULE_LICENSE("GPL");

User space:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>

#define PAGE_SIZE     4096

int main ( int argc, char **argv )
{
    int configfd;
    char * address = NULL;

    configfd = open("/sys/kernel/debug/mmap_example", O_RDWR);
    if(configfd < 0)
    {
        perror("Open call failed");
        return -1;
    }

    address = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, configfd, 0);
    if (address == MAP_FAILED)
    {
        perror("mmap operation failed");
        return -1;
    }
    printf("Initial message: %s\n", address);
    memcpy(address + 11 , "*user*", 6);
    printf("Changed message: %s\n", address);
    close(configfd);    
    return 0;
}

but with locks.

like image 858
Marek Waszkiewicz Avatar asked Dec 13 '25 09:12

Marek Waszkiewicz


1 Answers

Kernel space and user space have no shared mechanisms for concurrent access protection. If you want them, you need to implement them by yourself.

It can be some sort of mutex, implemented within you kernel module, and accessed from user space via special ioctl requests:

Kernel:

DECLARE_WAIT_QUEUE_HEAD(wq);
int my_mutex_val = 0;

/* 
 * Lock mutex.
 *
 * May be used directly by the kernel or via 'ioctl(MY_CMD_LOCK)' by user.
 */
void my_mutex_lock(void)
{
    spin_lock(&wq.lock);
    wait_event_interruptible_locked(&wq, my_mutex_val == 0);
    my_mutex_val = 1;
    spin_unlock(&wq.lock);
}

/* 
 * Unlock mutex.
 *
 * May be used directly by the kernel or via 'ioctl(MY_CMD_UNLOCK)' by user.
 */
void my_mutex_unlock(void)
{
    spin_lock(&wq.lock);
    my_mutex_val = 0;
    wake_up(&wq);
    spin_unlock(&wq.lock);
}

long unlocked_ioctl (struct file * filp, unsigned int cmd, unsigned long val)
{
    switch(cmd) {
    case MY_CMD_LOCK:
        my_mutex_lock();
    break;
    case MY_CMD_UNLOCK:
        my_mutex_unlock();
    break;
    }
}

User:

int main()
{
    ...
    ioctl(MY_CMD_LOCK);
    <read data>
    ioctl(MY_CMD_UNLOCK);
    ...
}

It can be some sort of spinlock, which value is stored in mmap-ed area (so visible both for kernel space and user space).

In any case, kernel module should be prepared for the case, when user space application doesn't follow locking conventions. This, probably, would cancel any expectation about mmap-ed area content, generated by the kernel, but kernel module shouldn't crash in that case. [This is why standard kernel's struct mutex is not used in the code above: user space may use it incorrectly].

like image 193
Tsyvarev Avatar answered Dec 15 '25 04:12

Tsyvarev