Skip to content

Commit 0f3b1c4

Browse files
davidhildenbrandmstsirkin
authored andcommitted
fs/proc/vmcore: disallow vmcore modifications while the vmcore is open
The vmcoredd_update_size() call and its effects (size/offset changes) are currently completely unsynchronized, and will cause trouble when performed concurrently, or when done while someone is already reading the vmcore. Let's protect all vmcore modifications by the vmcore_mutex, disallow vmcore modifications while the vmcore is open, and warn on vmcore modifications after the vmcore was already opened once: modifications while the vmcore is open are unsafe, and modifications after the vmcore was opened indicates trouble. Properly synchronize against concurrent opening of the vmcore. No need to grab the mutex during mmap()/read(): after we opened the vmcore, modifications are impossible. It's worth noting that modifications after the vmcore was opened are completely unexpected, so failing if open, and warning if already opened (+closed again) is good enough. This change not only handles concurrent adding of device dumps + concurrent reading of the vmcore properly, it also prepares for other mechanisms that will modify the vmcore. Signed-off-by: David Hildenbrand <[email protected]> Message-Id: <[email protected]> Acked-by: Andrew Morton <[email protected]> Signed-off-by: Michael S. Tsirkin <[email protected]>
1 parent 2083dfe commit 0f3b1c4

File tree

1 file changed

+34
-23
lines changed

1 file changed

+34
-23
lines changed

fs/proc/vmcore.c

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ DEFINE_STATIC_SRCU(vmcore_cb_srcu);
6868
static LIST_HEAD(vmcore_cb_list);
6969
/* Whether the vmcore has been opened once. */
7070
static bool vmcore_opened;
71+
/* Whether the vmcore is currently open. */
72+
static unsigned int vmcore_open;
7173

7274
void register_vmcore_cb(struct vmcore_cb *cb)
7375
{
@@ -122,6 +124,20 @@ static int open_vmcore(struct inode *inode, struct file *file)
122124
{
123125
mutex_lock(&vmcore_mutex);
124126
vmcore_opened = true;
127+
if (vmcore_open + 1 == 0) {
128+
mutex_unlock(&vmcore_mutex);
129+
return -EBUSY;
130+
}
131+
vmcore_open++;
132+
mutex_unlock(&vmcore_mutex);
133+
134+
return 0;
135+
}
136+
137+
static int release_vmcore(struct inode *inode, struct file *file)
138+
{
139+
mutex_lock(&vmcore_mutex);
140+
vmcore_open--;
125141
mutex_unlock(&vmcore_mutex);
126142

127143
return 0;
@@ -243,33 +259,27 @@ static int vmcoredd_copy_dumps(struct iov_iter *iter, u64 start, size_t size)
243259
{
244260
struct vmcoredd_node *dump;
245261
u64 offset = 0;
246-
int ret = 0;
247262
size_t tsz;
248263
char *buf;
249264

250-
mutex_lock(&vmcore_mutex);
251265
list_for_each_entry(dump, &vmcoredd_list, list) {
252266
if (start < offset + dump->size) {
253267
tsz = min(offset + (u64)dump->size - start, (u64)size);
254268
buf = dump->buf + start - offset;
255-
if (copy_to_iter(buf, tsz, iter) < tsz) {
256-
ret = -EFAULT;
257-
goto out_unlock;
258-
}
269+
if (copy_to_iter(buf, tsz, iter) < tsz)
270+
return -EFAULT;
259271

260272
size -= tsz;
261273
start += tsz;
262274

263275
/* Leave now if buffer filled already */
264276
if (!size)
265-
goto out_unlock;
277+
return 0;
266278
}
267279
offset += dump->size;
268280
}
269281

270-
out_unlock:
271-
mutex_unlock(&vmcore_mutex);
272-
return ret;
282+
return 0;
273283
}
274284

275285
#ifdef CONFIG_MMU
@@ -278,35 +288,29 @@ static int vmcoredd_mmap_dumps(struct vm_area_struct *vma, unsigned long dst,
278288
{
279289
struct vmcoredd_node *dump;
280290
u64 offset = 0;
281-
int ret = 0;
282291
size_t tsz;
283292
char *buf;
284293

285-
mutex_lock(&vmcore_mutex);
286294
list_for_each_entry(dump, &vmcoredd_list, list) {
287295
if (start < offset + dump->size) {
288296
tsz = min(offset + (u64)dump->size - start, (u64)size);
289297
buf = dump->buf + start - offset;
290298
if (remap_vmalloc_range_partial(vma, dst, buf, 0,
291-
tsz)) {
292-
ret = -EFAULT;
293-
goto out_unlock;
294-
}
299+
tsz))
300+
return -EFAULT;
295301

296302
size -= tsz;
297303
start += tsz;
298304
dst += tsz;
299305

300306
/* Leave now if buffer filled already */
301307
if (!size)
302-
goto out_unlock;
308+
return 0;
303309
}
304310
offset += dump->size;
305311
}
306312

307-
out_unlock:
308-
mutex_unlock(&vmcore_mutex);
309-
return ret;
313+
return 0;
310314
}
311315
#endif /* CONFIG_MMU */
312316
#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
@@ -691,6 +695,7 @@ static int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
691695

692696
static const struct proc_ops vmcore_proc_ops = {
693697
.proc_open = open_vmcore,
698+
.proc_release = release_vmcore,
694699
.proc_read_iter = read_vmcore,
695700
.proc_lseek = default_llseek,
696701
.proc_mmap = mmap_vmcore,
@@ -1516,12 +1521,18 @@ int vmcore_add_device_dump(struct vmcoredd_data *data)
15161521
dump->buf = buf;
15171522
dump->size = data_size;
15181523

1519-
/* Add the dump to driver sysfs list */
1524+
/* Add the dump to driver sysfs list and update the elfcore hdr */
15201525
mutex_lock(&vmcore_mutex);
1521-
list_add_tail(&dump->list, &vmcoredd_list);
1522-
mutex_unlock(&vmcore_mutex);
1526+
if (vmcore_opened)
1527+
pr_warn_once("Unexpected adding of device dump\n");
1528+
if (vmcore_open) {
1529+
ret = -EBUSY;
1530+
goto out_err;
1531+
}
15231532

1533+
list_add_tail(&dump->list, &vmcoredd_list);
15241534
vmcoredd_update_size(data_size);
1535+
mutex_unlock(&vmcore_mutex);
15251536
return 0;
15261537

15271538
out_err:

0 commit comments

Comments
 (0)