Skip to content

Commit 429a22e

Browse files
thejhtorvalds
authored andcommitted
coredump: rework elf/elf_fdpic vma_dump_size() into common helper
At the moment, the binfmt_elf and binfmt_elf_fdpic code have slightly different code to figure out which VMAs should be dumped, and if so, whether the dump should contain the entire VMA or just its first page. Eliminate duplicate code by reworking the binfmt_elf version into a generic core dumping helper in coredump.c. As part of that, change the heuristic for detecting executable/library header pages to check whether the inode is executable instead of looking at the file mode. This is less problematic in terms of locking because it lets us avoid get_user() under the mmap_sem. (And arguably it looks nicer and makes more sense in generic code.) Adjust a little bit based on the binfmt_elf_fdpic version: ->anon_vma is only meaningful under CONFIG_MMU, otherwise we have to assume that the VMA has been written to. Suggested-by: Linus Torvalds <[email protected]> Signed-off-by: Jann Horn <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Acked-by: Linus Torvalds <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Alexander Viro <[email protected]> Cc: "Eric W . Biederman" <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Hugh Dickins <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent afc63a9 commit 429a22e

File tree

4 files changed

+106
-199
lines changed

4 files changed

+106
-199
lines changed

fs/binfmt_elf.c

Lines changed: 0 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,126 +1414,6 @@ static int load_elf_library(struct file *file)
14141414
* Jeremy Fitzhardinge <[email protected]>
14151415
*/
14161416

1417-
/*
1418-
* The purpose of always_dump_vma() is to make sure that special kernel mappings
1419-
* that are useful for post-mortem analysis are included in every core dump.
1420-
* In that way we ensure that the core dump is fully interpretable later
1421-
* without matching up the same kernel and hardware config to see what PC values
1422-
* meant. These special mappings include - vDSO, vsyscall, and other
1423-
* architecture specific mappings
1424-
*/
1425-
static bool always_dump_vma(struct vm_area_struct *vma)
1426-
{
1427-
/* Any vsyscall mappings? */
1428-
if (vma == get_gate_vma(vma->vm_mm))
1429-
return true;
1430-
1431-
/*
1432-
* Assume that all vmas with a .name op should always be dumped.
1433-
* If this changes, a new vm_ops field can easily be added.
1434-
*/
1435-
if (vma->vm_ops && vma->vm_ops->name && vma->vm_ops->name(vma))
1436-
return true;
1437-
1438-
/*
1439-
* arch_vma_name() returns non-NULL for special architecture mappings,
1440-
* such as vDSO sections.
1441-
*/
1442-
if (arch_vma_name(vma))
1443-
return true;
1444-
1445-
return false;
1446-
}
1447-
1448-
/*
1449-
* Decide what to dump of a segment, part, all or none.
1450-
*/
1451-
static unsigned long vma_dump_size(struct vm_area_struct *vma,
1452-
unsigned long mm_flags)
1453-
{
1454-
#define FILTER(type) (mm_flags & (1UL << MMF_DUMP_##type))
1455-
1456-
/* always dump the vdso and vsyscall sections */
1457-
if (always_dump_vma(vma))
1458-
goto whole;
1459-
1460-
if (vma->vm_flags & VM_DONTDUMP)
1461-
return 0;
1462-
1463-
/* support for DAX */
1464-
if (vma_is_dax(vma)) {
1465-
if ((vma->vm_flags & VM_SHARED) && FILTER(DAX_SHARED))
1466-
goto whole;
1467-
if (!(vma->vm_flags & VM_SHARED) && FILTER(DAX_PRIVATE))
1468-
goto whole;
1469-
return 0;
1470-
}
1471-
1472-
/* Hugetlb memory check */
1473-
if (is_vm_hugetlb_page(vma)) {
1474-
if ((vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_SHARED))
1475-
goto whole;
1476-
if (!(vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_PRIVATE))
1477-
goto whole;
1478-
return 0;
1479-
}
1480-
1481-
/* Do not dump I/O mapped devices or special mappings */
1482-
if (vma->vm_flags & VM_IO)
1483-
return 0;
1484-
1485-
/* By default, dump shared memory if mapped from an anonymous file. */
1486-
if (vma->vm_flags & VM_SHARED) {
1487-
if (file_inode(vma->vm_file)->i_nlink == 0 ?
1488-
FILTER(ANON_SHARED) : FILTER(MAPPED_SHARED))
1489-
goto whole;
1490-
return 0;
1491-
}
1492-
1493-
/* Dump segments that have been written to. */
1494-
if (vma->anon_vma && FILTER(ANON_PRIVATE))
1495-
goto whole;
1496-
if (vma->vm_file == NULL)
1497-
return 0;
1498-
1499-
if (FILTER(MAPPED_PRIVATE))
1500-
goto whole;
1501-
1502-
/*
1503-
* If this looks like the beginning of a DSO or executable mapping,
1504-
* check for an ELF header. If we find one, dump the first page to
1505-
* aid in determining what was mapped here.
1506-
*/
1507-
if (FILTER(ELF_HEADERS) &&
1508-
vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) {
1509-
u32 __user *header = (u32 __user *) vma->vm_start;
1510-
u32 word;
1511-
/*
1512-
* Doing it this way gets the constant folded by GCC.
1513-
*/
1514-
union {
1515-
u32 cmp;
1516-
char elfmag[SELFMAG];
1517-
} magic;
1518-
BUILD_BUG_ON(SELFMAG != sizeof word);
1519-
magic.elfmag[EI_MAG0] = ELFMAG0;
1520-
magic.elfmag[EI_MAG1] = ELFMAG1;
1521-
magic.elfmag[EI_MAG2] = ELFMAG2;
1522-
magic.elfmag[EI_MAG3] = ELFMAG3;
1523-
if (unlikely(get_user(word, header)))
1524-
word = 0;
1525-
if (word == magic.cmp)
1526-
return PAGE_SIZE;
1527-
}
1528-
1529-
#undef FILTER
1530-
1531-
return 0;
1532-
1533-
whole:
1534-
return vma->vm_end - vma->vm_start;
1535-
}
1536-
15371417
/* An ELF note in memory */
15381418
struct memelfnote
15391419
{

fs/binfmt_elf_fdpic.c

Lines changed: 4 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,76 +1215,6 @@ struct elf_prstatus_fdpic
12151215
int pr_fpvalid; /* True if math co-processor being used. */
12161216
};
12171217

