150276Speter/*- 2166124Srafan * Copyright (c) 2006 Robert N. M. Watson 3166124Srafan * All rights reserved. 4166124Srafan * 5166124Srafan * Redistribution and use in source and binary forms, with or without 6166124Srafan * modification, are permitted provided that the following conditions 7166124Srafan * are met: 8166124Srafan * 1. Redistributions of source code must retain the above copyright 9166124Srafan * notice, this list of conditions and the following disclaimer. 10166124Srafan * 2. Redistributions in binary form must reproduce the above copyright 11166124Srafan * notice, this list of conditions and the following disclaimer in the 12166124Srafan * documentation and/or other materials provided with the distribution. 13166124Srafan * 14166124Srafan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15166124Srafan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16166124Srafan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17166124Srafan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18166124Srafan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19166124Srafan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20166124Srafan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21166124Srafan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22166124Srafan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23166124Srafan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24166124Srafan * SUCH DAMAGE. 25166124Srafan */ 26166124Srafan 27166124Srafan#include <sys/cdefs.h> 28166124Srafan__FBSDID("$FreeBSD: releng/10.2/tools/regression/posixshm/posixshm.c 175383 2008-01-16 15:51:24Z jhb $"); 29166124Srafan 3050276Speter#include <sys/param.h> 3150276Speter#include <sys/mman.h> 3250276Speter#include <sys/resource.h> 3350276Speter#include <sys/stat.h> 3450276Speter#include <sys/syscall.h> 3550276Speter#include <sys/wait.h> 3650276Speter 3750276Speter#include <errno.h> 3850276Speter#include <fcntl.h> 3950276Speter#include <stdio.h> 4066963Speter#include <stdlib.h> 4150276Speter#include <string.h> 4250276Speter#include <unistd.h> 4350276Speter 4450276Speter#include "test.h" 4550276Speter 4650276Speter#define TEST_PATH "/tmp/posixshm_regression_test" 4750276Speter 4856639Speter/* 4950276Speter * Attempt a shm_open() that should fail with an expected error of 'error'. 5050276Speter */ 5150276Speterstatic void 5250276Spetershm_open_should_fail(const char *path, int flags, mode_t mode, int error) 5350276Speter{ 5450276Speter int fd; 5550276Speter 5650276Speter fd = shm_open(path, flags, mode); 5750276Speter if (fd >= 0) { 5850276Speter fail_err("shm_open() didn't fail"); 5950276Speter close(fd); 6050276Speter return; 6150276Speter } 6250276Speter if (errno != error) { 6350276Speter fail_errno("shm_open"); 6450276Speter return; 6550276Speter } 6650276Speter pass(); 6750276Speter} 6850276Speter 6950276Speter/* 7050276Speter * Attempt a shm_unlink() that should fail with an expected error of 'error'. 7150276Speter */ 7250276Speterstatic void 73166124Srafanshm_unlink_should_fail(const char *path, int error) 74166124Srafan{ 75166124Srafan 76166124Srafan if (shm_unlink(path) >= 0) { 77166124Srafan fail_err("shm_unlink() didn't fail"); 78166124Srafan return; 79166124Srafan } 80166124Srafan if (errno != error) { 8150276Speter fail_errno("shm_unlink"); 8250276Speter return; 8350276Speter } 8450276Speter pass(); 8550276Speter} 8650276Speter 8750276Speter/* 8850276Speter * Open the test object and write '1' to the first byte. Returns valid fd 8950276Speter * on success and -1 on failure. 9050276Speter */ 9150276Speterstatic int 9250276Speterscribble_object(void) 9350276Speter{ 94166124Srafan char *page; 9550276Speter int fd; 9650276Speter 9750276Speter fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777); 9850276Speter if (fd < 0 && errno == EEXIST) { 9950276Speter if (shm_unlink(TEST_PATH) < 0) { 10050276Speter fail_errno("shm_unlink"); 10150276Speter return (-1); 10250276Speter } 10350276Speter fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777); 10450276Speter } 10550276Speter if (fd < 0) { 10650276Speter fail_errno("shm_open"); 10750276Speter return (-1); 10850276Speter } 10950276Speter if (ftruncate(fd, getpagesize()) < 0) { 11050276Speter fail_errno("ftruncate"); 11150276Speter close(fd); 11250276Speter shm_unlink(TEST_PATH); 11350276Speter return (-1); 11450276Speter } 11550276Speter 11650276Speter page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 11750276Speter 0); 11850276Speter if (page == MAP_FAILED) { 11950276Speter fail_errno("mmap"); 12050276Speter close(fd); 12150276Speter shm_unlink(TEST_PATH); 12250276Speter return (-1); 12350276Speter } 12450276Speter 12550276Speter page[0] = '1'; 12650276Speter 12750276Speter if (munmap(page, getpagesize()) < 0) { 12850276Speter fail_errno("munmap"); 12950276Speter close(fd); 13050276Speter shm_unlink(TEST_PATH); 13150276Speter return (-1); 13250276Speter } 13350276Speter 13466963Speter return (fd); 13566963Speter} 13666963Speter 13766963Speterstatic void 13850276Speterremap_object(void) 13950276Speter{ 14050276Speter char *page; 14150276Speter int fd; 14250276Speter 14350276Speter fd = scribble_object(); 14450276Speter if (fd < 0) 14550276Speter return; 14650276Speter 14750276Speter page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 14850276Speter 0); 14950276Speter if (page == MAP_FAILED) { 15050276Speter fail_errno("mmap(2)"); 15150276Speter close(fd); 15256639Speter shm_unlink(TEST_PATH); 15350276Speter return; 15450276Speter } 15550276Speter 15650276Speter if (page[0] != '1') { 15750276Speter fail_err("missing data"); 15850276Speter close(fd); 15950276Speter shm_unlink(TEST_PATH); 16050276Speter return; 16150276Speter } 16250276Speter 16350276Speter close(fd); 16450276Speter if (munmap(page, getpagesize()) < 0) { 16550276Speter fail_errno("munmap"); 16650276Speter shm_unlink(TEST_PATH); 16750276Speter return; 16850276Speter } 16950276Speter 17050276Speter if (shm_unlink(TEST_PATH) < 0) { 17150276Speter fail_errno("shm_unlink"); 17250276Speter return; 17350276Speter } 17450276Speter 17550276Speter pass(); 17650276Speter} 17750276SpeterTEST(remap_object, "remap object"); 17850276Speter 17950276Speterstatic void 18050276Speterreopen_object(void) 18150276Speter{ 18250276Speter char *page; 18350276Speter int fd; 18450276Speter 18550276Speter fd = scribble_object(); 18650276Speter if (fd < 0) 18750276Speter return; 18850276Speter close(fd); 18950276Speter 19050276Speter fd = shm_open(TEST_PATH, O_RDONLY, 0777); 19150276Speter if (fd < 0) { 19250276Speter fail_errno("shm_open(2)"); 19350276Speter shm_unlink(TEST_PATH); 19450276Speter return; 19550276Speter } 19650276Speter page = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); 19750276Speter if (page == MAP_FAILED) { 19850276Speter fail_errno("mmap(2)"); 19950276Speter close(fd); 20050276Speter shm_unlink(TEST_PATH); 20150276Speter return; 20250276Speter } 20350276Speter 20450276Speter if (page[0] != '1') { 20550276Speter fail_err("missing data"); 20650276Speter munmap(page, getpagesize()); 20750276Speter close(fd); 20850276Speter shm_unlink(TEST_PATH); 20966963Speter return; 21050276Speter } 211 212 munmap(page, getpagesize()); 213 close(fd); 214 shm_unlink(TEST_PATH); 215 pass(); 216} 217TEST(reopen_object, "reopen object"); 218 219static void 220readonly_mmap_write(void) 221{ 222 char *page; 223 int fd; 224 225 fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777); 226 if (fd < 0) { 227 fail_errno("shm_open"); 228 return; 229 } 230 231 /* PROT_WRITE should fail with EACCES. */ 232 page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 233 0); 234 if (page != MAP_FAILED) { 235 fail_err("mmap(PROT_WRITE) succeeded"); 236 munmap(page, getpagesize()); 237 close(fd); 238 shm_unlink(TEST_PATH); 239 return; 240 } 241 if (errno != EACCES) { 242 fail_errno("mmap"); 243 close(fd); 244 shm_unlink(TEST_PATH); 245 return; 246 } 247 248 close(fd); 249 shm_unlink(TEST_PATH); 250 pass(); 251} 252TEST(readonly_mmap_write, "RDONLY object"); 253 254static void 255open_after_unlink(void) 256{ 257 int fd; 258 259 fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777); 260 if (fd < 0) { 261 fail_errno("shm_open(1)"); 262 return; 263 } 264 close(fd); 265 266 if (shm_unlink(TEST_PATH) < 0) { 267 fail_errno("shm_unlink"); 268 return; 269 } 270 271 shm_open_should_fail(TEST_PATH, O_RDONLY, 0777, ENOENT); 272} 273TEST(open_after_unlink, "open after unlink"); 274 275static void 276open_invalid_path(void) 277{ 278 279 shm_open_should_fail("blah", O_RDONLY, 0777, EINVAL); 280} 281TEST(open_invalid_path, "open invalid path"); 282 283static void 284open_write_only(void) 285{ 286 287 shm_open_should_fail(TEST_PATH, O_WRONLY, 0777, EINVAL); 288} 289TEST(open_write_only, "open with O_WRONLY"); 290 291static void 292open_extra_flags(void) 293{ 294 295 shm_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, EINVAL); 296} 297TEST(open_extra_flags, "open with extra flags"); 298 299static void 300open_anon(void) 301{ 302 int fd; 303 304 fd = shm_open(SHM_ANON, O_RDWR, 0777); 305 if (fd < 0) { 306 fail_errno("shm_open"); 307 return; 308 } 309 close(fd); 310 pass(); 311} 312TEST(open_anon, "open anonymous object"); 313 314static void 315open_anon_readonly(void) 316{ 317 318 shm_open_should_fail(SHM_ANON, O_RDONLY, 0777, EINVAL); 319} 320TEST(open_anon_readonly, "open SHM_ANON with O_RDONLY"); 321 322static void 323open_bad_path_pointer(void) 324{ 325 326 shm_open_should_fail((char *)1024, O_RDONLY, 0777, EFAULT); 327} 328TEST(open_bad_path_pointer, "open bad path pointer"); 329 330static void 331open_path_too_long(void) 332{ 333 char *page; 334 335 page = malloc(MAXPATHLEN + 1); 336 memset(page, 'a', MAXPATHLEN); 337 page[MAXPATHLEN] = '\0'; 338 shm_open_should_fail(page, O_RDONLY, 0777, ENAMETOOLONG); 339 free(page); 340} 341TEST(open_path_too_long, "open pathname too long"); 342 343static void 344open_nonexisting_object(void) 345{ 346 347 shm_open_should_fail("/notreallythere", O_RDONLY, 0777, ENOENT); 348} 349TEST(open_nonexisting_object, "open nonexistent object"); 350 351static void 352exclusive_create_existing_object(void) 353{ 354 int fd; 355 356 fd = shm_open("/tmp/notreallythere", O_RDONLY | O_CREAT, 0777); 357 if (fd < 0) { 358 fail_errno("shm_open(O_CREAT)"); 359 return; 360 } 361 close(fd); 362 363 shm_open_should_fail("/tmp/notreallythere", O_RDONLY | O_CREAT | O_EXCL, 364 0777, EEXIST); 365 366 shm_unlink("/tmp/notreallythere"); 367} 368TEST(exclusive_create_existing_object, "O_EXCL of existing object"); 369 370static void 371trunc_resets_object(void) 372{ 373 struct stat sb; 374 int fd; 375 376 /* Create object and set size to 1024. */ 377 fd = shm_open(TEST_PATH, O_RDWR | O_CREAT, 0777); 378 if (fd < 0) { 379 fail_errno("shm_open(1)"); 380 return; 381 } 382 if (ftruncate(fd, 1024) < 0) { 383 fail_errno("ftruncate"); 384 close(fd); 385 return; 386 } 387 if (fstat(fd, &sb) < 0) { 388 fail_errno("fstat(1)"); 389 close(fd); 390 return; 391 } 392 if (sb.st_size != 1024) { 393 fail_err("size %d != 1024", (int)sb.st_size); 394 close(fd); 395 return; 396 } 397 close(fd); 398 399 /* Open with O_TRUNC which should reset size to 0. */ 400 fd = shm_open(TEST_PATH, O_RDWR | O_TRUNC, 0777); 401 if (fd < 0) { 402 fail_errno("shm_open(2)"); 403 return; 404 } 405 if (fstat(fd, &sb) < 0) { 406 fail_errno("fstat(2)"); 407 close(fd); 408 return; 409 } 410 if (sb.st_size != 0) { 411 fail_err("size after O_TRUNC %d != 0", (int)sb.st_size); 412 close(fd); 413 return; 414 } 415 close(fd); 416 if (shm_unlink(TEST_PATH) < 0) { 417 fail_errno("shm_unlink"); 418 return; 419 } 420 pass(); 421} 422TEST(trunc_resets_object, "O_TRUNC resets size"); 423 424static void 425unlink_bad_path_pointer(void) 426{ 427 428 shm_unlink_should_fail((char *)1024, EFAULT); 429} 430TEST(unlink_bad_path_pointer, "unlink bad path pointer"); 431 432static void 433unlink_path_too_long(void) 434{ 435 char *page; 436 437 page = malloc(MAXPATHLEN + 1); 438 memset(page, 'a', MAXPATHLEN); 439 page[MAXPATHLEN] = '\0'; 440 shm_unlink_should_fail(page, ENAMETOOLONG); 441 free(page); 442} 443TEST(unlink_path_too_long, "unlink pathname too long"); 444 445static void 446test_object_resize(void) 447{ 448 pid_t pid; 449 struct stat sb; 450 char *page; 451 int fd, status; 452 453 /* Start off with a size of a single page. */ 454 fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0777); 455 if (fd < 0) { 456 fail_errno("shm_open"); 457 return; 458 } 459 if (ftruncate(fd, getpagesize()) < 0) { 460 fail_errno("ftruncate(1)"); 461 close(fd); 462 return; 463 } 464 if (fstat(fd, &sb) < 0) { 465 fail_errno("fstat(1)"); 466 close(fd); 467 return; 468 } 469 if (sb.st_size != getpagesize()) { 470 fail_err("first resize failed"); 471 close(fd); 472 return; 473 } 474 475 /* Write a '1' to the first byte. */ 476 page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 477 0); 478 if (page == MAP_FAILED) { 479 fail_errno("mmap(1)"); 480 close(fd); 481 return; 482 } 483 484 page[0] = '1'; 485 486 if (munmap(page, getpagesize()) < 0) { 487 fail_errno("munmap(1)"); 488 close(fd); 489 return; 490 } 491 492 /* Grow the object to 2 pages. */ 493 if (ftruncate(fd, getpagesize() * 2) < 0) { 494 fail_errno("ftruncate(2)"); 495 close(fd); 496 return; 497 } 498 if (fstat(fd, &sb) < 0) { 499 fail_errno("fstat(2)"); 500 close(fd); 501 return; 502 } 503 if (sb.st_size != getpagesize() * 2) { 504 fail_err("second resize failed"); 505 close(fd); 506 return; 507 } 508 509 /* Check for '1' at the first byte. */ 510 page = mmap(0, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED, 511 fd, 0); 512 if (page == MAP_FAILED) { 513 fail_errno("mmap(2)"); 514 close(fd); 515 return; 516 } 517 518 if (page[0] != '1') { 519 fail_err("missing data at 0"); 520 close(fd); 521 return; 522 } 523 524 /* Write a '2' at the start of the second page. */ 525 page[getpagesize()] = '2'; 526 527 /* Shrink the object back to 1 page. */ 528 if (ftruncate(fd, getpagesize()) < 0) { 529 fail_errno("ftruncate(3)"); 530 close(fd); 531 return; 532 } 533 if (fstat(fd, &sb) < 0) { 534 fail_errno("fstat(3)"); 535 close(fd); 536 return; 537 } 538 if (sb.st_size != getpagesize()) { 539 fail_err("third resize failed"); 540 close(fd); 541 return; 542 } 543 544 /* 545 * Fork a child process to make sure the second page is no 546 * longer valid. 547 */ 548 pid = fork(); 549 if (pid < 0) { 550 fail_errno("fork"); 551 close(fd); 552 return; 553 } 554 555 if (pid == 0) { 556 struct rlimit lim; 557 char c; 558 559 /* Don't generate a core dump. */ 560 getrlimit(RLIMIT_CORE, &lim); 561 lim.rlim_cur = 0; 562 setrlimit(RLIMIT_CORE, &lim); 563 564 /* 565 * The previous ftruncate(2) shrunk the backing object 566 * so that this address is no longer valid, so reading 567 * from it should trigger a SIGSEGV. 568 */ 569 c = page[getpagesize()]; 570 fprintf(stderr, "child: page 1: '%c'\n", c); 571 exit(0); 572 } 573 if (wait(&status) < 0) { 574 fail_errno("wait"); 575 close(fd); 576 return; 577 } 578 if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGSEGV) { 579 fail_err("child terminated with status %x", status); 580 close(fd); 581 return; 582 } 583 584 /* Grow the object back to 2 pages. */ 585 if (ftruncate(fd, getpagesize() * 2) < 0) { 586 fail_errno("ftruncate(4)"); 587 close(fd); 588 return; 589 } 590 if (fstat(fd, &sb) < 0) { 591 fail_errno("fstat(4)"); 592 close(fd); 593 return; 594 } 595 if (sb.st_size != getpagesize() * 2) { 596 fail_err("second resize failed"); 597 close(fd); 598 return; 599 } 600 601 /* 602 * Note that the mapping at 'page' for the second page is 603 * still valid, and now that the shm object has been grown 604 * back up to 2 pages, there is now memory backing this page 605 * so the read will work. However, the data should be zero 606 * rather than '2' as the old data was thrown away when the 607 * object was shrunk and the new pages when an object are 608 * grown are zero-filled. 609 */ 610 if (page[getpagesize()] != 0) { 611 fail_err("invalid data at %d", getpagesize()); 612 close(fd); 613 return; 614 } 615 616 close(fd); 617 pass(); 618} 619TEST(test_object_resize, "object resize"); 620 621int 622main(int argc, char *argv[]) 623{ 624 625 run_tests(); 626 return (0); 627} 628