Skip to content

Commit f5117ed

Browse files
Kefeng Wangintel-lab-lkp
authored andcommitted
mm: hwpoison: coredump: support recovery from dump_user_range()
The dump_user_range() is used to copy the user page to a coredump file, but if a hardware memory error occurred during copy, which called from __kernel_write_iter() in dump_user_range(), it crashs, CPU: 112 PID: 7014 Comm: mca-recover Not tainted 6.3.0-rc2 torvalds#425 pc : __memcpy+0x110/0x260 lr : _copy_from_iter+0x3bc/0x4c8 ... Call trace: __memcpy+0x110/0x260 copy_page_from_iter+0xcc/0x130 pipe_write+0x164/0x6d8 __kernel_write_iter+0x9c/0x210 dump_user_range+0xc8/0x1d8 elf_core_dump+0x308/0x368 do_coredump+0x2e8/0xa40 get_signal+0x59c/0x788 do_signal+0x118/0x1f8 do_notify_resume+0xf0/0x280 el0_da+0x130/0x138 el0t_64_sync_handler+0x68/0xc0 el0t_64_sync+0x188/0x190 Generally, the '->write_iter' of file ops will use copy_page_from_iter() and copy_page_from_iter_atomic(), change memcpy() to copy_mc_to_kernel() in both of them to handle #MC during source read, which stop coredump processing and kill the task instead of kernel panic, but the source address may not always an user address, so introduce a new copy_mc flag in struct iov_iter{} to indicate that the iter could do a safe memory copy, also introduce the helpers to set/clear/check the flag, for now, it's only used in coredump's dump_user_range(), but it could expand to any other scenarios to fix the similar issue. Signed-off-by: Kefeng Wang <[email protected]>
1 parent 7d8214b commit f5117ed

File tree

3 files changed

+43
-2
lines changed

3 files changed

+43
-2
lines changed

fs/coredump.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,9 @@ static int dump_emit_page(struct coredump_params *cprm, struct page *page)
882882
pos = file->f_pos;
883883
bvec_set_page(&bvec, page, PAGE_SIZE, 0);
884884
iov_iter_bvec(&iter, ITER_SOURCE, &bvec, 1, PAGE_SIZE);
885+
iov_iter_set_copy_mc(&iter);
885886
n = __kernel_write_iter(cprm->file, &iter, &pos);
887+
iov_iter_clear_copy_mc(&iter);
886888
if (n != PAGE_SIZE)
887889
return 0;
888890
file->f_pos = pos;

include/linux/uio.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct iov_iter {
4343
bool nofault;
4444
bool data_source;
4545
bool user_backed;
46+
bool copy_mc;
4647
size_t iov_offset;
4748
/*
4849
* Hack alert: overlay ubuf_iovec with iovec + count, so
@@ -142,6 +143,30 @@ static inline bool user_backed_iter(const struct iov_iter *i)
142143
return i->user_backed;
143144
}
144145

146+
#ifdef CONFIG_ARCH_HAS_COPY_MC
147+
static inline void iov_iter_set_copy_mc(struct iov_iter *i)
148+
{
149+
i->copy_mc = true;
150+
}
151+
152+
static inline void iov_iter_clear_copy_mc(struct iov_iter *i)
153+
{
154+
i->copy_mc = false;
155+
}
156+
157+
static inline bool iov_iter_is_copy_mc(const struct iov_iter *i)
158+
{
159+
return i->copy_mc;
160+
}
161+
#else
162+
static inline void iov_iter_set_copy_mc(struct iov_iter *i) { }
163+
static inline void iov_iter_clear_copy_mc(struct iov_iter *i) {}
164+
static inline bool iov_iter_is_copy_mc(const struct iov_iter *i)
165+
{
166+
return false;
167+
}
168+
#endif
169+
145170
/*
146171
* Total number of bytes covered by an iovec.
147172
*
@@ -359,6 +384,7 @@ static inline void iov_iter_ubuf(struct iov_iter *i, unsigned int direction,
359384
.iter_type = ITER_UBUF,
360385
.user_backed = true,
361386
.data_source = direction,
387+
.copy_mc = false,
362388
.ubuf = buf,
363389
.count = count,
364390
.nr_segs = 1

lib/iov_iter.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ void iov_iter_init(struct iov_iter *i, unsigned int direction,
291291
.nofault = false,
292292
.user_backed = true,
293293
.data_source = direction,
294+
.copy_mc = false,
294295
.__iov = iov,
295296
.nr_segs = nr_segs,
296297
.iov_offset = 0,
@@ -371,6 +372,14 @@ size_t _copy_mc_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
371372
EXPORT_SYMBOL_GPL(_copy_mc_to_iter);
372373
#endif /* CONFIG_ARCH_HAS_COPY_MC */
373374

375+
static void *memcpy_from_iter(struct iov_iter *i, void *to, const void *from,
376+
size_t size)
377+
{
378+
if (iov_iter_is_copy_mc(i))
379+
return (void *)copy_mc_to_kernel(to, from, size);
380+
return memcpy(to, from, size);
381+
}
382+
374383
size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
375384
{
376385
if (WARN_ON_ONCE(!i->data_source))
@@ -380,7 +389,7 @@ size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
380389
might_fault();
381390
iterate_and_advance(i, bytes, base, len, off,
382391
copyin(addr + off, base, len),
383-
memcpy(addr + off, base, len)
392+
memcpy_from_iter(i, addr + off, base, len)
384393
)
385394

386395
return bytes;
@@ -571,7 +580,7 @@ size_t copy_page_from_iter_atomic(struct page *page, unsigned offset, size_t byt
571580
}
572581
iterate_and_advance(i, bytes, base, len, off,
573582
copyin(p + off, base, len),
574-
memcpy(p + off, base, len)
583+
memcpy_from_iter(i, p + off, base, len)
575584
)
576585
kunmap_atomic(kaddr);
577586
return bytes;
@@ -705,6 +714,7 @@ void iov_iter_kvec(struct iov_iter *i, unsigned int direction,
705714
*i = (struct iov_iter){
706715
.iter_type = ITER_KVEC,
707716
.data_source = direction,
717+
.copy_mc = false,
708718
.kvec = kvec,
709719
.nr_segs = nr_segs,
710720
.iov_offset = 0,
@@ -721,6 +731,7 @@ void iov_iter_bvec(struct iov_iter *i, unsigned int direction,
721731
*i = (struct iov_iter){
722732
.iter_type = ITER_BVEC,
723733
.data_source = direction,
734+
.copy_mc = false,
724735
.bvec = bvec,
725736
.nr_segs = nr_segs,
726737
.iov_offset = 0,
@@ -749,6 +760,7 @@ void iov_iter_xarray(struct iov_iter *i, unsigned int direction,
749760
*i = (struct iov_iter) {
750761
.iter_type = ITER_XARRAY,
751762
.data_source = direction,
763+
.copy_mc = false,
752764
.xarray = xarray,
753765
.xarray_start = start,
754766
.count = count,
@@ -772,6 +784,7 @@ void iov_iter_discard(struct iov_iter *i, unsigned int direction, size_t count)
772784
*i = (struct iov_iter){
773785
.iter_type = ITER_DISCARD,
774786
.data_source = false,
787+
.copy_mc = false,
775788
.count = count,
776789
.iov_offset = 0
777790
};

0 commit comments

Comments
 (0)