diff options
author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2009-09-21 20:02:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-22 10:17:34 -0400 |
commit | d0107eb07320b5d37c0f8a9f015534caebb28a48 (patch) | |
tree | 9f4f1000af3919d1118356ae7c6b298c6aa81769 /mm/vmalloc.c | |
parent | dd32c279983bf77fdcc8a9aa4a05b0ffdc75859c (diff) |
kcore: fix vread/vwrite to be aware of holes
vread/vwrite access vmalloc area without checking there is a page or not.
In most case, this works well.
In old ages, the caller of get_vm_ara() is only IOREMAP and there is no
memory hole within vm_struct's [addr...addr + size - PAGE_SIZE] (
-PAGE_SIZE is for a guard page.)
After per-cpu-alloc patch, it uses get_vm_area() for reserve continuous
virtual address but remap _later_. There tend to be a hole in valid
vmalloc area in vm_struct lists. Then, skip the hole (not mapped page) is
necessary. This patch updates vread/vwrite() for avoiding memory hole.
Routines which access vmalloc area without knowing for which addr is used
are
- /proc/kcore
- /dev/kmem
kcore checks IOREMAP, /dev/kmem doesn't. After this patch, IOREMAP is
checked and /dev/kmem will avoid to read/write it. Fixes to /proc/kcore
will be in the next patch in series.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: WANG Cong <xiyou.wangcong@gmail.com>
Cc: Mike Smith <scgtrp@gmail.com>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/vmalloc.c')
-rw-r--r-- | mm/vmalloc.c | 199 |
1 files changed, 176 insertions, 23 deletions
diff --git a/mm/vmalloc.c b/mm/vmalloc.c index c4071fa8e12a..9216b2555d07 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <linux/rcupdate.h> | 25 | #include <linux/rcupdate.h> |
26 | #include <linux/pfn.h> | 26 | #include <linux/pfn.h> |
27 | #include <linux/kmemleak.h> | 27 | #include <linux/kmemleak.h> |
28 | 28 | #include <linux/highmem.h> | |
29 | #include <asm/atomic.h> | 29 | #include <asm/atomic.h> |
30 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
31 | #include <asm/tlbflush.h> | 31 | #include <asm/tlbflush.h> |
@@ -1643,10 +1643,120 @@ void *vmalloc_32_user(unsigned long size) | |||
1643 | } | 1643 | } |
1644 | EXPORT_SYMBOL(vmalloc_32_user); | 1644 | EXPORT_SYMBOL(vmalloc_32_user); |
1645 | 1645 | ||
1646 | /* | ||
1647 | * small helper routine , copy contents to buf from addr. | ||
1648 | * If the page is not present, fill zero. | ||
1649 | */ | ||
1650 | |||
1651 | static int aligned_vread(char *buf, char *addr, unsigned long count) | ||
1652 | { | ||
1653 | struct page *p; | ||
1654 | int copied = 0; | ||
1655 | |||
1656 | while (count) { | ||
1657 | unsigned long offset, length; | ||
1658 | |||
1659 | offset = (unsigned long)addr & ~PAGE_MASK; | ||
1660 | length = PAGE_SIZE - offset; | ||
1661 | if (length > count) | ||
1662 | length = count; | ||
1663 | p = vmalloc_to_page(addr); | ||
1664 | /* | ||
1665 | * To do safe access to this _mapped_ area, we need | ||
1666 | * lock. But adding lock here means that we need to add | ||
1667 | * overhead of vmalloc()/vfree() calles for this _debug_ | ||
1668 | * interface, rarely used. Instead of that, we'll use | ||
1669 | * kmap() and get small overhead in this access function. | ||
1670 | */ | ||
1671 | if (p) { | ||
1672 | /* | ||
1673 | * we can expect USER0 is not used (see vread/vwrite's | ||
1674 | * function description) | ||
1675 | */ | ||
1676 | void *map = kmap_atomic(p, KM_USER0); | ||
1677 | memcpy(buf, map + offset, length); | ||
1678 | kunmap_atomic(map, KM_USER0); | ||
1679 | } else | ||
1680 | memset(buf, 0, length); | ||
1681 | |||
1682 | addr += length; | ||
1683 | buf += length; | ||
1684 | copied += length; | ||
1685 | count -= length; | ||
1686 | } | ||
1687 | return copied; | ||
1688 | } | ||
1689 | |||
1690 | static int aligned_vwrite(char *buf, char *addr, unsigned long count) | ||
1691 | { | ||
1692 | struct page *p; | ||
1693 | int copied = 0; | ||
1694 | |||
1695 | while (count) { | ||
1696 | unsigned long offset, length; | ||
1697 | |||
1698 | offset = (unsigned long)addr & ~PAGE_MASK; | ||
1699 | length = PAGE_SIZE - offset; | ||
1700 | if (length > count) | ||
1701 | length = count; | ||
1702 | p = vmalloc_to_page(addr); | ||
1703 | /* | ||
1704 | * To do safe access to this _mapped_ area, we need | ||
1705 | * lock. But adding lock here means that we need to add | ||
1706 | * overhead of vmalloc()/vfree() calles for this _debug_ | ||
1707 | * interface, rarely used. Instead of that, we'll use | ||
1708 | * kmap() and get small overhead in this access function. | ||
1709 | */ | ||
1710 | if (p) { | ||
1711 | /* | ||
1712 | * we can expect USER0 is not used (see vread/vwrite's | ||
1713 | * function description) | ||
1714 | */ | ||
1715 | void *map = kmap_atomic(p, KM_USER0); | ||
1716 | memcpy(map + offset, buf, length); | ||
1717 | kunmap_atomic(map, KM_USER0); | ||
1718 | } | ||
1719 | addr += length; | ||
1720 | buf += length; | ||
1721 | copied += length; | ||
1722 | count -= length; | ||
1723 | } | ||
1724 | return copied; | ||
1725 | } | ||
1726 | |||
1727 | /** | ||
1728 | * vread() - read vmalloc area in a safe way. | ||
1729 | * @buf: buffer for reading data | ||
1730 | * @addr: vm address. | ||
1731 | * @count: number of bytes to be read. | ||
1732 | * | ||
1733 | * Returns # of bytes which addr and buf should be increased. | ||
1734 | * (same number to @count). Returns 0 if [addr...addr+count) doesn't | ||
1735 | * includes any intersect with alive vmalloc area. | ||
1736 | * | ||
1737 | * This function checks that addr is a valid vmalloc'ed area, and | ||
1738 | * copy data from that area to a given buffer. If the given memory range | ||
1739 | * of [addr...addr+count) includes some valid address, data is copied to | ||
1740 | * proper area of @buf. If there are memory holes, they'll be zero-filled. | ||
1741 | * IOREMAP area is treated as memory hole and no copy is done. | ||
1742 | * | ||
1743 | * If [addr...addr+count) doesn't includes any intersects with alive | ||
1744 | * vm_struct area, returns 0. | ||
1745 | * @buf should be kernel's buffer. Because this function uses KM_USER0, | ||
1746 | * the caller should guarantee KM_USER0 is not used. | ||
1747 | * | ||
1748 | * Note: In usual ops, vread() is never necessary because the caller | ||
1749 | * should know vmalloc() area is valid and can use memcpy(). | ||
1750 | * This is for routines which have to access vmalloc area without | ||
1751 | * any informaion, as /dev/kmem. | ||
1752 | * | ||
1753 | */ | ||
1754 | |||
1646 | long vread(char *buf, char *addr, unsigned long count) | 1755 | long vread(char *buf, char *addr, unsigned long count) |
1647 | { | 1756 | { |
1648 | struct vm_struct *tmp; | 1757 | struct vm_struct *tmp; |
1649 | char *vaddr, *buf_start = buf; | 1758 | char *vaddr, *buf_start = buf; |
1759 | unsigned long buflen = count; | ||
1650 | unsigned long n; | 1760 | unsigned long n; |
1651 | 1761 | ||
1652 | /* Don't allow overflow */ | 1762 | /* Don't allow overflow */ |
@@ -1654,7 +1764,7 @@ long vread(char *buf, char *addr, unsigned long count) | |||
1654 | count = -(unsigned long) addr; | 1764 | count = -(unsigned long) addr; |
1655 | 1765 | ||
1656 | read_lock(&vmlist_lock); | 1766 | read_lock(&vmlist_lock); |
1657 | for (tmp = vmlist; tmp; tmp = tmp->next) { | 1767 | for (tmp = vmlist; count && tmp; tmp = tmp->next) { |
1658 | vaddr = (char *) tmp->addr; | 1768 | vaddr = (char *) tmp->addr; |
1659 | if (addr >= vaddr + tmp->size - PAGE_SIZE) | 1769 | if (addr >= vaddr + tmp->size - PAGE_SIZE) |
1660 | continue; | 1770 | continue; |
@@ -1667,32 +1777,72 @@ long vread(char *buf, char *addr, unsigned long count) | |||
1667 | count--; | 1777 | count--; |
1668 | } | 1778 | } |
1669 | n = vaddr + tmp->size - PAGE_SIZE - addr; | 1779 | n = vaddr + tmp->size - PAGE_SIZE - addr; |
1670 | do { | 1780 | if (n > count) |
1671 | if (count == 0) | 1781 | n = count; |
1672 | goto finished; | 1782 | if (!(tmp->flags & VM_IOREMAP)) |
1673 | *buf = *addr; | 1783 | aligned_vread(buf, addr, n); |
1674 | buf++; | 1784 | else /* IOREMAP area is treated as memory hole */ |
1675 | addr++; | 1785 | memset(buf, 0, n); |
1676 | count--; | 1786 | buf += n; |
1677 | } while (--n > 0); | 1787 | addr += n; |
1788 | count -= n; | ||
1678 | } | 1789 | } |
1679 | finished: | 1790 | finished: |
1680 | read_unlock(&vmlist_lock); | 1791 | read_unlock(&vmlist_lock); |
1681 | return buf - buf_start; | 1792 | |
1793 | if (buf == buf_start) | ||
1794 | return 0; | ||
1795 | /* zero-fill memory holes */ | ||
1796 | if (buf != buf_start + buflen) | ||
1797 | memset(buf, 0, buflen - (buf - buf_start)); | ||
1798 | |||
1799 | return buflen; | ||
1682 | } | 1800 | } |
1683 | 1801 | ||
1802 | /** | ||
1803 | * vwrite() - write vmalloc area in a safe way. | ||
1804 | * @buf: buffer for source data | ||
1805 | * @addr: vm address. | ||
1806 | * @count: number of bytes to be read. | ||
1807 | * | ||
1808 | * Returns # of bytes which addr and buf should be incresed. | ||
1809 | * (same number to @count). | ||
1810 | * If [addr...addr+count) doesn't includes any intersect with valid | ||
1811 | * vmalloc area, returns 0. | ||
1812 | * | ||
1813 | * This function checks that addr is a valid vmalloc'ed area, and | ||
1814 | * copy data from a buffer to the given addr. If specified range of | ||
1815 | * [addr...addr+count) includes some valid address, data is copied from | ||
1816 | * proper area of @buf. If there are memory holes, no copy to hole. | ||
1817 | * IOREMAP area is treated as memory hole and no copy is done. | ||
1818 | * | ||
1819 | * If [addr...addr+count) doesn't includes any intersects with alive | ||
1820 | * vm_struct area, returns 0. | ||
1821 | * @buf should be kernel's buffer. Because this function uses KM_USER0, | ||
1822 | * the caller should guarantee KM_USER0 is not used. | ||
1823 | * | ||
1824 | * Note: In usual ops, vwrite() is never necessary because the caller | ||
1825 | * should know vmalloc() area is valid and can use memcpy(). | ||
1826 | * This is for routines which have to access vmalloc area without | ||
1827 | * any informaion, as /dev/kmem. | ||
1828 | * | ||
1829 | * The caller should guarantee KM_USER1 is not used. | ||
1830 | */ | ||
1831 | |||
1684 | long vwrite(char *buf, char *addr, unsigned long count) | 1832 | long vwrite(char *buf, char *addr, unsigned long count) |
1685 | { | 1833 | { |
1686 | struct vm_struct *tmp; | 1834 | struct vm_struct *tmp; |
1687 | char *vaddr, *buf_start = buf; | 1835 | char *vaddr; |
1688 | unsigned long n; | 1836 | unsigned long n, buflen; |
1837 | int copied = 0; | ||
1689 | 1838 | ||
1690 | /* Don't allow overflow */ | 1839 | /* Don't allow overflow */ |
1691 | if ((unsigned long) addr + count < count) | 1840 | if ((unsigned long) addr + count < count) |
1692 | count = -(unsigned long) addr; | 1841 | count = -(unsigned long) addr; |
1842 | buflen = count; | ||
1693 | 1843 | ||
1694 | read_lock(&vmlist_lock); | 1844 | read_lock(&vmlist_lock); |
1695 | for (tmp = vmlist; tmp; tmp = tmp->next) { | 1845 | for (tmp = vmlist; count && tmp; tmp = tmp->next) { |
1696 | vaddr = (char *) tmp->addr; | 1846 | vaddr = (char *) tmp->addr; |
1697 | if (addr >= vaddr + tmp->size - PAGE_SIZE) | 1847 | if (addr >= vaddr + tmp->size - PAGE_SIZE) |
1698 | continue; | 1848 | continue; |
@@ -1704,18 +1854,21 @@ long vwrite(char *buf, char *addr, unsigned long count) | |||
1704 | count--; | 1854 | count--; |
1705 | } | 1855 | } |
1706 | n = vaddr + tmp->size - PAGE_SIZE - addr; | 1856 | n = vaddr + tmp->size - PAGE_SIZE - addr; |
1707 | do { | 1857 | if (n > count) |
1708 | if (count == 0) | 1858 | n = count; |
1709 | goto finished; | 1859 | if (!(tmp->flags & VM_IOREMAP)) { |
1710 | *addr = *buf; | 1860 | aligned_vwrite(buf, addr, n); |
1711 | buf++; | 1861 | copied++; |
1712 | addr++; | 1862 | } |
1713 | count--; | 1863 | buf += n; |
1714 | } while (--n > 0); | 1864 | addr += n; |
1865 | count -= n; | ||
1715 | } | 1866 | } |
1716 | finished: | 1867 | finished: |
1717 | read_unlock(&vmlist_lock); | 1868 | read_unlock(&vmlist_lock); |
1718 | return buf - buf_start; | 1869 | if (!copied) |
1870 | return 0; | ||
1871 | return buflen; | ||
1719 | } | 1872 | } |
1720 | 1873 | ||
1721 | /** | 1874 | /** |