linux/tools/testing/selftests/mqueue/mq_open_tests.c
<<
>>
Prefs
   1/*
   2 * This application is Copyright 2012 Red Hat, Inc.
   3 *      Doug Ledford <dledford@redhat.com>
   4 *
   5 * mq_open_tests is free software: you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation, version 3.
   8 *
   9 * mq_open_tests is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * For the full text of the license, see <http://www.gnu.org/licenses/>.
  15 *
  16 * mq_open_tests.c
  17 *   Tests the various situations that should either succeed or fail to
  18 *   open a posix message queue and then reports whether or not they
  19 *   did as they were supposed to.
  20 *
  21 */
  22#include <stdio.h>
  23#include <stdlib.h>
  24#include <unistd.h>
  25#include <fcntl.h>
  26#include <string.h>
  27#include <limits.h>
  28#include <errno.h>
  29#include <sys/types.h>
  30#include <sys/time.h>
  31#include <sys/resource.h>
  32#include <sys/stat.h>
  33#include <mqueue.h>
  34#include <error.h>
  35
  36static char *usage =
  37"Usage:\n"
  38"  %s path\n"
  39"\n"
  40"       path    Path name of the message queue to create\n"
  41"\n"
  42"       Note: this program must be run as root in order to enable all tests\n"
  43"\n";
  44
  45char *DEF_MSGS = "/proc/sys/fs/mqueue/msg_default";
  46char *DEF_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_default";
  47char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
  48char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
  49
  50int default_settings;
  51struct rlimit saved_limits, cur_limits;
  52int saved_def_msgs, saved_def_msgsize, saved_max_msgs, saved_max_msgsize;
  53int cur_def_msgs, cur_def_msgsize, cur_max_msgs, cur_max_msgsize;
  54FILE *def_msgs, *def_msgsize, *max_msgs, *max_msgsize;
  55char *queue_path;
  56mqd_t queue = -1;
  57
  58static inline void __set(FILE *stream, int value, char *err_msg);
  59void shutdown(int exit_val, char *err_cause, int line_no);
  60static inline int get(FILE *stream);
  61static inline void set(FILE *stream, int value);
  62static inline void getr(int type, struct rlimit *rlim);
  63static inline void setr(int type, struct rlimit *rlim);
  64void validate_current_settings();
  65static inline void test_queue(struct mq_attr *attr, struct mq_attr *result);
  66static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result);
  67
  68static inline void __set(FILE *stream, int value, char *err_msg)
  69{
  70        rewind(stream);
  71        if (fprintf(stream, "%d", value) < 0)
  72                perror(err_msg);
  73}
  74
  75
  76void shutdown(int exit_val, char *err_cause, int line_no)
  77{
  78        static int in_shutdown = 0;
  79
  80        /* In case we get called recursively by a set() call below */
  81        if (in_shutdown++)
  82                return;
  83
  84        if (seteuid(0) == -1)
  85                perror("seteuid() failed");
  86
  87        if (queue != -1)
  88                if (mq_close(queue))
  89                        perror("mq_close() during shutdown");
  90        if (queue_path)
  91                /*
  92                 * Be silent if this fails, if we cleaned up already it's
  93                 * expected to fail
  94                 */
  95                mq_unlink(queue_path);
  96        if (default_settings) {
  97                if (saved_def_msgs)
  98                        __set(def_msgs, saved_def_msgs,
  99                              "failed to restore saved_def_msgs");
 100                if (saved_def_msgsize)
 101                        __set(def_msgsize, saved_def_msgsize,
 102                              "failed to restore saved_def_msgsize");
 103        }
 104        if (saved_max_msgs)
 105                __set(max_msgs, saved_max_msgs,
 106                      "failed to restore saved_max_msgs");
 107        if (saved_max_msgsize)
 108                __set(max_msgsize, saved_max_msgsize,
 109                      "failed to restore saved_max_msgsize");
 110        if (exit_val)
 111                error(exit_val, errno, "%s at %d", err_cause, line_no);
 112        exit(0);
 113}
 114
 115static inline int get(FILE *stream)
 116{
 117        int value;
 118        rewind(stream);
 119        if (fscanf(stream, "%d", &value) != 1)
 120                shutdown(4, "Error reading /proc entry", __LINE__ - 1);
 121        return value;
 122}
 123
 124static inline void set(FILE *stream, int value)
 125{
 126        int new_value;
 127
 128        rewind(stream);
 129        if (fprintf(stream, "%d", value) < 0)
 130                return shutdown(5, "Failed writing to /proc file",
 131                                __LINE__ - 1);
 132        new_value = get(stream);
 133        if (new_value != value)
 134                return shutdown(5, "We didn't get what we wrote to /proc back",
 135                                __LINE__ - 1);
 136}
 137
 138static inline void getr(int type, struct rlimit *rlim)
 139{
 140        if (getrlimit(type, rlim))
 141                shutdown(6, "getrlimit()", __LINE__ - 1);
 142}
 143
 144static inline void setr(int type, struct rlimit *rlim)
 145{
 146        if (setrlimit(type, rlim))
 147                shutdown(7, "setrlimit()", __LINE__ - 1);
 148}
 149
 150void validate_current_settings()
 151{
 152        int rlim_needed;
 153
 154        if (cur_limits.rlim_cur < 4096) {
 155                printf("Current rlimit value for POSIX message queue bytes is "
 156                       "unreasonably low,\nincreasing.\n\n");
 157                cur_limits.rlim_cur = 8192;
 158                cur_limits.rlim_max = 16384;
 159                setr(RLIMIT_MSGQUEUE, &cur_limits);
 160        }
 161
 162        if (default_settings) {
 163                rlim_needed = (cur_def_msgs + 1) * (cur_def_msgsize + 1 +
 164                                                    2 * sizeof(void *));
 165                if (rlim_needed > cur_limits.rlim_cur) {
 166                        printf("Temporarily lowering default queue parameters "
 167                               "to something that will work\n"
 168                               "with the current rlimit values.\n\n");
 169                        set(def_msgs, 10);
 170                        cur_def_msgs = 10;
 171                        set(def_msgsize, 128);
 172                        cur_def_msgsize = 128;
 173                }
 174        } else {
 175                rlim_needed = (cur_max_msgs + 1) * (cur_max_msgsize + 1 +
 176                                                    2 * sizeof(void *));
 177                if (rlim_needed > cur_limits.rlim_cur) {
 178                        printf("Temporarily lowering maximum queue parameters "
 179                               "to something that will work\n"
 180                               "with the current rlimit values in case this is "
 181                               "a kernel that ties the default\n"
 182                               "queue parameters to the maximum queue "
 183                               "parameters.\n\n");
 184                        set(max_msgs, 10);
 185                        cur_max_msgs = 10;
 186                        set(max_msgsize, 128);
 187                        cur_max_msgsize = 128;
 188                }
 189        }
 190}
 191
 192/*
 193 * test_queue - Test opening a queue, shutdown if we fail.  This should
 194 * only be called in situations that should never fail.  We clean up
 195 * after ourselves and return the queue attributes in *result.
 196 */
 197static inline void test_queue(struct mq_attr *attr, struct mq_attr *result)
 198{
 199        int flags = O_RDWR | O_EXCL | O_CREAT;
 200        int perms = DEFFILEMODE;
 201
 202        if ((queue = mq_open(queue_path, flags, perms, attr)) == -1)
 203                shutdown(1, "mq_open()", __LINE__);
 204        if (mq_getattr(queue, result))
 205                shutdown(1, "mq_getattr()", __LINE__);
 206        if (mq_close(queue))
 207                shutdown(1, "mq_close()", __LINE__);
 208        queue = -1;
 209        if (mq_unlink(queue_path))
 210                shutdown(1, "mq_unlink()", __LINE__);
 211}
 212
 213/*
 214 * Same as test_queue above, but failure is not fatal.
 215 * Returns:
 216 * 0 - Failed to create a queue
 217 * 1 - Created a queue, attributes in *result
 218 */
 219static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result)
 220{
 221        int flags = O_RDWR | O_EXCL | O_CREAT;
 222        int perms = DEFFILEMODE;
 223
 224        if ((queue = mq_open(queue_path, flags, perms, attr)) == -1)
 225                return 0;
 226        if (mq_getattr(queue, result))
 227                shutdown(1, "mq_getattr()", __LINE__);
 228        if (mq_close(queue))
 229                shutdown(1, "mq_close()", __LINE__);
 230        queue = -1;
 231        if (mq_unlink(queue_path))
 232                shutdown(1, "mq_unlink()", __LINE__);
 233        return 1;
 234}
 235
 236int main(int argc, char *argv[])
 237{
 238        struct mq_attr attr, result;
 239
 240        if (argc != 2) {
 241                fprintf(stderr, "Must pass a valid queue name\n\n");
 242                fprintf(stderr, usage, argv[0]);
 243                exit(1);
 244        }
 245
 246        /*
 247         * Although we can create a msg queue with a non-absolute path name,
 248         * unlink will fail.  So, if the name doesn't start with a /, add one
 249         * when we save it.
 250         */
 251        if (*argv[1] == '/')
 252                queue_path = strdup(argv[1]);
 253        else {
 254                queue_path = malloc(strlen(argv[1]) + 2);
 255                if (!queue_path) {
 256                        perror("malloc()");
 257                        exit(1);
 258                }
 259                queue_path[0] = '/';
 260                queue_path[1] = 0;
 261                strcat(queue_path, argv[1]);
 262        }
 263
 264        if (getuid() != 0) {
 265                fprintf(stderr, "Not running as root, but almost all tests "
 266                        "require root in order to modify\nsystem settings.  "
 267                        "Exiting.\n");
 268                exit(1);
 269        }
 270
 271        /* Find out what files there are for us to make tweaks in */
 272        def_msgs = fopen(DEF_MSGS, "r+");
 273        def_msgsize = fopen(DEF_MSGSIZE, "r+");
 274        max_msgs = fopen(MAX_MSGS, "r+");
 275        max_msgsize = fopen(MAX_MSGSIZE, "r+");
 276
 277        if (!max_msgs)
 278                shutdown(2, "Failed to open msg_max", __LINE__);
 279        if (!max_msgsize)
 280                shutdown(2, "Failed to open msgsize_max", __LINE__);
 281        if (def_msgs || def_msgsize)
 282                default_settings = 1;
 283
 284        /* Load up the current system values for everything we can */
 285        getr(RLIMIT_MSGQUEUE, &saved_limits);
 286        cur_limits = saved_limits;
 287        if (default_settings) {
 288                saved_def_msgs = cur_def_msgs = get(def_msgs);
 289                saved_def_msgsize = cur_def_msgsize = get(def_msgsize);
 290        }
 291        saved_max_msgs = cur_max_msgs = get(max_msgs);
 292        saved_max_msgsize = cur_max_msgsize = get(max_msgsize);
 293
 294        /* Tell the user our initial state */
 295        printf("\nInitial system state:\n");
 296        printf("\tUsing queue path:\t\t%s\n", queue_path);
 297        printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n",
 298                (long) saved_limits.rlim_cur);
 299        printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n",
 300                (long) saved_limits.rlim_max);
 301        printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize);
 302        printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs);
 303        if (default_settings) {
 304                printf("\tDefault Message Size:\t\t%d\n", saved_def_msgsize);
 305                printf("\tDefault Queue Size:\t\t%d\n", saved_def_msgs);
 306        } else {
 307                printf("\tDefault Message Size:\t\tNot Supported\n");
 308                printf("\tDefault Queue Size:\t\tNot Supported\n");
 309        }
 310        printf("\n");
 311
 312        validate_current_settings();
 313
 314        printf("Adjusted system state for testing:\n");
 315        printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n", (long) cur_limits.rlim_cur);
 316        printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n", (long) cur_limits.rlim_max);
 317        printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize);
 318        printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs);
 319        if (default_settings) {
 320                printf("\tDefault Message Size:\t\t%d\n", cur_def_msgsize);
 321                printf("\tDefault Queue Size:\t\t%d\n", cur_def_msgs);
 322        }
 323
 324        printf("\n\nTest series 1, behavior when no attr struct "
 325               "passed to mq_open:\n");
 326        if (!default_settings) {
 327                test_queue(NULL, &result);
 328                printf("Given sane system settings, mq_open without an attr "
 329                       "struct succeeds:\tPASS\n");
 330                if (result.mq_maxmsg != cur_max_msgs ||
 331                    result.mq_msgsize != cur_max_msgsize) {
 332                        printf("Kernel does not support setting the default "
 333                               "mq attributes,\nbut also doesn't tie the "
 334                               "defaults to the maximums:\t\t\tPASS\n");
 335                } else {
 336                        set(max_msgs, ++cur_max_msgs);
 337                        set(max_msgsize, ++cur_max_msgsize);
 338                        test_queue(NULL, &result);
 339                        if (result.mq_maxmsg == cur_max_msgs &&
 340                            result.mq_msgsize == cur_max_msgsize)
 341                                printf("Kernel does not support setting the "
 342                                       "default mq attributes and\n"
 343                                       "also ties system wide defaults to "
 344                                       "the system wide maximums:\t\t"
 345                                       "FAIL\n");
 346                        else
 347                                printf("Kernel does not support setting the "
 348                                       "default mq attributes,\n"
 349                                       "but also doesn't tie the defaults to "
 350                                       "the maximums:\t\t\tPASS\n");
 351                }
 352        } else {
 353                printf("Kernel supports setting defaults separately from "
 354                       "maximums:\t\tPASS\n");
 355                /*
 356                 * While we are here, go ahead and test that the kernel
 357                 * properly follows the default settings
 358                 */
 359                test_queue(NULL, &result);
 360                printf("Given sane values, mq_open without an attr struct "
 361                       "succeeds:\t\tPASS\n");
 362                if (result.mq_maxmsg != cur_def_msgs ||
 363                    result.mq_msgsize != cur_def_msgsize)
 364                        printf("Kernel supports setting defaults, but does "
 365                               "not actually honor them:\tFAIL\n\n");
 366                else {
 367                        set(def_msgs, ++cur_def_msgs);
 368                        set(def_msgsize, ++cur_def_msgsize);
 369                        /* In case max was the same as the default */
 370                        set(max_msgs, ++cur_max_msgs);
 371                        set(max_msgsize, ++cur_max_msgsize);
 372                        test_queue(NULL, &result);
 373                        if (result.mq_maxmsg != cur_def_msgs ||
 374                            result.mq_msgsize != cur_def_msgsize)
 375                                printf("Kernel supports setting defaults, but "
 376                                       "does not actually honor them:\t"
 377                                       "FAIL\n");
 378                        else
 379                                printf("Kernel properly honors default setting "
 380                                       "knobs:\t\t\t\tPASS\n");
 381                }
 382                set(def_msgs, cur_max_msgs + 1);
 383                cur_def_msgs = cur_max_msgs + 1;
 384                set(def_msgsize, cur_max_msgsize + 1);
 385                cur_def_msgsize = cur_max_msgsize + 1;
 386                if (cur_def_msgs * (cur_def_msgsize + 2 * sizeof(void *)) >=
 387                    cur_limits.rlim_cur) {
 388                        cur_limits.rlim_cur = (cur_def_msgs + 2) *
 389                                (cur_def_msgsize + 2 * sizeof(void *));
 390                        cur_limits.rlim_max = 2 * cur_limits.rlim_cur;
 391                        setr(RLIMIT_MSGQUEUE, &cur_limits);
 392                }
 393                if (test_queue_fail(NULL, &result)) {
 394                        if (result.mq_maxmsg == cur_max_msgs &&
 395                            result.mq_msgsize == cur_max_msgsize)
 396                                printf("Kernel properly limits default values "
 397                                       "to lesser of default/max:\t\tPASS\n");
 398                        else
 399                                printf("Kernel does not properly set default "
 400                                       "queue parameters when\ndefaults > "
 401                                       "max:\t\t\t\t\t\t\t\tFAIL\n");
 402                } else
 403                        printf("Kernel fails to open mq because defaults are "
 404                               "greater than maximums:\tFAIL\n");
 405                set(def_msgs, --cur_def_msgs);
 406                set(def_msgsize, --cur_def_msgsize);
 407                cur_limits.rlim_cur = cur_limits.rlim_max = cur_def_msgs *
 408                        cur_def_msgsize;
 409                setr(RLIMIT_MSGQUEUE, &cur_limits);
 410                if (test_queue_fail(NULL, &result))
 411                        printf("Kernel creates queue even though defaults "
 412                               "would exceed\nrlimit setting:"
 413                               "\t\t\t\t\t\t\t\tFAIL\n");
 414                else
 415                        printf("Kernel properly fails to create queue when "
 416                               "defaults would\nexceed rlimit:"
 417                               "\t\t\t\t\t\t\t\tPASS\n");
 418        }
 419
 420        /*
 421         * Test #2 - open with an attr struct that exceeds rlimit
 422         */
 423        printf("\n\nTest series 2, behavior when attr struct is "
 424               "passed to mq_open:\n");
 425        cur_max_msgs = 32;
 426        cur_max_msgsize = cur_limits.rlim_max >> 4;
 427        set(max_msgs, cur_max_msgs);
 428        set(max_msgsize, cur_max_msgsize);
 429        attr.mq_maxmsg = cur_max_msgs;
 430        attr.mq_msgsize = cur_max_msgsize;
 431        if (test_queue_fail(&attr, &result))
 432                printf("Queue open in excess of rlimit max when euid = 0 "
 433                       "succeeded:\t\tFAIL\n");
 434        else
 435                printf("Queue open in excess of rlimit max when euid = 0 "
 436                       "failed:\t\tPASS\n");
 437        attr.mq_maxmsg = cur_max_msgs + 1;
 438        attr.mq_msgsize = 10;
 439        if (test_queue_fail(&attr, &result))
 440                printf("Queue open with mq_maxmsg > limit when euid = 0 "
 441                       "succeeded:\t\tPASS\n");
 442        else
 443                printf("Queue open with mq_maxmsg > limit when euid = 0 "
 444                       "failed:\t\tFAIL\n");
 445        attr.mq_maxmsg = 1;
 446        attr.mq_msgsize = cur_max_msgsize + 1;
 447        if (test_queue_fail(&attr, &result))
 448                printf("Queue open with mq_msgsize > limit when euid = 0 "
 449                       "succeeded:\t\tPASS\n");
 450        else
 451                printf("Queue open with mq_msgsize > limit when euid = 0 "
 452                       "failed:\t\tFAIL\n");
 453        attr.mq_maxmsg = 65536;
 454        attr.mq_msgsize = 65536;
 455        if (test_queue_fail(&attr, &result))
 456                printf("Queue open with total size > 2GB when euid = 0 "
 457                       "succeeded:\t\tFAIL\n");
 458        else
 459                printf("Queue open with total size > 2GB when euid = 0 "
 460                       "failed:\t\t\tPASS\n");
 461
 462        if (seteuid(99) == -1) {
 463                perror("seteuid() failed");
 464                exit(1);
 465        }
 466
 467        attr.mq_maxmsg = cur_max_msgs;
 468        attr.mq_msgsize = cur_max_msgsize;
 469        if (test_queue_fail(&attr, &result))
 470                printf("Queue open in excess of rlimit max when euid = 99 "
 471                       "succeeded:\t\tFAIL\n");
 472        else
 473                printf("Queue open in excess of rlimit max when euid = 99 "
 474                       "failed:\t\tPASS\n");
 475        attr.mq_maxmsg = cur_max_msgs + 1;
 476        attr.mq_msgsize = 10;
 477        if (test_queue_fail(&attr, &result))
 478                printf("Queue open with mq_maxmsg > limit when euid = 99 "
 479                       "succeeded:\t\tFAIL\n");
 480        else
 481                printf("Queue open with mq_maxmsg > limit when euid = 99 "
 482                       "failed:\t\tPASS\n");
 483        attr.mq_maxmsg = 1;
 484        attr.mq_msgsize = cur_max_msgsize + 1;
 485        if (test_queue_fail(&attr, &result))
 486                printf("Queue open with mq_msgsize > limit when euid = 99 "
 487                       "succeeded:\t\tFAIL\n");
 488        else
 489                printf("Queue open with mq_msgsize > limit when euid = 99 "
 490                       "failed:\t\tPASS\n");
 491        attr.mq_maxmsg = 65536;
 492        attr.mq_msgsize = 65536;
 493        if (test_queue_fail(&attr, &result))
 494                printf("Queue open with total size > 2GB when euid = 99 "
 495                       "succeeded:\t\tFAIL\n");
 496        else
 497                printf("Queue open with total size > 2GB when euid = 99 "
 498                       "failed:\t\t\tPASS\n");
 499
 500        shutdown(0,"",0);
 501}
 502