diff options
author | Csaba Henk <csaba@gluster.com> | 2009-08-26 13:17:22 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2009-09-16 08:15:29 -0400 |
commit | 487ea5af6358cb27c994e2cf056d4ee0872e43c3 (patch) | |
tree | 26db6260934ad1743db6da6319cb7a502dd775e2 /fs | |
parent | d6db07ded51c5fb4df2f4a32e6a41e9bb5db7fc4 (diff) |
fuse: limit user-specified values of max background requests
An untrusted user could DoS the system if s/he were allowed to accumulate an
arbitrary number of pending background requests by setting the above limits
to extremely high values in INIT. This patch excludes this possibility by
imposing global upper limits on the possible values of per-mount "max
background requests" and "congestion threshold" parameters for unprivileged
FUSE filesystems.
These global limits are implemented as module parameters.
Signed-off-by: Csaba Henk <csaba@gluster.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fuse/inode.c | 78 |
1 files changed, 72 insertions, 6 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 9aa6f46d0c32..e54ddbda208c 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/seq_file.h> | 14 | #include <linux/seq_file.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/moduleparam.h> | ||
17 | #include <linux/parser.h> | 18 | #include <linux/parser.h> |
18 | #include <linux/statfs.h> | 19 | #include <linux/statfs.h> |
19 | #include <linux/random.h> | 20 | #include <linux/random.h> |
@@ -28,6 +29,24 @@ static struct kmem_cache *fuse_inode_cachep; | |||
28 | struct list_head fuse_conn_list; | 29 | struct list_head fuse_conn_list; |
29 | DEFINE_MUTEX(fuse_mutex); | 30 | DEFINE_MUTEX(fuse_mutex); |
30 | 31 | ||
32 | static int set_global_limit(const char *val, struct kernel_param *kp); | ||
33 | |||
34 | static unsigned max_user_bgreq; | ||
35 | module_param_call(max_user_bgreq, set_global_limit, param_get_uint, | ||
36 | &max_user_bgreq, 0644); | ||
37 | __MODULE_PARM_TYPE(max_user_bgreq, "uint"); | ||
38 | MODULE_PARM_DESC(max_user_bgreq, | ||
39 | "Global limit for the maximum number of backgrounded requests an " | ||
40 | "unprivileged user can set"); | ||
41 | |||
42 | static unsigned max_user_congthresh; | ||
43 | module_param_call(max_user_congthresh, set_global_limit, param_get_uint, | ||
44 | &max_user_congthresh, 0644); | ||
45 | __MODULE_PARM_TYPE(max_user_congthresh, "uint"); | ||
46 | MODULE_PARM_DESC(max_user_congthresh, | ||
47 | "Global limit for the maximum congestion threshold an " | ||
48 | "unprivileged user can set"); | ||
49 | |||
31 | #define FUSE_SUPER_MAGIC 0x65735546 | 50 | #define FUSE_SUPER_MAGIC 0x65735546 |
32 | 51 | ||
33 | #define FUSE_DEFAULT_BLKSIZE 512 | 52 | #define FUSE_DEFAULT_BLKSIZE 512 |
@@ -735,6 +754,54 @@ static const struct super_operations fuse_super_operations = { | |||
735 | .show_options = fuse_show_options, | 754 | .show_options = fuse_show_options, |
736 | }; | 755 | }; |
737 | 756 | ||
757 | static void sanitize_global_limit(unsigned *limit) | ||
758 | { | ||
759 | if (*limit == 0) | ||
760 | *limit = ((num_physpages << PAGE_SHIFT) >> 13) / | ||
761 | sizeof(struct fuse_req); | ||
762 | |||
763 | if (*limit >= 1 << 16) | ||
764 | *limit = (1 << 16) - 1; | ||
765 | } | ||
766 | |||
767 | static int set_global_limit(const char *val, struct kernel_param *kp) | ||
768 | { | ||
769 | int rv; | ||
770 | |||
771 | rv = param_set_uint(val, kp); | ||
772 | if (rv) | ||
773 | return rv; | ||
774 | |||
775 | sanitize_global_limit((unsigned *)kp->arg); | ||
776 | |||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | static void process_init_limits(struct fuse_conn *fc, struct fuse_init_out *arg) | ||
781 | { | ||
782 | int cap_sys_admin = capable(CAP_SYS_ADMIN); | ||
783 | |||
784 | if (arg->minor < 13) | ||
785 | return; | ||
786 | |||
787 | sanitize_global_limit(&max_user_bgreq); | ||
788 | sanitize_global_limit(&max_user_congthresh); | ||
789 | |||
790 | if (arg->max_background) { | ||
791 | fc->max_background = arg->max_background; | ||
792 | |||
793 | if (!cap_sys_admin && fc->max_background > max_user_bgreq) | ||
794 | fc->max_background = max_user_bgreq; | ||
795 | } | ||
796 | if (arg->congestion_threshold) { | ||
797 | fc->congestion_threshold = arg->congestion_threshold; | ||
798 | |||
799 | if (!cap_sys_admin && | ||
800 | fc->congestion_threshold > max_user_congthresh) | ||
801 | fc->congestion_threshold = max_user_congthresh; | ||
802 | } | ||
803 | } | ||
804 | |||
738 | static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | 805 | static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) |
739 | { | 806 | { |
740 | struct fuse_init_out *arg = &req->misc.init_out; | 807 | struct fuse_init_out *arg = &req->misc.init_out; |
@@ -744,12 +811,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | |||
744 | else { | 811 | else { |
745 | unsigned long ra_pages; | 812 | unsigned long ra_pages; |
746 | 813 | ||
747 | if (arg->minor >= 13) { | 814 | process_init_limits(fc, arg); |
748 | if (arg->max_background) | 815 | |
749 | fc->max_background = arg->max_background; | ||
750 | if (arg->congestion_threshold) | ||
751 | fc->congestion_threshold = arg->congestion_threshold; | ||
752 | } | ||
753 | if (arg->minor >= 6) { | 816 | if (arg->minor >= 6) { |
754 | ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; | 817 | ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; |
755 | if (arg->flags & FUSE_ASYNC_READ) | 818 | if (arg->flags & FUSE_ASYNC_READ) |
@@ -1161,6 +1224,9 @@ static int __init fuse_init(void) | |||
1161 | if (res) | 1224 | if (res) |
1162 | goto err_sysfs_cleanup; | 1225 | goto err_sysfs_cleanup; |
1163 | 1226 | ||
1227 | sanitize_global_limit(&max_user_bgreq); | ||
1228 | sanitize_global_limit(&max_user_congthresh); | ||
1229 | |||
1164 | return 0; | 1230 | return 0; |
1165 | 1231 | ||
1166 | err_sysfs_cleanup: | 1232 | err_sysfs_cleanup: |