]>
Commit | Line | Data |
---|---|---|
b75945de GKH |
1 | From 6d69bb536bac0d403d83db1ca841444981b280cd Mon Sep 17 00:00:00 2001 |
2 | From: Ilya Dryomov <idryomov@gmail.com> | |
3 | Date: Sun, 11 Oct 2015 19:38:00 +0200 | |
4 | Subject: rbd: prevent kernel stack blow up on rbd map | |
5 | ||
6 | From: Ilya Dryomov <idryomov@gmail.com> | |
7 | ||
8 | commit 6d69bb536bac0d403d83db1ca841444981b280cd upstream. | |
9 | ||
10 | Mapping an image with a long parent chain (e.g. image foo, whose parent | |
11 | is bar, whose parent is baz, etc) currently leads to a kernel stack | |
12 | overflow, due to the following recursion in the reply path: | |
13 | ||
14 | rbd_osd_req_callback() | |
15 | rbd_obj_request_complete() | |
16 | rbd_img_obj_callback() | |
17 | rbd_img_parent_read_callback() | |
18 | rbd_obj_request_complete() | |
19 | ... | |
20 | ||
21 | Limit the parent chain to 16 images, which is ~5K worth of stack. When | |
22 | the above recursion is eliminated, this limit can be lifted. | |
23 | ||
24 | Fixes: http://tracker.ceph.com/issues/12538 | |
25 | ||
26 | Signed-off-by: Ilya Dryomov <idryomov@gmail.com> | |
27 | Reviewed-by: Josh Durgin <jdurgin@redhat.com> | |
28 | [idryomov@gmail.com: backport to 3.14: rbd_dev->opts, context] | |
29 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
30 | ||
31 | --- | |
32 | drivers/block/rbd.c | 29 +++++++++++++++++++++-------- | |
33 | 1 file changed, 21 insertions(+), 8 deletions(-) | |
34 | ||
35 | --- a/drivers/block/rbd.c | |
36 | +++ b/drivers/block/rbd.c | |
37 | @@ -94,6 +94,8 @@ static int atomic_dec_return_safe(atomic | |
38 | #define RBD_MINORS_PER_MAJOR 256 | |
39 | #define RBD_SINGLE_MAJOR_PART_SHIFT 4 | |
40 | ||
41 | +#define RBD_MAX_PARENT_CHAIN_LEN 16 | |
42 | + | |
43 | #define RBD_SNAP_DEV_NAME_PREFIX "snap_" | |
44 | #define RBD_MAX_SNAP_NAME_LEN \ | |
45 | (NAME_MAX - (sizeof (RBD_SNAP_DEV_NAME_PREFIX) - 1)) | |
46 | @@ -411,7 +413,7 @@ static ssize_t rbd_add_single_major(stru | |
47 | size_t count); | |
48 | static ssize_t rbd_remove_single_major(struct bus_type *bus, const char *buf, | |
49 | size_t count); | |
50 | -static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping); | |
51 | +static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth); | |
52 | static void rbd_spec_put(struct rbd_spec *spec); | |
53 | ||
54 | static int rbd_dev_id_to_minor(int dev_id) | |
55 | @@ -4822,7 +4824,12 @@ out_err: | |
56 | return ret; | |
57 | } | |
58 | ||
59 | -static int rbd_dev_probe_parent(struct rbd_device *rbd_dev) | |
60 | +/* | |
61 | + * @depth is rbd_dev_image_probe() -> rbd_dev_probe_parent() -> | |
62 | + * rbd_dev_image_probe() recursion depth, which means it's also the | |
63 | + * length of the already discovered part of the parent chain. | |
64 | + */ | |
65 | +static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth) | |
66 | { | |
67 | struct rbd_device *parent = NULL; | |
68 | int ret; | |
69 | @@ -4830,6 +4837,12 @@ static int rbd_dev_probe_parent(struct r | |
70 | if (!rbd_dev->parent_spec) | |
71 | return 0; | |
72 | ||
73 | + if (++depth > RBD_MAX_PARENT_CHAIN_LEN) { | |
74 | + pr_info("parent chain is too long (%d)\n", depth); | |
75 | + ret = -EINVAL; | |
76 | + goto out_err; | |
77 | + } | |
78 | + | |
79 | parent = rbd_dev_create(rbd_dev->rbd_client, rbd_dev->parent_spec); | |
80 | if (!parent) { | |
81 | ret = -ENOMEM; | |
82 | @@ -4843,7 +4856,7 @@ static int rbd_dev_probe_parent(struct r | |
83 | __rbd_get_client(rbd_dev->rbd_client); | |
84 | rbd_spec_get(rbd_dev->parent_spec); | |
85 | ||
86 | - ret = rbd_dev_image_probe(parent, false); | |
87 | + ret = rbd_dev_image_probe(parent, depth); | |
88 | if (ret < 0) | |
89 | goto out_err; | |
90 | ||
91 | @@ -4970,7 +4983,7 @@ static void rbd_dev_image_release(struct | |
92 | * parent), initiate a watch on its header object before using that | |
93 | * object to get detailed information about the rbd image. | |
94 | */ | |
95 | -static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) | |
96 | +static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) | |
97 | { | |
98 | int ret; | |
99 | ||
100 | @@ -4990,7 +5003,7 @@ static int rbd_dev_image_probe(struct rb | |
101 | if (ret) | |
102 | goto err_out_format; | |
103 | ||
104 | - if (mapping) { | |
105 | + if (!depth) { | |
106 | ret = rbd_dev_header_watch_sync(rbd_dev); | |
107 | if (ret) | |
108 | goto out_header_name; | |
109 | @@ -5007,7 +5020,7 @@ static int rbd_dev_image_probe(struct rb | |
110 | if (ret) | |
111 | goto err_out_probe; | |
112 | ||
113 | - ret = rbd_dev_probe_parent(rbd_dev); | |
114 | + ret = rbd_dev_probe_parent(rbd_dev, depth); | |
115 | if (ret) | |
116 | goto err_out_probe; | |
117 | ||
118 | @@ -5018,7 +5031,7 @@ static int rbd_dev_image_probe(struct rb | |
119 | err_out_probe: | |
120 | rbd_dev_unprobe(rbd_dev); | |
121 | err_out_watch: | |
122 | - if (mapping) | |
123 | + if (!depth) | |
124 | rbd_dev_header_unwatch_sync(rbd_dev); | |
125 | out_header_name: | |
126 | kfree(rbd_dev->header_name); | |
127 | @@ -5085,7 +5098,7 @@ static ssize_t do_rbd_add(struct bus_typ | |
128 | rbdc = NULL; /* rbd_dev now owns this */ | |
129 | spec = NULL; /* rbd_dev now owns this */ | |
130 | ||
131 | - rc = rbd_dev_image_probe(rbd_dev, true); | |
132 | + rc = rbd_dev_image_probe(rbd_dev, 0); | |
133 | if (rc < 0) | |
134 | goto err_out_rbd_dev; | |
135 |