diff options
| -rw-r--r-- | fs/fcntl.c | 5 | ||||
| -rw-r--r-- | include/linux/shmem_fs.h | 17 | ||||
| -rw-r--r-- | include/uapi/linux/fcntl.h | 15 | ||||
| -rw-r--r-- | mm/shmem.c | 143 |
4 files changed, 180 insertions, 0 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index 72c82f69b01b..22d1c3df61ac 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/rcupdate.h> | 21 | #include <linux/rcupdate.h> |
| 22 | #include <linux/pid_namespace.h> | 22 | #include <linux/pid_namespace.h> |
| 23 | #include <linux/user_namespace.h> | 23 | #include <linux/user_namespace.h> |
| 24 | #include <linux/shmem_fs.h> | ||
| 24 | 25 | ||
| 25 | #include <asm/poll.h> | 26 | #include <asm/poll.h> |
| 26 | #include <asm/siginfo.h> | 27 | #include <asm/siginfo.h> |
| @@ -336,6 +337,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, | |||
| 336 | case F_GETPIPE_SZ: | 337 | case F_GETPIPE_SZ: |
| 337 | err = pipe_fcntl(filp, cmd, arg); | 338 | err = pipe_fcntl(filp, cmd, arg); |
| 338 | break; | 339 | break; |
| 340 | case F_ADD_SEALS: | ||
| 341 | case F_GET_SEALS: | ||
| 342 | err = shmem_fcntl(filp, cmd, arg); | ||
| 343 | break; | ||
| 339 | default: | 344 | default: |
| 340 | break; | 345 | break; |
| 341 | } | 346 | } |
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index 4d1771c2d29f..50777b5b1e4c 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #ifndef __SHMEM_FS_H | 1 | #ifndef __SHMEM_FS_H |
| 2 | #define __SHMEM_FS_H | 2 | #define __SHMEM_FS_H |
| 3 | 3 | ||
| 4 | #include <linux/file.h> | ||
| 4 | #include <linux/swap.h> | 5 | #include <linux/swap.h> |
| 5 | #include <linux/mempolicy.h> | 6 | #include <linux/mempolicy.h> |
| 6 | #include <linux/pagemap.h> | 7 | #include <linux/pagemap.h> |
| @@ -11,6 +12,7 @@ | |||
| 11 | 12 | ||
| 12 | struct shmem_inode_info { | 13 | struct shmem_inode_info { |
| 13 | spinlock_t lock; | 14 | spinlock_t lock; |
| 15 | unsigned int seals; /* shmem seals */ | ||
| 14 | unsigned long flags; | 16 | unsigned long flags; |
| 15 | unsigned long alloced; /* data pages alloced to file */ | 17 | unsigned long alloced; /* data pages alloced to file */ |
| 16 | union { | 18 | union { |
| @@ -65,4 +67,19 @@ static inline struct page *shmem_read_mapping_page( | |||
| 65 | mapping_gfp_mask(mapping)); | 67 | mapping_gfp_mask(mapping)); |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 70 | #ifdef CONFIG_TMPFS | ||
| 71 | |||
| 72 | extern int shmem_add_seals(struct file *file, unsigned int seals); | ||
| 73 | extern int shmem_get_seals(struct file *file); | ||
| 74 | extern long shmem_fcntl(struct file *file, unsigned int cmd, unsigned long arg); | ||
| 75 | |||
| 76 | #else | ||
| 77 | |||
| 78 | static inline long shmem_fcntl(struct file *f, unsigned int c, unsigned long a) | ||
| 79 | { | ||
| 80 | return -EINVAL; | ||
| 81 | } | ||
| 82 | |||
| 83 | #endif | ||
| 84 | |||
| 68 | #endif | 85 | #endif |
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 074b886c6be0..beed138bd359 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h | |||
| @@ -28,6 +28,21 @@ | |||
| 28 | #define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) | 28 | #define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) |
| 29 | 29 | ||
| 30 | /* | 30 | /* |
| 31 | * Set/Get seals | ||
| 32 | */ | ||
| 33 | #define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) | ||
| 34 | #define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Types of seals | ||
| 38 | */ | ||
| 39 | #define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ | ||
| 40 | #define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ | ||
| 41 | #define F_SEAL_GROW 0x0004 /* prevent file from growing */ | ||
| 42 | #define F_SEAL_WRITE 0x0008 /* prevent writes */ | ||
| 43 | /* (1U << 31) is reserved for signed error codes */ | ||
| 44 | |||
| 45 | /* | ||
| 31 | * Types of directory notifications that may be requested. | 46 | * Types of directory notifications that may be requested. |
| 32 | */ | 47 | */ |
| 33 | #define DN_ACCESS 0x00000001 /* File accessed */ | 48 | #define DN_ACCESS 0x00000001 /* File accessed */ |
diff --git a/mm/shmem.c b/mm/shmem.c index 6dc80d298f9d..8b43bb7a4efe 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
| @@ -66,6 +66,7 @@ static struct vfsmount *shm_mnt; | |||
| 66 | #include <linux/highmem.h> | 66 | #include <linux/highmem.h> |
| 67 | #include <linux/seq_file.h> | 67 | #include <linux/seq_file.h> |
| 68 | #include <linux/magic.h> | 68 | #include <linux/magic.h> |
| 69 | #include <linux/fcntl.h> | ||
| 69 | 70 | ||
| 70 | #include <asm/uaccess.h> | 71 | #include <asm/uaccess.h> |
| 71 | #include <asm/pgtable.h> | 72 | #include <asm/pgtable.h> |
| @@ -547,6 +548,7 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range); | |||
| 547 | static int shmem_setattr(struct dentry *dentry, struct iattr *attr) | 548 | static int shmem_setattr(struct dentry *dentry, struct iattr *attr) |
| 548 | { | 549 | { |
| 549 | struct inode *inode = dentry->d_inode; | 550 | struct inode *inode = dentry->d_inode; |
| 551 | struct shmem_inode_info *info = SHMEM_I(inode); | ||
| 550 | int error; | 552 | int error; |
| 551 | 553 | ||
| 552 | error = inode_change_ok(inode, attr); | 554 | error = inode_change_ok(inode, attr); |
| @@ -557,6 +559,11 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr) | |||
| 557 | loff_t oldsize = inode->i_size; | 559 | loff_t oldsize = inode->i_size; |
| 558 | loff_t newsize = attr->ia_size; | 560 | loff_t newsize = attr->ia_size; |
| 559 | 561 | ||
| 562 | /* protected by i_mutex */ | ||
| 563 | if ((newsize < oldsize && (info->seals & F_SEAL_SHRINK)) || | ||
| 564 | (newsize > oldsize && (info->seals & F_SEAL_GROW))) | ||
| 565 | return -EPERM; | ||
| 566 | |||
| 560 | if (newsize != oldsize) { | 567 | if (newsize != oldsize) { |
| 561 | error = shmem_reacct_size(SHMEM_I(inode)->flags, | 568 | error = shmem_reacct_size(SHMEM_I(inode)->flags, |
| 562 | oldsize, newsize); | 569 | oldsize, newsize); |
| @@ -1412,6 +1419,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode | |||
| 1412 | info = SHMEM_I(inode); | 1419 | info = SHMEM_I(inode); |
| 1413 | memset(info, 0, (char *)inode - (char *)info); | 1420 | memset(info, 0, (char *)inode - (char *)info); |
| 1414 | spin_lock_init(&info->lock); | 1421 | spin_lock_init(&info->lock); |
| 1422 | info->seals = F_SEAL_SEAL; | ||
| 1415 | info->flags = flags & VM_NORESERVE; | 1423 | info->flags = flags & VM_NORESERVE; |
| 1416 | INIT_LIST_HEAD(&info->swaplist); | 1424 | INIT_LIST_HEAD(&info->swaplist); |
| 1417 | simple_xattrs_init(&info->xattrs); | 1425 | simple_xattrs_init(&info->xattrs); |
| @@ -1470,7 +1478,17 @@ shmem_write_begin(struct file *file, struct address_space *mapping, | |||
| 1470 | struct page **pagep, void **fsdata) | 1478 | struct page **pagep, void **fsdata) |
| 1471 | { | 1479 | { |
| 1472 | struct inode *inode = mapping->host; | 1480 | struct inode *inode = mapping->host; |
| 1481 | struct shmem_inode_info *info = SHMEM_I(inode); | ||
| 1473 | pgoff_t index = pos >> PAGE_CACHE_SHIFT; | 1482 | pgoff_t index = pos >> PAGE_CACHE_SHIFT; |
| 1483 | |||
| 1484 | /* i_mutex is held by caller */ | ||
| 1485 | if (unlikely(info->seals)) { | ||
| 1486 | if (info->seals & F_SEAL_WRITE) | ||
| 1487 | return -EPERM; | ||
| 1488 | if ((info->seals & F_SEAL_GROW) && pos + len > inode->i_size) | ||
| 1489 | return -EPERM; | ||
| 1490 | } | ||
| 1491 | |||
| 1474 | return shmem_getpage(inode, index, pagep, SGP_WRITE, NULL); | 1492 | return shmem_getpage(inode, index, pagep, SGP_WRITE, NULL); |
| 1475 | } | 1493 | } |
| 1476 | 1494 | ||
| @@ -1808,11 +1826,125 @@ static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence) | |||
| 1808 | return offset; | 1826 | return offset; |
| 1809 | } | 1827 | } |
| 1810 | 1828 | ||
| 1829 | static int shmem_wait_for_pins(struct address_space *mapping) | ||
| 1830 | { | ||
| 1831 | return 0; | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | #define F_ALL_SEALS (F_SEAL_SEAL | \ | ||
| 1835 | F_SEAL_SHRINK | \ | ||
| 1836 | F_SEAL_GROW | \ | ||
| 1837 | F_SEAL_WRITE) | ||
| 1838 | |||
| 1839 | int shmem_add_seals(struct file *file, unsigned int seals) | ||
| 1840 | { | ||
| 1841 | struct inode *inode = file_inode(file); | ||
| 1842 | struct shmem_inode_info *info = SHMEM_I(inode); | ||
| 1843 | int error; | ||
| 1844 | |||
| 1845 | /* | ||
| 1846 | * SEALING | ||
| 1847 | * Sealing allows multiple parties to share a shmem-file but restrict | ||
| 1848 | * access to a specific subset of file operations. Seals can only be | ||
| 1849 | * added, but never removed. This way, mutually untrusted parties can | ||
| 1850 | * share common memory regions with a well-defined policy. A malicious | ||
| 1851 | * peer can thus never perform unwanted operations on a shared object. | ||
| 1852 | * | ||
| 1853 | * Seals are only supported on special shmem-files and always affect | ||
| 1854 | * the whole underlying inode. Once a seal is set, it may prevent some | ||
| 1855 | * kinds of access to the file. Currently, the following seals are | ||
| 1856 | * defined: | ||
| 1857 | * SEAL_SEAL: Prevent further seals from being set on this file | ||
| 1858 | * SEAL_SHRINK: Prevent the file from shrinking | ||
| 1859 | * SEAL_GROW: Prevent the file from growing | ||
| 1860 | * SEAL_WRITE: Prevent write access to the file | ||
| 1861 | * | ||
| 1862 | * As we don't require any trust relationship between two parties, we | ||
| 1863 | * must prevent seals from being removed. Therefore, sealing a file | ||
| 1864 | * only adds a given set of seals to the file, it never touches | ||
| 1865 | * existing seals. Furthermore, the "setting seals"-operation can be | ||
| 1866 | * sealed itself, which basically prevents any further seal from being | ||
| 1867 | * added. | ||
| 1868 | * | ||
| 1869 | * Semantics of sealing are only defined on volatile files. Only | ||
| 1870 | * anonymous shmem files support sealing. More importantly, seals are | ||
| 1871 | * never written to disk. Therefore, there's no plan to support it on | ||
| 1872 | * other file types. | ||
| 1873 | */ | ||
| 1874 | |||
| 1875 | if (file->f_op != &shmem_file_operations) | ||
| 1876 | return -EINVAL; | ||
| 1877 | if (!(file->f_mode & FMODE_WRITE)) | ||
| 1878 | return -EPERM; | ||
| 1879 | if (seals & ~(unsigned int)F_ALL_SEALS) | ||
| 1880 | return -EINVAL; | ||
| 1881 | |||
| 1882 | mutex_lock(&inode->i_mutex); | ||
| 1883 | |||
| 1884 | if (info->seals & F_SEAL_SEAL) { | ||
| 1885 | error = -EPERM; | ||
| 1886 | goto unlock; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | if ((seals & F_SEAL_WRITE) && !(info->seals & F_SEAL_WRITE)) { | ||
| 1890 | error = mapping_deny_writable(file->f_mapping); | ||
| 1891 | if (error) | ||
| 1892 | goto unlock; | ||
| 1893 | |||
| 1894 | error = shmem_wait_for_pins(file->f_mapping); | ||
| 1895 | if (error) { | ||
| 1896 | mapping_allow_writable(file->f_mapping); | ||
| 1897 | goto unlock; | ||
| 1898 | } | ||
| 1899 | } | ||
| 1900 | |||
| 1901 | info->seals |= seals; | ||
| 1902 | error = 0; | ||
| 1903 | |||
| 1904 | unlock: | ||
| 1905 | mutex_unlock(&inode->i_mutex); | ||
| 1906 | return error; | ||
| 1907 | } | ||
| 1908 | EXPORT_SYMBOL_GPL(shmem_add_seals); | ||
| 1909 | |||
| 1910 | int shmem_get_seals(struct file *file) | ||
| 1911 | { | ||
| 1912 | if (file->f_op != &shmem_file_operations) | ||
| 1913 | return -EINVAL; | ||
| 1914 | |||
| 1915 | return SHMEM_I(file_inode(file))->seals; | ||
| 1916 | } | ||
| 1917 | EXPORT_SYMBOL_GPL(shmem_get_seals); | ||
| 1918 | |||
| 1919 | long shmem_fcntl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 1920 | { | ||
| 1921 | long error; | ||
| 1922 | |||
| 1923 | switch (cmd) { | ||
| 1924 | case F_ADD_SEALS: | ||
| 1925 | /* disallow upper 32bit */ | ||
| 1926 | if (arg > UINT_MAX) | ||
| 1927 | return -EINVAL; | ||
| 1928 | |||
| 1929 | error = shmem_add_seals(file, arg); | ||
| 1930 | break; | ||
| 1931 | case F_GET_SEALS: | ||
| 1932 | error = shmem_get_seals(file); | ||
| 1933 | break; | ||
| 1934 | default: | ||
| 1935 | error = -EINVAL; | ||
| 1936 | break; | ||
| 1937 | } | ||
| 1938 | |||
| 1939 | return error; | ||
| 1940 | } | ||
| 1941 | |||
| 1811 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, | 1942 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, |
| 1812 | loff_t len) | 1943 | loff_t len) |
| 1813 | { | 1944 | { |
| 1814 | struct inode *inode = file_inode(file); | 1945 | struct inode *inode = file_inode(file); |
| 1815 | struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); | 1946 | struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); |
| 1947 | struct shmem_inode_info *info = SHMEM_I(inode); | ||
| 1816 | struct shmem_falloc shmem_falloc; | 1948 | struct shmem_falloc shmem_falloc; |
| 1817 | pgoff_t start, index, end; | 1949 | pgoff_t start, index, end; |
| 1818 | int error; | 1950 | int error; |
| @@ -1828,6 +1960,12 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, | |||
| 1828 | loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1; | 1960 | loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1; |
| 1829 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(shmem_falloc_waitq); | 1961 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(shmem_falloc_waitq); |
| 1830 | 1962 | ||
| 1963 | /* protected by i_mutex */ | ||
| 1964 | if (info->seals & F_SEAL_WRITE) { | ||
| 1965 | error = -EPERM; | ||
| 1966 | goto out; | ||
| 1967 | } | ||
| 1968 | |||
| 1831 | shmem_falloc.waitq = &shmem_falloc_waitq; | 1969 | shmem_falloc.waitq = &shmem_falloc_waitq; |
| 1832 | shmem_falloc.start = unmap_start >> PAGE_SHIFT; | 1970 | shmem_falloc.start = unmap_start >> PAGE_SHIFT; |
| 1833 | shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT; | 1971 | shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT; |
| @@ -1854,6 +1992,11 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset, | |||
| 1854 | if (error) | 1992 | if (error) |
| 1855 | goto out; | 1993 | goto out; |
| 1856 | 1994 | ||
| 1995 | if ((info->seals & F_SEAL_GROW) && offset + len > inode->i_size) { | ||
| 1996 | error = -EPERM; | ||
| 1997 | goto out; | ||
| 1998 | } | ||
| 1999 | |||
| 1857 | start = offset >> PAGE_CACHE_SHIFT; | 2000 | start = offset >> PAGE_CACHE_SHIFT; |
| 1858 | end = (offset + len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | 2001 | end = (offset + len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; |
| 1859 | /* Try to avoid a swapstorm if len is impossible to satisfy */ | 2002 | /* Try to avoid a swapstorm if len is impossible to satisfy */ |
