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