| From 16ca53587e80324277212fe931c6d3a94b2f1750 Mon Sep 17 00:00:00 2001 |
| From: Will Drewry <wad@chromium.org> |
| Date: Thu, 21 Mar 2013 17:00:53 -0500 |
| Subject: [PATCH] CHROMIUM: Restrict swapon() to "zram" devices / lock down |
| zram |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| This cl contains three distinct changes collapsed into |
| one CL as per semenzato's request: |
| 1. Restrict CONFIG_SWAP to ensure that only zram devices |
| may be used. |
| 2. Restrict zram to disallow open calls, even from root, if |
| the device is claimed (e.g., by sys_swapon) |
| 3. Add swapoff fallback when filp_open fails to use the path lookup. |
| I don't believe swapoff needs filp_open() since kern_path() |
| provides the data needed without a file object. |
| 4. Add an open counter to zram to ensure that it is not opened more |
| times than swapon(2) will claim it -- twice: |
| - blkdev_get and filp_open (for swap_file) |
| |
| Signed-off-by: Will Drewry <wad@chromium.org> |
| TEST=tested on lumpy; swapon, swapoff, change zram during swapon fails, change withotu swapon succeeds. |
| BUG=chromium:220974 |
| |
| Change-Id: Ic281a7004a81b2897cf0bf1c5d334351061261f1 |
| [sonnyrao: 3.8: fixup conflicts] |
| Reviewed-on: https://gerrit.chromium.org/gerrit/46168 |
| Tested-by: Will Drewry <wad@chromium.org> |
| Reviewed-by: Luigi Semenzato <semenzato@chromium.org> |
| Commit-Queue: Will Drewry <wad@chromium.org> |
| Reviewed-on: https://gerrit.chromium.org/gerrit/58780 |
| Commit-Queue: Sonny Rao <sonnyrao@chromium.org> |
| Reviewed-by: Sonny Rao <sonnyrao@chromium.org> |
| Tested-by: Sonny Rao <sonnyrao@chromium.org> |
| |
| [benzh: 3.14 rebase. Squashed a compilation error fix. Also zram driver moved |
| from drivers/staging/ to drivers/block/ in upstream] |
| Signed-off-by: Ben Zhang <benzh@chromium.org> |
| |
| [rebase44(filbranden): Adapted the patch to merge our zram_open with the |
| one introduced upstream (between v3.18 and v4.4). |
| Squashed these three commits together: |
| - CHROMIUM: Restrict swapon() to "zram" devices / lock down zram |
| - CHROMIUM: Allow disk-based swap files |
| - CHROMIUM: minor changes to chromium specific open. |
| ] |
| Signed-off-by: Filipe Brandenburger <filbranden@chromium.org> |
| |
| Conflicts: |
| drivers/block/zram/zram_drv.h |
| kernel/sysctl.c |
| |
| [rebase412(groeck): Resolved conflicts] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| |
| [rebase419(groeck): |
| Squashed commit: |
| CHROMIUM: Fix mismatched mutex_unlock in swapon() |
| Resolved context conflicts] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| |
| [rebase54(yuzhao): |
| Restrict access to zram block device by bd_opener. This |
| eliminates the need of an atomic counter and achieves both |
| 2 & 4; therefore there is no need to check bd_holder.] |
| Signed-off-by: Yu Zhao <yuzhao@google.com> |
| [rebase510(groeck): Resolved conflicts] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| |
| [rebase515(groeck) |
| Squash: |
| FIXUP: CHROMIUM: Restrict swapon() to "zram" devices / lock down zram |
| ] |
| |
| Create fixup. |
| |
| Signed-off-by: Łukasz Bartosik <ukaszb@google.com> |
| Change-Id: I2cd096c1def691bdd72d8d2ed921210876662989 |
| |
| [rebase61(tzungbi): |
| Squashed: |
| FIXUP: CHROMIUM: Restrict swapon() to "zram" devices / lock down zram |
| ] |
| Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org> |
| |
| Kcr-patch: d03264366eed62cfb1d2fe7b03a54e96aaeb189e45d57aad7d659712.patch |
| Change-Id: If43bb7962765d22eb2ad7d378c4abb7d7d9941e9 |
| --- |
| drivers/block/zram/zram_drv.c | 10 ++++++++++ |
| include/linux/mm.h | 2 ++ |
| init/Kconfig | 9 +++++++++ |
| kernel/sysctl.c | 11 +++++++++++ |
| mm/swapfile.c | 35 ++++++++++++++++++++++++++++++----- |
| 5 files changed, 62 insertions(+), 5 deletions(-) |
| |
| diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c |
| index d77d3664ca080529e23783561d2a00c650be01b6..f8f9312b7378e8366c89f64a63ece638128a5829 100644 |
| --- a/drivers/block/zram/zram_drv.c |
| +++ b/drivers/block/zram/zram_drv.c |
| @@ -2108,6 +2108,16 @@ static int zram_open(struct gendisk *disk, blk_mode_t mode) |
| |
| WARN_ON(!mutex_is_locked(&disk->open_mutex)); |
| |
| + /* |
| + * Chromium OS specific behavior: |
| + * sys_swapon opens the device once to populate its swapinfo->swap_file |
| + * and once when it claims the block device (blkdev_get). By limiting |
| + * the maximum number of opens to 2, we ensure there are no prior open |
| + * references before swap is enabled. |
| + */ |
| + if (atomic_read(&bdev->bd_openers) > 1) |
| + return -EBUSY; |
| + |
| /* zram was claimed to reset so open request fails */ |
| if (zram->claim) |
| return -EBUSY; |
| diff --git a/include/linux/mm.h b/include/linux/mm.h |
| index 418d26608ece70d12a5608dff42f0f4d04af5aea..c92f9fb1cde916b2c7fcd935c81479ac3d3dabd2 100644 |
| --- a/include/linux/mm.h |
| +++ b/include/linux/mm.h |
| @@ -202,6 +202,8 @@ extern int sysctl_overcommit_memory; |
| extern int sysctl_overcommit_ratio; |
| extern unsigned long sysctl_overcommit_kbytes; |
| |
| +extern int sysctl_disk_based_swap; |
| + |
| int overcommit_ratio_handler(struct ctl_table *, int, void *, size_t *, |
| loff_t *); |
| int overcommit_kbytes_handler(struct ctl_table *, int, void *, size_t *, |
| diff --git a/init/Kconfig b/init/Kconfig |
| index 845fabc2d9e0699595a53657cb39352c05dd64f5..3e26a924831868bf5fd3bf77172b0b7fcfb74e28 100644 |
| --- a/init/Kconfig |
| +++ b/init/Kconfig |
| @@ -368,6 +368,15 @@ config DEFAULT_HOSTNAME |
| but you may wish to use a different default here to make a minimal |
| system more usable with less configuration. |
| |
| +config DISK_BASED_SWAP |
| + bool "Allow disk-based swap files in Chromium OS kernels" |
| + depends on SWAP |
| + default n |
| + help |
| + By default, the Chromium OS kernel allows swapping only to |
| + zram devices. This option allows you to use disk-based files |
| + as swap devices too. If unsure say N. |
| + |
| config SYSVIPC |
| bool "System V IPC" |
| help |
| diff --git a/kernel/sysctl.c b/kernel/sysctl.c |
| index 157f7ce2942d26a6401bc3c8f0dd2077ad69ff5d..8ef9b22dbdd37b951e217b42eac4be511c3cd869 100644 |
| --- a/kernel/sysctl.c |
| +++ b/kernel/sysctl.c |
| @@ -2239,6 +2239,17 @@ static struct ctl_table vm_table[] = { |
| .extra1 = (void *)&mmap_rnd_compat_bits_min, |
| .extra2 = (void *)&mmap_rnd_compat_bits_max, |
| }, |
| +#endif |
| +#ifdef CONFIG_DISK_BASED_SWAP |
| + { |
| + .procname = "disk_based_swap", |
| + .data = &sysctl_disk_based_swap, |
| + .maxlen = sizeof(sysctl_disk_based_swap), |
| + .mode = 0644, |
| + .proc_handler = proc_dointvec_minmax, |
| + .extra1 = SYSCTL_ZERO, |
| + .extra2 = SYSCTL_ONE, |
| + }, |
| #endif |
| { } |
| }; |
| diff --git a/mm/swapfile.c b/mm/swapfile.c |
| index 4bc70f459164147e260eeb3b669737b0df1a2b82..3871e7d8d25967f495cf491f24f95f3a911bd6f0 100644 |
| --- a/mm/swapfile.c |
| +++ b/mm/swapfile.c |
| @@ -2396,6 +2396,8 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) |
| struct filename *pathname; |
| int err, found = 0; |
| unsigned int old_block_size; |
| + struct path path_holder; |
| + struct path *victim_path = NULL; |
| |
| if (!capable(CAP_SYS_ADMIN)) |
| return -EPERM; |
| @@ -2408,10 +2410,16 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) |
| |
| victim = file_open_name(pathname, O_RDWR|O_LARGEFILE, 0); |
| err = PTR_ERR(victim); |
| - if (IS_ERR(victim)) |
| - goto out; |
| - |
| - mapping = victim->f_mapping; |
| + if (IS_ERR(victim)) { |
| + /* Fallback to just the inode mapping if possible. */ |
| + if (kern_path(pathname->name, LOOKUP_FOLLOW, &path_holder)) |
| + goto out; /* Propogate the original err. */ |
| + victim_path = &path_holder; |
| + mapping = victim_path->dentry->d_inode->i_mapping; |
| + victim = NULL; |
| + } else { |
| + mapping = victim->f_mapping; |
| + } |
| spin_lock(&swap_lock); |
| plist_for_each_entry(p, &swap_active_head, list) { |
| if (p->flags & SWP_WRITEOK) { |
| @@ -2555,7 +2563,10 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) |
| wake_up_interruptible(&proc_poll_wait); |
| |
| out_dput: |
| - filp_close(victim, NULL); |
| + if (victim) |
| + filp_close(victim, NULL); |
| + if (victim_path) |
| + path_put(victim_path); |
| out: |
| putname(pathname); |
| return err; |
| @@ -2758,11 +2769,17 @@ static struct swap_info_struct *alloc_swap_info(void) |
| return p; |
| } |
| |
| +/* This sysctl is only exposed when CONFIG_DISK_BASED_SWAP is enabled. */ |
| +int sysctl_disk_based_swap; |
| + |
| static int claim_swapfile(struct swap_info_struct *p, struct inode *inode) |
| { |
| int error; |
| |
| + /* On Chromium OS, we only support zram swap devices. */ |
| if (S_ISBLK(inode->i_mode)) { |
| + char name[BDEVNAME_SIZE]; |
| + |
| p->bdev_handle = bdev_open_by_dev(inode->i_rdev, |
| BLK_OPEN_READ | BLK_OPEN_WRITE, p, NULL); |
| if (IS_ERR(p->bdev_handle)) { |
| @@ -2770,6 +2787,12 @@ static int claim_swapfile(struct swap_info_struct *p, struct inode *inode) |
| p->bdev_handle = NULL; |
| return error; |
| } |
| + snprintf(name, sizeof(name), "%pg", p->bdev); |
| + if (strncmp(name, "zram", strlen("zram"))) { |
| + iput(p->bdev->bd_inode); |
| + p->bdev = NULL; |
| + return -EINVAL; |
| + } |
| p->bdev = p->bdev_handle->bdev; |
| p->old_block_size = block_size(p->bdev); |
| error = set_blocksize(p->bdev, PAGE_SIZE); |
| @@ -2784,6 +2807,8 @@ static int claim_swapfile(struct swap_info_struct *p, struct inode *inode) |
| return -EINVAL; |
| p->flags |= SWP_BLKDEV; |
| } else if (S_ISREG(inode->i_mode)) { |
| + if (!sysctl_disk_based_swap) |
| + return -EINVAL; |
| p->bdev = inode->i_sb->s_bdev; |
| } |
| |
| -- |
| 2.43.0.rc2.451.g8631bc7472-goog |
| |