When duplicating a process' virtual memory mappings also duplicate all of its registered .sframe sections stored in the per-mm maple tree to enable stacktracing using sframe of the child process. Signed-off-by: Jens Remus --- Notes (jremus): Changes in v16: - dup_mmap(): Drop unnecessary CONFIG_HAVE_UNWIND_USER_SFRAME #ifdefs. (Sashiko AI) - dup_mmap(): Call sframe_dup_mm() prior to arch_dup_mmap(), so that comes last. Changes in v15: - New patch. include/linux/sframe.h | 5 ++++ kernel/unwind/sframe.c | 48 ++++++++++++++++++++++++++++++++++++ kernel/unwind/sframe_debug.h | 7 ++++++ mm/mmap.c | 5 ++++ 4 files changed, 65 insertions(+) diff --git a/include/linux/sframe.h b/include/linux/sframe.h index b79c5ec09229..91889b4fe3dd 100644 --- a/include/linux/sframe.h +++ b/include/linux/sframe.h @@ -28,6 +28,7 @@ struct sframe_section { }; #define INIT_MM_SFRAME .sframe_mt = MTREE_INIT(sframe_mt, 0), +extern int sframe_dup_mm(struct mm_struct *mm, struct mm_struct *oldmm); extern void sframe_free_mm(struct mm_struct *mm); extern int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, @@ -45,6 +46,10 @@ static inline bool current_has_sframe(void) #else /* !CONFIG_HAVE_UNWIND_USER_SFRAME */ #define INIT_MM_SFRAME +static inline int sframe_dup_mm(struct mm_struct *mm, struct mm_struct *oldmm) +{ + return 0; +} static inline void sframe_free_mm(struct mm_struct *mm) {} static inline int sframe_add_section(unsigned long sframe_start, unsigned long sframe_end, unsigned long text_start, unsigned long text_end) diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c index 7f439600b0f0..db88d993dff1 100644 --- a/kernel/unwind/sframe.c +++ b/kernel/unwind/sframe.c @@ -875,6 +875,54 @@ int sframe_remove_section(unsigned long sframe_start) return 0; } +static void __sframe_dup_section(struct sframe_section *sec, struct sframe_section *oldsec) +{ + sec->sframe_start = oldsec->sframe_start; + sec->sframe_end = oldsec->sframe_end; + sec->text_start = oldsec->text_start; + sec->text_end = oldsec->text_end; + + sec->fdes_start = oldsec->fdes_start; + sec->fres_start = oldsec->fres_start; + sec->fres_end = oldsec->fres_end; + sec->num_fdes = oldsec->num_fdes; + + sec->ra_off = oldsec->ra_off; + sec->fp_off = oldsec->fp_off; + + dbg_dup(sec, oldsec); +} + +int sframe_dup_mm(struct mm_struct *mm, struct mm_struct *oldmm) +{ + struct sframe_section *sec, *oldsec; + unsigned long index = 0; + int ret; + + guard(srcu)(&sframe_srcu); + + mt_for_each(&oldmm->sframe_mt, oldsec, index, ULONG_MAX) { + sec = kzalloc(sizeof(*sec), GFP_KERNEL_ACCOUNT); + if (!sec) + return -ENOMEM; + + __sframe_dup_section(sec, oldsec); + + ret = mtree_insert_range(&mm->sframe_mt, + sec->text_start, + sec->text_end - 1, + sec, GFP_KERNEL_ACCOUNT); + if (ret) + goto err_free; + } + + return 0; + +err_free: + free_section(sec); + return ret; +} + void sframe_free_mm(struct mm_struct *mm) { struct sframe_section *sec; diff --git a/kernel/unwind/sframe_debug.h b/kernel/unwind/sframe_debug.h index a63e75cccc70..2503972155e8 100644 --- a/kernel/unwind/sframe_debug.h +++ b/kernel/unwind/sframe_debug.h @@ -48,6 +48,12 @@ static inline void dbg_init(struct sframe_section *sec) sec->filename = kstrdup("(anonymous)", GFP_KERNEL_ACCOUNT); } +static inline void dbg_dup(struct sframe_section *sec, struct sframe_section *oldsec) +{ + if (oldsec->filename) + sec->filename = kstrdup(oldsec->filename, GFP_KERNEL_ACCOUNT); +} + static inline void dbg_free(struct sframe_section *sec) { kfree(sec->filename); @@ -61,6 +67,7 @@ static inline void dbg_free(struct sframe_section *sec) static inline void dbg_print_header(struct sframe_section *sec) {} static inline void dbg_init(struct sframe_section *sec) {} +static inline void dbg_dup(struct sframe_section *sec, struct sframe_section *oldsec) {} static inline void dbg_free(struct sframe_section *sec) {} #endif /* !CONFIG_DYNAMIC_DEBUG */ diff --git a/mm/mmap.c b/mm/mmap.c index 5754d1c36462..8715be691488 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -1844,6 +1845,9 @@ __latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) goto loop_out; } } + retval = sframe_dup_mm(mm, oldmm); + if (retval) + goto loop_out; /* a new mm has just been created */ retval = arch_dup_mmap(oldmm, mm); loop_out: @@ -1893,6 +1897,7 @@ __latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) vm_unacct_memory(charge); } __mt_destroy(&mm->mm_mt); + sframe_free_mm(mm); /* * The mm_struct is going to exit, but the locks will be dropped * first. Set the mm_struct as unstable is advisable as it is -- 2.51.0