/* * Copyright (C) 2012 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Further, this software is distributed without any warranty that it * is free of the rightful claim of any third person regarding * infringement or the like. Any license provided herein, whether * implied or otherwise, applies only to this software file. Patent * licenses, if any, provided herein do not apply to combinations of * this program with other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /* * This is a reproducer for CVE-2011-2496. * * The normal mmap paths all avoid creating a mapping where the pgoff * inside the mapping could wrap around due to overflow. However, an * expanding mremap() can take such a non-wrapping mapping and make it * bigger and cause a wrapping condition. There is also another case * where we expand mappings hiding in plain sight: the automatic stack * expansion. * * This program tries to remap a mapping with a new size that would * wrap pgoff. Notice that it only works on 32-bit arch for now. */ #define _GNU_SOURCE #include "config.h" #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/syscall.h> #include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "test.h" #include "safe_macros.h" #include "tst_kernel.h" char *TCID = "vma03"; int TST_TOTAL = 1; #ifdef __NR_mmap2 #define TESTFILE "testfile" static size_t pgsz; static int fd; static void *mmap2(void *addr, size_t length, int prot, int flags, int fd, off_t pgoffset); static void setup(void); static void cleanup(void); int main(int argc, char *argv[]) { int lc; void *map, *remap; off_t pgoff; if (__WORDSIZE != 32 || tst_kernel_bits() != 32) { tst_brkm(TCONF, NULL, "test is designed for 32-bit system only."); } tst_parse_opts(argc, argv, NULL, NULL); pgsz = sysconf(_SC_PAGE_SIZE); setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; fd = SAFE_OPEN(NULL, TESTFILE, O_RDWR); /* * The pgoff is counted in 4K units and must be page-aligned, * hence we must align it down to page_size/4096 in a case that * the system has page_size > 4K. */ pgoff = (ULONG_MAX - 1)&(~((pgsz-1)>>12)); map = mmap2(NULL, pgsz, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, pgoff); if (map == MAP_FAILED) tst_brkm(TBROK | TERRNO, cleanup, "mmap2"); remap = mremap(map, pgsz, 2 * pgsz, 0); if (remap == MAP_FAILED) { if (errno == EINVAL) tst_resm(TPASS, "mremap failed as expected."); else tst_resm(TFAIL | TERRNO, "mremap"); munmap(map, pgsz); } else { tst_resm(TFAIL, "mremap succeeded unexpectedly."); munmap(remap, 2 * pgsz); } close(fd); } cleanup(); tst_exit(); } static void *mmap2(void *addr, size_t length, int prot, int flags, int fd, off_t pgoffset) { return (void *)syscall(SYS_mmap2, addr, length, prot, flags, fd, pgoffset); } static void setup(void) { tst_sig(FORK, DEF_HANDLER, cleanup); tst_tmpdir(); fd = SAFE_CREAT(NULL, TESTFILE, 0644); close(fd); TEST_PAUSE; } static void cleanup(void) { tst_rmdir(); } #else /* __NR_mmap2 */ int main(int argc, char *argv[]) { tst_brkm(TCONF, NULL, "__NR_mmap2 is not defined on your system"); } #endif