1218-
/*
1219-
* Decide whether a segment is worth dumping; default is yes to be
1220-
* sure (missing info is worse than too much; etc).
1221-
* Personally I'd include everything, and use the coredump limit...
1222-
*
1223-
* I think we should skip something. But I am not sure how. H.J.
1224-
*/
1225-
static int maydump(struct vm_area_struct *vma, unsigned long mm_flags)
1226-
{
1227-
int dump_ok;
1228-
1229-
/* Do not dump I/O mapped devices or special mappings */
1230-
if (vma->vm_flags & VM_IO) {
1231-
kdcore("%08lx: %08lx: no (IO)", vma->vm_start, vma->vm_flags);
1232-
return 0;
1233-
}
1234-
1235-
/* If we may not read the contents, don't allow us to dump
1236-
* them either. "dump_write()" can't handle it anyway.
1237-
*/
1238-
if (!(vma->vm_flags & VM_READ)) {
1239-
kdcore("%08lx: %08lx: no (!read)", vma->vm_start, vma->vm_flags);
1240-
return 0;
1241-
}
1242-
1243-
/* support for DAX */
1244-
if (vma_is_dax(vma)) {
1245-
if (vma->vm_flags & VM_SHARED) {
1246-
dump_ok = test_bit(MMF_DUMP_DAX_SHARED, &mm_flags);
1247-
kdcore("%08lx: %08lx: %s (DAX shared)", vma->vm_start,
1248-
vma->vm_flags, dump_ok ? "yes" : "no");
1249-
} else {
1250-
dump_ok = test_bit(MMF_DUMP_DAX_PRIVATE, &mm_flags);
1251-
kdcore("%08lx: %08lx: %s (DAX private)", vma->vm_start,
1252-
vma->vm_flags, dump_ok ? "yes" : "no");
1253-
}
1254-
return dump_ok;
1255-
}
1256-
1257-
/* By default, dump shared memory if mapped from an anonymous file. */
1258-
if (vma->vm_flags & VM_SHARED) {
1259-
if (file_inode(vma->vm_file)->i_nlink == 0) {
1260-
dump_ok = test_bit(MMF_DUMP_ANON_SHARED, &mm_flags);
1261-
kdcore("%08lx: %08lx: %s (share)", vma->vm_start,
1262-
vma->vm_flags, dump_ok ? "yes" : "no");
1263-
return dump_ok;
1264-
}
1265-
1266-
dump_ok = test_bit(MMF_DUMP_MAPPED_SHARED, &mm_flags);
1267-
kdcore("%08lx: %08lx: %s (share)", vma->vm_start,
1268-
vma->vm_flags, dump_ok ? "yes" : "no");
1269-
return dump_ok;
1270-
}
1271-
1272-
#ifdef CONFIG_MMU
1273-
/* By default, if it hasn't been written to, don't write it out */
1274-
if (!vma->anon_vma) {
1275-
dump_ok = test_bit(MMF_DUMP_MAPPED_PRIVATE, &mm_flags);
1276-
kdcore("%08lx: %08lx: %s (!anon)", vma->vm_start,
1277-
vma->vm_flags, dump_ok ? "yes" : "no");
1278-
return dump_ok;
1279-
}
1280-
#endif
1281-
1282-
dump_ok = test_bit(MMF_DUMP_ANON_PRIVATE, &mm_flags);
1283-
kdcore("%08lx: %08lx: %s", vma->vm_start, vma->vm_flags,
1284-
dump_ok ? "yes" : "no");
1285-
return dump_ok;
1286-
}
1287-
12881218
/* An ELF note in memory */
12891219
struct memelfnote
12901220
{
@@ -1529,13 +1459,9 @@ static bool elf_fdpic_dump_segments(struct coredump_params *cprm)
15291459
struct vm_area_struct *vma;
15301460

15311461
for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
1532-
unsigned long addr;
1533-
1534-
if (!maydump(vma, cprm->mm_flags))
1535-
continue;
1462+
unsigned long size = vma_dump_size(vma, cprm->mm_flags);
15361463

1537-
if (!dump_user_range(cprm, vma->vm_start,
1538-
vma->vma_end - vma->vm_start))
1464+
if (!dump_user_range(cprm, vma->vm_start, size))
15391465
return false;
15401466
}
15411467
return true;
@@ -1547,8 +1473,7 @@ static size_t elf_core_vma_data_size(unsigned long mm_flags)
15471473
size_t size = 0;
15481474

