Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <david@redhat.com>
Message-Id: <20241204125444.1734652-4-david@redhat.com>
Acked-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>

authored by

David Hildenbrand and committed by
Michael S. Tsirkin
0f3b1c40 2083dfe4

+34 -23
+34 -23
fs/proc/vmcore.c
··· 68 68 static LIST_HEAD(vmcore_cb_list); 69 69 /* Whether the vmcore has been opened once. */ 70 70 static bool vmcore_opened; 71 + /* Whether the vmcore is currently open. */ 72 + static unsigned int vmcore_open; 71 73 72 74 void register_vmcore_cb(struct vmcore_cb *cb) 73 75 { ··· 124 122 { 125 123 mutex_lock(&vmcore_mutex); 126 124 vmcore_opened = true; 125 + if (vmcore_open + 1 == 0) { 126 + mutex_unlock(&vmcore_mutex); 127 + return -EBUSY; 128 + } 129 + vmcore_open++; 130 + mutex_unlock(&vmcore_mutex); 131 + 132 + return 0; 133 + } 134 + 135 + static int release_vmcore(struct inode *inode, struct file *file) 136 + { 137 + mutex_lock(&vmcore_mutex); 138 + vmcore_open--; 127 139 mutex_unlock(&vmcore_mutex); 128 140 129 141 return 0; ··· 259 243 { 260 244 struct vmcoredd_node *dump; 261 245 u64 offset = 0; 262 - int ret = 0; 263 246 size_t tsz; 264 247 char *buf; 265 248 266 - mutex_lock(&vmcore_mutex); 267 249 list_for_each_entry(dump, &vmcoredd_list, list) { 268 250 if (start < offset + dump->size) { 269 251 tsz = min(offset + (u64)dump->size - start, (u64)size); 270 252 buf = dump->buf + start - offset; 271 - if (copy_to_iter(buf, tsz, iter) < tsz) { 272 - ret = -EFAULT; 273 - goto out_unlock; 274 - } 253 + if (copy_to_iter(buf, tsz, iter) < tsz) 254 + return -EFAULT; 275 255 276 256 size -= tsz; 277 257 start += tsz; 278 258 279 259 /* Leave now if buffer filled already */ 280 260 if (!size) 281 - goto out_unlock; 261 + return 0; 282 262 } 283 263 offset += dump->size; 284 264 } 285 265 286 - out_unlock: 287 - mutex_unlock(&vmcore_mutex); 288 - return ret; 266 + return 0; 289 267 } 290 268 291 269 #ifdef CONFIG_MMU ··· 288 278 { 289 279 struct vmcoredd_node *dump; 290 280 u64 offset = 0; 291 - int ret = 0; 292 281 size_t tsz; 293 282 char *buf; 294 283 295 - mutex_lock(&vmcore_mutex); 296 284 list_for_each_entry(dump, &vmcoredd_list, list) { 297 285 if (start < offset + dump->size) { 298 286 tsz = min(offset + (u64)dump->size - start, (u64)size); 299 287 buf = dump->buf + start - offset; 300 288 if (remap_vmalloc_range_partial(vma, dst, buf, 0, 301 - tsz)) { 302 - ret = -EFAULT; 303 - goto out_unlock; 304 - } 289 + tsz)) 290 + return -EFAULT; 305 291 306 292 size -= tsz; 307 293 start += tsz; ··· 305 299 306 300 /* Leave now if buffer filled already */ 307 301 if (!size) 308 - goto out_unlock; 302 + return 0; 309 303 } 310 304 offset += dump->size; 311 305 } 312 306 313 - out_unlock: 314 - mutex_unlock(&vmcore_mutex); 315 - return ret; 307 + return 0; 316 308 } 317 309 #endif /* CONFIG_MMU */ 318 310 #endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ ··· 695 691 696 692 static const struct proc_ops vmcore_proc_ops = { 697 693 .proc_open = open_vmcore, 694 + .proc_release = release_vmcore, 698 695 .proc_read_iter = read_vmcore, 699 696 .proc_lseek = default_llseek, 700 697 .proc_mmap = mmap_vmcore, ··· 1521 1516 dump->buf = buf; 1522 1517 dump->size = data_size; 1523 1518 1524 - /* Add the dump to driver sysfs list */ 1519 + /* Add the dump to driver sysfs list and update the elfcore hdr */ 1525 1520 mutex_lock(&vmcore_mutex); 1526 - list_add_tail(&dump->list, &vmcoredd_list); 1527 - mutex_unlock(&vmcore_mutex); 1521 + if (vmcore_opened) 1522 + pr_warn_once("Unexpected adding of device dump\n"); 1523 + if (vmcore_open) { 1524 + ret = -EBUSY; 1525 + goto out_err; 1526 + } 1528 1527 1528 + list_add_tail(&dump->list, &vmcoredd_list); 1529 1529 vmcoredd_update_size(data_size); 1530 + mutex_unlock(&vmcore_mutex); 1530 1531 return 0; 1531 1532 1532 1533 out_err: