共享内存忽略Linux c中的只读标志

时间:2017-07-03 06:17:55

标签: c linux shared-memory

我使用共享内存shmgetshmat用于教育目的 我试图让内存块只有它的创建者可变,而所有其他进程只能读取。
但是读者进程可以以某种方式编写而没有任何错误。

这是我的共享内存创建者的代码:

#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;
}

这是我的读者代码:

#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;
}

正如您所看到的那样,读者可以编辑内存,但即使我在阅读器中以只读方式打开内存并在共享内存的创建者中使用只读标志创建内存,也不会发生错误。

1 个答案:

答案 0 :(得分:1)

我还没有看到任何O_RDONLYSHM_RDONLY被记录为linux或freebsd手册页中shmat(2)系统调用的标志。问题可能是误用或误解了它的运作方式。最后有关详细信息,在尝试之后,我发现SHM_RDONLY是您应该用来控制只读附件的标志,而不是O_RDONLY(这里没用)

可能您必须在创建shmget(2)系统调用中指定权限位以禁用其他用户进程的访问权限,以实现您想要的功能。使用权限,它确实有效,或者您使用共享内存的系统存在严重的安全问题(例如,postgresql数据库使用sysvipc共享内存段)

据我所知,实现的最佳方法是将共享内存段的编写器作为某个用户运行,并允许进程将其作为不同的用户读取,调整权限位以允许它们读取但不写入在共享内存段上。类似于拥有相同组ID的所有进程,其中编写进程作为创建共享内存段的用户而其他进程只具有读访问权限,对其他用户ID没有权限,对于任何应用程序都足够了。

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

并将其他进程作为同一组ID中的其他不同用户运行。

修改

在freebsd机器上测试你的代码后(抱歉,没有linux可用,但是ipc调用是SysV AT&amp; T unix调用,所以一切都应该是兼容的)创建过程在shmat(2)调用时因错误而停止以下消息:

$ shm_creator
Err:: Permission denied

很可能是因为你没有给共享内存创建权限,甚至给予所有者(我试图想象你的机器中没有root开发,是吗?;))< / p>

ipcs(1)显示:

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

并且您看到共享内存段没有活动的权限位,但它已创建。我已经修改了你的程序,而不是在while(1);循环中执行busywait,而是使用sleep(3600);进行非消耗的cpu等待,这将使它睡眠整整一个小时。

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;
}

我以用户usr1

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

然后我切换到另一个用户usr2,然后运行:

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

并且在标记时,它发生在shmat(2)系统调用中。但如果我以usr1运行它,我会得到:

usr1@host:~/shm$ shm_client 
b

如果在SHM_RDONLY源文件中使用shm_client.c作为标记,则在运行时(作为相同或不同的用户),我得到以下内容:

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

这是预期的行为,因为你试图写入不可写的内存(它被附加为只读内存)

编辑2

在线浏览linux手册页后,有SHM_RDONLY的引用,允许以只读方式附加共享内存段。否则,不为只写共享内存段提供支持。由于freebsd上没有记录,因此该选项也可用(常量包含在正确的包含文件中),并且在freebsd手册中可以找到其他一些不精确的内容(使用S_IROWN,{{1} },S_IWOWNS_IRGRPS_IWGRPS_IROTH标志来控制权限位,并且在手册页的概要中不包含S_IWOTH

CONCLUSSION

如果您的系统中有#include <sys/stat.h>可用,那么您可以将其用作禁止对共享内存进行写访问的非抢占方式,但如果您想要强制执行内核,则必须切换到用户权限位方法。