15491475
for (vma = current->mm->mmap; vma; vma = vma->vm_next)
1550-
if (maydump(vma, mm_flags))
1551-
size += vma->vm_end - vma->vm_start;
1476+
size += vma_dump_size(vma, mm_flags);
15521477
return size;
15531478
}
15541479

@@ -1694,7 +1619,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
16941619
phdr.p_offset = offset;
16951620
phdr.p_vaddr = vma->vm_start;
16961621
phdr.p_paddr = 0;
1697-
phdr.p_filesz = maydump(vma, cprm->mm_flags) ? sz : 0;
1622+
phdr.p_filesz = vma_dump_size(vma, cprm->mm_flags);
16981623
phdr.p_memsz = sz;
16991624
offset += phdr.p_filesz;
17001625
phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;

fs/coredump.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,3 +936,104 @@ void dump_truncate(struct coredump_params *cprm)
936936
}
937937
}
938938
EXPORT_SYMBOL(dump_truncate);
939+
940+
/*
941+
* The purpose of always_dump_vma() is to make sure that special kernel mappings
942+
* that are useful for post-mortem analysis are included in every core dump.
943+
* In that way we ensure that the core dump is fully interpretable later
944+
* without matching up the same kernel and hardware config to see what PC values
945+
* meant. These special mappings include - vDSO, vsyscall, and other
946+
* architecture specific mappings
947+
*/
948+
static bool always_dump_vma(struct vm_area_struct *vma)
949+
{
950+
/* Any vsyscall mappings? */
951+
if (vma == get_gate_vma(vma->vm_mm))
952+
return true;
953+
954+
/*
955+
* Assume that all vmas with a .name op should always be dumped.
956+
* If this changes, a new vm_ops field can easily be added.
957+
*/
958+
if (vma->vm_ops && vma->vm_ops->name && vma->vm_ops->name(vma))
959+
return true;
960+
961+
/*
962+
* arch_vma_name() returns non-NULL for special architecture mappings,
963+
* such as vDSO sections.
964+
*/
965+
if (arch_vma_name(vma))
966+
return true;
967+
968+
return false;
969+
}
970+
971+
/*
972+
* Decide how much of @vma's contents should be included in a core dump.
973+
*/
974+
unsigned long vma_dump_size(struct vm_area_struct *vma, unsigned long mm_flags)
975+
{
976+
#define FILTER(type) (mm_flags & (1UL << MMF_DUMP_##type))
977+
978+
/* always dump the vdso and vsyscall sections */
979+
if (always_dump_vma(vma))
980+
goto whole;
981+
982+
if (vma->vm_flags & VM_DONTDUMP)
983+
return 0;
984+
985+
/* support for DAX */
986+
if (vma_is_dax(vma)) {
987+
if ((vma->vm_flags & VM_SHARED) && FILTER(DAX_SHARED))
988+
goto whole;
989+
if (!(vma->vm_flags & VM_SHARED) && FILTER(DAX_PRIVATE))
990+
goto whole;
991+
return 0;
992+
}
993+
994+
/* Hugetlb memory check */
995+
if (is_vm_hugetlb_page(vma)) {
996+
if ((vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_SHARED))
997+
goto whole;
998+
if (!(vma->vm_flags & VM_SHARED) && FILTER(HUGETLB_PRIVATE))
999+
goto whole;
1000+
return 0;
1001+
}
1002+
1003+
/* Do not dump I/O mapped devices or special mappings */
1004+
if (vma->vm_flags & VM_IO)
1005+
return 0;
1006+
1007+
/* By default, dump shared memory if mapped from an anonymous file. */
1008+
if (vma->vm_flags & VM_SHARED) {
1009+
if (file_inode(vma->vm_file)->i_nlink == 0 ?
1010+
FILTER(ANON_SHARED) : FILTER(MAPPED_SHARED))
1011+
goto whole;
1012+
return 0;
1013+
}
1014+
1015+
/* Dump segments that have been written to. */
1016+
if ((!IS_ENABLED(CONFIG_MMU) || vma->anon_vma) && FILTER(ANON_PRIVATE))
1017+
goto whole;
1018+
if (vma->vm_file == NULL)
1019+
return 0;
1020+
1021+
if (FILTER(MAPPED_PRIVATE))
1022+
goto whole;
1023+
1024+
/*
1025+
* If this is the beginning of an executable file mapping,
1026+
* dump the first page to aid in determining what was mapped here.
1027+
*/
1028+
if (FILTER(ELF_HEADERS) &&
1029+
vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ) &&
1030+
(READ_ONCE(file_inode(vma->vm_file)->i_mode) & 0111) != 0)
1031+
return PAGE_SIZE;
1032+
1033+
#undef FILTER
1034+
1035+
return 0;
1036+
1037+
whole:
1038+
return vma->vm_end - vma->vm_start;
1039+
}

include/linux/coredump.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern int dump_skip(struct coredump_params *cprm, size_t nr);
1616
extern int dump_emit(struct coredump_params *cprm, const void *addr, int nr);
1717
extern int dump_align(struct coredump_params *cprm, int align);
1818
extern void dump_truncate(struct coredump_params *cprm);
19+
unsigned long vma_dump_size(struct vm_area_struct *vma, unsigned long mm_flags);
1920
int dump_user_range(struct coredump_params *cprm, unsigned long start,
2021
unsigned long len);
2122
#ifdef CONFIG_COREDUMP

0 commit comments

Comments
 (0)