diff options
author | Alexei Starovoitov <ast@plumgrid.com> | 2015-01-22 20:11:08 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-01-26 20:20:40 -0500 |
commit | 8ebe667c41e054384df19f2f382bc415badfaee1 (patch) | |
tree | 45c41bc68790f63aba66c5ab65492a89423587db /kernel/bpf | |
parent | 600ddd6825543962fb807884169e57b580dba208 (diff) |
bpf: rcu lock must not be held when calling copy_to_user()
BUG: sleeping function called from invalid context at mm/memory.c:3732
in_atomic(): 0, irqs_disabled(): 0, pid: 671, name: test_maps
1 lock held by test_maps/671:
#0: (rcu_read_lock){......}, at: [<0000000000264190>] map_lookup_elem+0xe8/0x260
Call Trace:
([<0000000000115b7e>] show_trace+0x12e/0x150)
[<0000000000115c40>] show_stack+0xa0/0x100
[<00000000009b163c>] dump_stack+0x74/0xc8
[<000000000017424a>] ___might_sleep+0x23a/0x248
[<00000000002b58e8>] might_fault+0x70/0xe8
[<0000000000264230>] map_lookup_elem+0x188/0x260
[<0000000000264716>] SyS_bpf+0x20e/0x840
Fix it by allocating temporary buffer to store map element value.
Fixes: db20fd2b0108 ("bpf: add lookup/update/delete/iterate methods to BPF maps")
Reported-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Acked-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/bpf')
-rw-r--r-- | kernel/bpf/syscall.c | 25 |
1 files changed, 17 insertions, 8 deletions
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 088ac0b1b106..536edc2be307 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
@@ -150,7 +150,7 @@ static int map_lookup_elem(union bpf_attr *attr) | |||
150 | int ufd = attr->map_fd; | 150 | int ufd = attr->map_fd; |
151 | struct fd f = fdget(ufd); | 151 | struct fd f = fdget(ufd); |
152 | struct bpf_map *map; | 152 | struct bpf_map *map; |
153 | void *key, *value; | 153 | void *key, *value, *ptr; |
154 | int err; | 154 | int err; |
155 | 155 | ||
156 | if (CHECK_ATTR(BPF_MAP_LOOKUP_ELEM)) | 156 | if (CHECK_ATTR(BPF_MAP_LOOKUP_ELEM)) |
@@ -169,20 +169,29 @@ static int map_lookup_elem(union bpf_attr *attr) | |||
169 | if (copy_from_user(key, ukey, map->key_size) != 0) | 169 | if (copy_from_user(key, ukey, map->key_size) != 0) |
170 | goto free_key; | 170 | goto free_key; |
171 | 171 | ||
172 | err = -ENOENT; | 172 | err = -ENOMEM; |
173 | rcu_read_lock(); | 173 | value = kmalloc(map->value_size, GFP_USER); |
174 | value = map->ops->map_lookup_elem(map, key); | ||
175 | if (!value) | 174 | if (!value) |
176 | goto err_unlock; | 175 | goto free_key; |
176 | |||
177 | rcu_read_lock(); | ||
178 | ptr = map->ops->map_lookup_elem(map, key); | ||
179 | if (ptr) | ||
180 | memcpy(value, ptr, map->value_size); | ||
181 | rcu_read_unlock(); | ||
182 | |||
183 | err = -ENOENT; | ||
184 | if (!ptr) | ||
185 | goto free_value; | ||
177 | 186 | ||
178 | err = -EFAULT; | 187 | err = -EFAULT; |
179 | if (copy_to_user(uvalue, value, map->value_size) != 0) | 188 | if (copy_to_user(uvalue, value, map->value_size) != 0) |
180 | goto err_unlock; | 189 | goto free_value; |
181 | 190 | ||
182 | err = 0; | 191 | err = 0; |
183 | 192 | ||
184 | err_unlock: | 193 | free_value: |
185 | rcu_read_unlock(); | 194 | kfree(value); |
186 | free_key: | 195 | free_key: |
187 | kfree(key); | 196 | kfree(key); |
188 | err_put: | 197 | err_put: |