cipcmessage-queuesysvsysv-ipc

Is there a way to increase the maximum amount of messages that can be contained in a SysV message queue?


I just switched from POSIX to SysV because the limit is much higher on SysV (1024 vs 10). But I still need an higher limit. The limit should be changed at runtime, because it depends on the data the user chooses.

Using POSIX, it was possible to increase the limit but it was necessary to run the code as root every time and I cannot do that.

Is there a way to increase the limit in SysV?


Solution

  • As SYSV IPC are considered deprecated, it is a pity to design brand new applications with thoses old fashioned services.

    POSIX message queues are based on a file system. It is usually mounted on /dev/mqueue:

    $ ls -la /dev/mqueue
    total 0
    drwxrwxrwt  2 root root   40 dec.  25 21:02 .
    drwxr-xr-x 20 root root 4600 dec.  25 20:51 ..
    

    A message queue is created with mq_open(). The attr parameter provides the ability to set some attributes:

    struct mq_attr {
      long mq_flags;       /* Flags (ignored for mq_open()) */
      long mq_maxmsg;      /* Max. # of messages on queue */
      long mq_msgsize;     /* Max. message size (bytes) */
      long mq_curmsgs;     /* # of messages currently in queue (ignored for mq_open()) */
    };
    

    According to the documentation, the /proc/sys/fs/mqueue/msg_max file defines the ceiling value for the maximum number of messages in a queue. In Linux 5.4, its default value is DFLT_MSGMAX (10) and its upper limit is HARD_MSGMAX (65536).

    The defaults values are defined in the Linux source code (cf. /include/linux/ipc_namespace.h):

    #define DFLT_QUEUESMAX      256
    #define MIN_MSGMAX          1
    #define DFLT_MSG            10U
    #define DFLT_MSGMAX         10
    #define HARD_MSGMAX         65536
    #define MIN_MSGSIZEMAX      128
    #define DFLT_MSGSIZE        8192U
    #define DFLT_MSGSIZEMAX     8192
    #define HARD_MSGSIZEMAX     (16*1024*1024)
    

    Here is an example program which creates a message queue. It receives as parameters the message queue name and the maximum number of messages in the queue:

    #include <errno.h>
    #include <pthread.h>
    #include <mqueue.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
      mqd_t mqdes;
      struct mq_attr attr;
    
      if (argc != 3) {
        fprintf(stderr, "Usage: %s <mq-name> <msg_max>\n", argv[0]);
        exit(EXIT_FAILURE);
      }
    
      attr.mq_flags = 0;
      attr.mq_maxmsg = atoi(argv[2]);
      attr.mq_msgsize = 2048;
      attr.mq_curmsgs = 0;
    
      mqdes = mq_open(argv[1], O_CREAT | O_RDWR, 0777, &attr);
      if (mqdes == (mqd_t) -1) {
        perror("mq_open");
        return 1;
      }
    
      return 0;
    }
    

    At execution time we can verify that, by default, we can't go above 10 messages for the queue:

    $ gcc mq.c -o mq -lrt
    $ ./mq 
    Usage: ./mq <mq-name> <msg_max>
    $ ./mq /q0 5
    $ ls -la /dev/mqueue/
    total 0
    drwxrwxrwt  2 root   root     60 dec.  25 21:09 .
    drwxr-xr-x 20 root   root   4600 dec.  25 20:51 ..
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:09 q0
    $ ./mq /q1 9
    $ ./mq /q2 10
    $ ./mq /q3 11
    mq_open: Invalid argument
    $ ls -la /dev/mqueue/
    total 0
    drwxrwxrwt  2 root   root    100 dec.  25 21:10 .
    drwxr-xr-x 20 root   root   4600 dec.  25 20:51 ..
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:09 q0
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q1
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q2
    

    Let's change the ceiling value from 10 to 256 for msg_max:

    $ cat /proc/sys/fs/mqueue/msg_max
    10
    $ sudo sh -c "echo 256 > /proc/sys/fs/mqueue/msg_max"
    $ cat /proc/sys/fs/mqueue/msg_max
    256
    

    Now it is possible to create message queues with up to 256 messages:

    $ ./mq /q3 11
    $ ./mq /q4 256
    $ ./mq /q5 257
    mq_open: Invalid argument
    $ ls -la /dev/mqueue/
    total 0
    drwxrwxrwt  2 root   root    140 dec.  25 21:16 .
    drwxr-xr-x 20 root   root   4600 dec.  25 20:51 ..
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:09 q0
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q1
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:10 q2
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:15 q3
    -rwxrwxr-x  1 xxxx   xxxx     80 dec.  25 21:16 q4
    

    But as you say, increasing the ceiling value requires super user rights. It could be possible to create a "setuid helper" which increases the ceiling value. For example, the following program sets the ceiling value passed as parameter:

    #include <errno.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
    int fd;
    int rc;
    int msg_max;
    
      if (argc != 2) {
        fprintf(stderr, "Usage: %s <msg_max>\n", argv[0]);
        return 1;
      }
    
      fd = open("/proc/sys/fs/mqueue/msg_max", O_RDWR);
      if (fd < 0) {
        perror("open()");
        return 1;
      }
    
      rc = write(fd, argv[1], strlen(argv[1]));
      if (rc != strlen(argv[1])) {
        if (rc >= 0) {
          errno = EIO;
        }
        perror("write()");
        return 1;
      }
    
      close(fd);
    
      return 0;
    }
    

    We can build it, change its owner/group to root and add the setuid bit:

    $ gcc mq_helper.c -o mq_helper
    $ sudo chown root mq_helper
    $ sudo chgrp root mq_helper
    $ sudo chmod 4555 mq_helper
    $ ls -l mq_helper
    -r-sr-xr-x 1 root root 17016 dec.  25 21:45 mq_helper
    

    Then, it is possible to run this program from a non super user account to change the ceiling value of msg_max:

    $ cat /proc/sys/fs/mqueue/msg_max
    256
    $ ./mq_helper 98
    $ cat /proc/sys/fs/mqueue/msg_max
    98
    

    Note: some file systems are mounted with the nosuid option to prevent users from introducing privileged programs.