Shared memory ignores read-only flag in Linux c

I use shared memory with shmget

and shmat

for educational purposes.
I am trying to make a chunk of memory mutable only by this creator and all other processes can only read.
But the reading processes can somehow write without errors.

This is my code for the shared memory creator:

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

int main(){
    int shmid = shmget((key_t)56666, 1, IPC_CREAT | O_RDONLY);
     if (shmid ==-1) {
        perror("Err0:");
        exit(EXIT_FAILURE);
    }
    void* shmaddr = shmat(shmid, (void *)0,0);
    if (shmaddr == (void *)-1) {
        perror("Err:");
        exit(EXIT_FAILURE);
    }
    *(char*)shmaddr = 'a'; 
    putchar(*(char*)shmaddr);
    while(1);
    return 0;
}

      

And this is my code for the reader:

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int main(){
    int shmid = shmget((key_t)56666, 4, O_RDONLY);
     if (shmid ==-1) {
        perror("Err0:");
        exit(EXIT_FAILURE);
    }
    void* shmaddr = shmat(shmid, (void *)0,0);
    if (shmaddr == (void *)-1) {
        perror("Err:");
        exit(EXIT_FAILURE);
    }
    *(char*)shmaddr = 'b'; 
    putchar(*(char*)shmaddr);
    return 0;
}

      

As you can see, the reader is able to edit the memory, but the error does not occur even though I open the memory as read only in the reader and create it with a single read-only flag in the shared memory creator.

+3


source to share


1 answer


I have not seen any of the O_RDONLY

or SHM_RDONLY

documented as flags for the system call shmat(2)

in the linux or freebsd man pages. The problem is likely a misuse or misunderstanding of how it works. More on this at the end, as after trying I see that SHM_RDONLY

- this is the flag you should use to control read-only attachment, instead of O_RDONLY

(which is useless here)

You probably need to specify the permission bits in the create system call shmget(2)

to disable access for other user processes in order to implement what you want. With permissions, it works, or you run into serious security issues on systems using shared memory (for example, the postgresql database uses sysvipc shared memory segments)

As far as I know, the best way to implement is to run the script of the shared memory segment as some user and the processes allowed it to be read as different users by setting the permission bits so that they can read but not write on the shared memory segment. Something like that all processes in the same group id with the write process as the user creating the shared memory segment and others having read-only access without rights to other user IDs would be sufficient for any application.

shmget((key_t)56666, 1, IPC_CREAT | 0640);

      

and start other processes as a different user on the same group ID.

EDIT

after testing your code on freebsd machine (sorry linux is not available, but ipc calls are AT&T unix SysV calls, so everything should be compatible) the build process stops on error when called shmat(2)

with the following message

$ shm_creator
Err:: Permission denied

      

most likely because you didnโ€™t give permission to create shared memory even to the owner (and Iโ€™m trying to imagine that you donโ€™t develop like root

in your machine, do you?;))

ipcs(1)

shows:

usr1@host ~$ ipcs -m
Shared Memory:
T           ID          KEY MODE        OWNER    GROUP   
m        65537        56666 ----------- usr1     usr1    

      

and you see there are no active permission bits for the shared memory segment, but it was created. I changed your program, instead of doing busywait in a loop while(1);

, doing the waiting processor with a help sleep(3600);

that will make it sleep for an hour.

shm_creator.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

int main(){
    int shmid = shmget((key_t)56666, 1, IPC_CREAT | 0640 );
     if (shmid ==-1) {
        perror("Err0:");
        exit(EXIT_FAILURE);
    }
    void* shmaddr = shmat(shmid, (void *)0,0);
    if (shmaddr == (void *)-1) {
        perror("Err:");
        exit(EXIT_FAILURE);
    }
    *(char*)shmaddr = 'a'; 
    putchar(*(char*)shmaddr);
    puts("");
    sleep(3600);
    return 0;
}

      



which I run as user usr1

:

usr1@host:~/shm$ shm_creator &
[2] 76950
a

      

then I will switch to another user usr2

and run:

$ su usr2
Password:
[usr2@host /home/usr1/shm]$ shm_client &
[1] 76963
[usr2@host /home/usr1/shm]$ Err:: Permission denied

      

and as you noted, it happens on a system call shmat(2)

. But if I run it like usr1

, I get:

usr1@host:~/shm$ shm_client 
b

      

if used SHM_RDONLY

as a flag in a source file shm_client.c

, when run (either as the same or a different user) I get this:

usr1@host:~/shm$ shm_client
Segmentation fault (generated `core')

      

which is the expected behavior as you were trying to write non-playable memory (it was attached as read-only memory)

EDIT 2

After browsing the online linux manual pages, please refer to SHM_RDONLY

to allow attach shared memory segment as read-only. Otherwise, support is not supported for write-only shared memory segments. Because it is not registered on the freebsd, this option is also available there (the constant is included in the corresponding include files), and some other discrepancies found in the freebsd guide (as the use of S_IROWN

, S_IWOWN

, S_IRGRP

, S_IWGRP

, S_IROTH

and S_IWOTH

to control bits permits and without the inclusion #include <sys/stat.h>

in the manual pages SYNOPSIS)

CONCLUSSION

If SHM_RDONLY

available on your system, you can use it as an unmanaged way to deny write access to your shared memory, but if you want the kernel to be enforced you need to switch to the user permission bit.

+1


source







All Articles