]> git.proxmox.com Git - pve-kernel-jessie.git/blame - CVE-2016-4794-1-percpu-fix-synchronization-between-chunk-map_extend_.patch
add scheduler fix for ceph on numa hosts
[pve-kernel-jessie.git] / CVE-2016-4794-1-percpu-fix-synchronization-between-chunk-map_extend_.patch
CommitLineData
1e4df1fa
FG
1From 120f27d6c4ff44d31052fc74438efa64b361980a Mon Sep 17 00:00:00 2001
2From: Tejun Heo <tj@kernel.org>
3Date: Tue, 12 Jul 2016 17:03:15 +0100
4Subject: [PATCH 1/2] percpu: fix synchronization between
5 chunk->map_extend_work and chunk destruction
6
7Atomic allocations can trigger async map extensions which is serviced
8by chunk->map_extend_work. pcpu_balance_work which is responsible for
9destroying idle chunks wasn't synchronizing properly against
10chunk->map_extend_work and may end up freeing the chunk while the work
11item is still in flight.
12
13This patch fixes the bug by rolling async map extension operations
14into pcpu_balance_work.
15
16Signed-off-by: Tejun Heo <tj@kernel.org>
17Reported-and-tested-by: Alexei Starovoitov <alexei.starovoitov@gmail.com>
18Reported-by: Vlastimil Babka <vbabka@suse.cz>
19Reported-by: Sasha Levin <sasha.levin@oracle.com>
20Cc: stable@vger.kernel.org # v3.18+
21Fixes: 9c824b6a172c ("percpu: make sure chunk->map array has available space")
22(cherry picked from commit 4f996e234dad488e5d9ba0858bc1bae12eff82c3)
23CVE-2016-4794
24BugLink: https://bugs.launchpad.net/bugs/1581871
25Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
26Acked-by: Christopher Arges <chris.j.arges@canonical.com>
27Signed-off-by: Kamal Mostafa <kamal@canonical.com>
28---
29 mm/percpu.c | 57 ++++++++++++++++++++++++++++++++++++---------------------
30 1 file changed, 36 insertions(+), 21 deletions(-)
31
32diff --git a/mm/percpu.c b/mm/percpu.c
33index 8a943b9..58b0149 100644
34--- a/mm/percpu.c
35+++ b/mm/percpu.c
36@@ -110,7 +110,7 @@ struct pcpu_chunk {
37 int map_used; /* # of map entries used before the sentry */
38 int map_alloc; /* # of map entries allocated */
39 int *map; /* allocation map */
40- struct work_struct map_extend_work;/* async ->map[] extension */
41+ struct list_head map_extend_list;/* on pcpu_map_extend_chunks */
42
43 void *data; /* chunk data */
44 int first_free; /* no free below this */
45@@ -164,6 +164,9 @@ static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop */
46
47 static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
48
49+/* chunks which need their map areas extended, protected by pcpu_lock */
50+static LIST_HEAD(pcpu_map_extend_chunks);
51+
52 /*
53 * The number of empty populated pages, protected by pcpu_lock. The
54 * reserved chunk doesn't contribute to the count.
55@@ -397,13 +400,19 @@ static int pcpu_need_to_extend(struct pcpu_chunk *chunk, bool is_atomic)
56 {
57 int margin, new_alloc;
58
59+ lockdep_assert_held(&pcpu_lock);
60+
61 if (is_atomic) {
62 margin = 3;
63
64 if (chunk->map_alloc <
65- chunk->map_used + PCPU_ATOMIC_MAP_MARGIN_LOW &&
66- pcpu_async_enabled)
67- schedule_work(&chunk->map_extend_work);
68+ chunk->map_used + PCPU_ATOMIC_MAP_MARGIN_LOW) {
69+ if (list_empty(&chunk->map_extend_list)) {
70+ list_add_tail(&chunk->map_extend_list,
71+ &pcpu_map_extend_chunks);
72+ pcpu_schedule_balance_work();
73+ }
74+ }
75 } else {
76 margin = PCPU_ATOMIC_MAP_MARGIN_HIGH;
77 }
78@@ -469,20 +478,6 @@ out_unlock:
79 return 0;
80 }
81
82-static void pcpu_map_extend_workfn(struct work_struct *work)
83-{
84- struct pcpu_chunk *chunk = container_of(work, struct pcpu_chunk,
85- map_extend_work);
86- int new_alloc;
87-
88- spin_lock_irq(&pcpu_lock);
89- new_alloc = pcpu_need_to_extend(chunk, false);
90- spin_unlock_irq(&pcpu_lock);
91-
92- if (new_alloc)
93- pcpu_extend_area_map(chunk, new_alloc);
94-}
95-
96 /**
97 * pcpu_fit_in_area - try to fit the requested allocation in a candidate area
98 * @chunk: chunk the candidate area belongs to
99@@ -742,7 +737,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void)
100 chunk->map_used = 1;
101
102 INIT_LIST_HEAD(&chunk->list);
103- INIT_WORK(&chunk->map_extend_work, pcpu_map_extend_workfn);
104+ INIT_LIST_HEAD(&chunk->map_extend_list);
105 chunk->free_size = pcpu_unit_size;
106 chunk->contig_hint = pcpu_unit_size;
107
108@@ -1131,6 +1126,7 @@ static void pcpu_balance_workfn(struct work_struct *work)
109 if (chunk == list_first_entry(free_head, struct pcpu_chunk, list))
110 continue;
111
112+ list_del_init(&chunk->map_extend_list);
113 list_move(&chunk->list, &to_free);
114 }
115
116@@ -1148,6 +1144,25 @@ static void pcpu_balance_workfn(struct work_struct *work)
117 pcpu_destroy_chunk(chunk);
118 }
119
120+ /* service chunks which requested async area map extension */
121+ do {
122+ int new_alloc = 0;
123+
124+ spin_lock_irq(&pcpu_lock);
125+
126+ chunk = list_first_entry_or_null(&pcpu_map_extend_chunks,
127+ struct pcpu_chunk, map_extend_list);
128+ if (chunk) {
129+ list_del_init(&chunk->map_extend_list);
130+ new_alloc = pcpu_need_to_extend(chunk, false);
131+ }
132+
133+ spin_unlock_irq(&pcpu_lock);
134+
135+ if (new_alloc)
136+ pcpu_extend_area_map(chunk, new_alloc);
137+ } while (chunk);
138+
139 /*
140 * Ensure there are certain number of free populated pages for
141 * atomic allocs. Fill up from the most packed so that atomic
142@@ -1646,7 +1661,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
143 */
144 schunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0);
145 INIT_LIST_HEAD(&schunk->list);
146- INIT_WORK(&schunk->map_extend_work, pcpu_map_extend_workfn);
147+ INIT_LIST_HEAD(&schunk->map_extend_list);
148 schunk->base_addr = base_addr;
149 schunk->map = smap;
150 schunk->map_alloc = ARRAY_SIZE(smap);
151@@ -1675,7 +1690,7 @@ int __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai,
152 if (dyn_size) {
153 dchunk = memblock_virt_alloc(pcpu_chunk_struct_size, 0);
154 INIT_LIST_HEAD(&dchunk->list);
155- INIT_WORK(&dchunk->map_extend_work, pcpu_map_extend_workfn);
156+ INIT_LIST_HEAD(&dchunk->map_extend_list);
157 dchunk->base_addr = base_addr;
158 dchunk->map = dmap;
159 dchunk->map_alloc = ARRAY_SIZE(dmap);
160--
1612.1.4
162