| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* binfmt_elf_fdpic.c: FDPIC ELF binary format |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved. |
|---|
| 4 | 5 | * Written by David Howells (dhowells@redhat.com) |
|---|
| 5 | 6 | * Derived from binfmt_elf.c |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or |
|---|
| 8 | | - * modify it under the terms of the GNU General Public License |
|---|
| 9 | | - * as published by the Free Software Foundation; either version |
|---|
| 10 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 39 | 35 | #include <linux/elfcore.h> |
|---|
| 40 | 36 | #include <linux/coredump.h> |
|---|
| 41 | 37 | #include <linux/dax.h> |
|---|
| 38 | +#include <linux/regset.h> |
|---|
| 42 | 39 | |
|---|
| 43 | 40 | #include <linux/uaccess.h> |
|---|
| 44 | 41 | #include <asm/param.h> |
|---|
| 45 | | -#include <asm/pgalloc.h> |
|---|
| 46 | 42 | |
|---|
| 47 | 43 | typedef char *elf_caddr_t; |
|---|
| 48 | 44 | |
|---|
| .. | .. |
|---|
| 342 | 338 | interp_params.flags |= ELF_FDPIC_FLAG_CONSTDISP; |
|---|
| 343 | 339 | |
|---|
| 344 | 340 | /* flush all traces of the currently running executable */ |
|---|
| 345 | | - retval = flush_old_exec(bprm); |
|---|
| 341 | + retval = begin_new_exec(bprm); |
|---|
| 346 | 342 | if (retval) |
|---|
| 347 | 343 | goto error; |
|---|
| 348 | 344 | |
|---|
| 349 | 345 | /* there's now no turning back... the old userspace image is dead, |
|---|
| 350 | 346 | * defunct, deceased, etc. |
|---|
| 351 | 347 | */ |
|---|
| 348 | + SET_PERSONALITY(exec_params.hdr); |
|---|
| 352 | 349 | if (elf_check_fdpic(&exec_params.hdr)) |
|---|
| 353 | | - set_personality(PER_LINUX_FDPIC); |
|---|
| 354 | | - else |
|---|
| 355 | | - set_personality(PER_LINUX); |
|---|
| 350 | + current->personality |= PER_LINUX_FDPIC; |
|---|
| 356 | 351 | if (elf_read_implies_exec(&exec_params.hdr, executable_stack)) |
|---|
| 357 | 352 | current->personality |= READ_IMPLIES_EXEC; |
|---|
| 358 | 353 | |
|---|
| .. | .. |
|---|
| 438 | 433 | current->mm->start_stack = current->mm->start_brk + stack_size; |
|---|
| 439 | 434 | #endif |
|---|
| 440 | 435 | |
|---|
| 441 | | - install_exec_creds(bprm); |
|---|
| 442 | | - if (create_elf_fdpic_tables(bprm, current->mm, |
|---|
| 443 | | - &exec_params, &interp_params) < 0) |
|---|
| 436 | + retval = create_elf_fdpic_tables(bprm, current->mm, &exec_params, |
|---|
| 437 | + &interp_params); |
|---|
| 438 | + if (retval < 0) |
|---|
| 444 | 439 | goto error; |
|---|
| 445 | 440 | |
|---|
| 446 | 441 | kdebug("- start_code %lx", current->mm->start_code); |
|---|
| .. | .. |
|---|
| 541 | 536 | platform_len = strlen(k_platform) + 1; |
|---|
| 542 | 537 | sp -= platform_len; |
|---|
| 543 | 538 | u_platform = (char __user *) sp; |
|---|
| 544 | | - if (__copy_to_user(u_platform, k_platform, platform_len) != 0) |
|---|
| 539 | + if (copy_to_user(u_platform, k_platform, platform_len) != 0) |
|---|
| 545 | 540 | return -EFAULT; |
|---|
| 546 | 541 | } |
|---|
| 547 | 542 | |
|---|
| .. | .. |
|---|
| 556 | 551 | platform_len = strlen(k_base_platform) + 1; |
|---|
| 557 | 552 | sp -= platform_len; |
|---|
| 558 | 553 | u_base_platform = (char __user *) sp; |
|---|
| 559 | | - if (__copy_to_user(u_base_platform, k_base_platform, platform_len) != 0) |
|---|
| 554 | + if (copy_to_user(u_base_platform, k_base_platform, platform_len) != 0) |
|---|
| 560 | 555 | return -EFAULT; |
|---|
| 561 | 556 | } |
|---|
| 562 | 557 | |
|---|
| .. | .. |
|---|
| 593 | 588 | nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0) + |
|---|
| 594 | 589 | (k_base_platform ? 1 : 0) + AT_VECTOR_SIZE_ARCH; |
|---|
| 595 | 590 | |
|---|
| 596 | | - if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) |
|---|
| 591 | + if (bprm->have_execfd) |
|---|
| 597 | 592 | nitems++; |
|---|
| 598 | 593 | |
|---|
| 599 | 594 | csp = sp; |
|---|
| .. | .. |
|---|
| 608 | 603 | /* put the ELF interpreter info on the stack */ |
|---|
| 609 | 604 | #define NEW_AUX_ENT(id, val) \ |
|---|
| 610 | 605 | do { \ |
|---|
| 611 | | - struct { unsigned long _id, _val; } __user *ent; \ |
|---|
| 606 | + struct { unsigned long _id, _val; } __user *ent, v; \ |
|---|
| 612 | 607 | \ |
|---|
| 613 | 608 | ent = (void __user *) csp; \ |
|---|
| 614 | | - __put_user((id), &ent[nr]._id); \ |
|---|
| 615 | | - __put_user((val), &ent[nr]._val); \ |
|---|
| 609 | + v._id = (id); \ |
|---|
| 610 | + v._val = (val); \ |
|---|
| 611 | + if (copy_to_user(ent + nr, &v, sizeof(v))) \ |
|---|
| 612 | + return -EFAULT; \ |
|---|
| 616 | 613 | nr++; \ |
|---|
| 617 | 614 | } while (0) |
|---|
| 618 | 615 | |
|---|
| .. | .. |
|---|
| 633 | 630 | (elf_addr_t) (unsigned long) u_base_platform); |
|---|
| 634 | 631 | } |
|---|
| 635 | 632 | |
|---|
| 636 | | - if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) { |
|---|
| 633 | + if (bprm->have_execfd) { |
|---|
| 637 | 634 | nr = 0; |
|---|
| 638 | 635 | csp -= 2 * sizeof(unsigned long); |
|---|
| 639 | | - NEW_AUX_ENT(AT_EXECFD, bprm->interp_data); |
|---|
| 636 | + NEW_AUX_ENT(AT_EXECFD, bprm->execfd); |
|---|
| 640 | 637 | } |
|---|
| 641 | 638 | |
|---|
| 642 | 639 | nr = 0; |
|---|
| .. | .. |
|---|
| 679 | 676 | |
|---|
| 680 | 677 | /* stack argc */ |
|---|
| 681 | 678 | csp -= sizeof(unsigned long); |
|---|
| 682 | | - __put_user(bprm->argc, (unsigned long __user *) csp); |
|---|
| 679 | + if (put_user(bprm->argc, (unsigned long __user *) csp)) |
|---|
| 680 | + return -EFAULT; |
|---|
| 683 | 681 | |
|---|
| 684 | 682 | BUG_ON(csp != sp); |
|---|
| 685 | 683 | |
|---|
| .. | .. |
|---|
| 693 | 691 | |
|---|
| 694 | 692 | p = (char __user *) current->mm->arg_start; |
|---|
| 695 | 693 | for (loop = bprm->argc; loop > 0; loop--) { |
|---|
| 696 | | - __put_user((elf_caddr_t) p, argv++); |
|---|
| 694 | + if (put_user((elf_caddr_t) p, argv++)) |
|---|
| 695 | + return -EFAULT; |
|---|
| 697 | 696 | len = strnlen_user(p, MAX_ARG_STRLEN); |
|---|
| 698 | 697 | if (!len || len > MAX_ARG_STRLEN) |
|---|
| 699 | 698 | return -EINVAL; |
|---|
| 700 | 699 | p += len; |
|---|
| 701 | 700 | } |
|---|
| 702 | | - __put_user(NULL, argv); |
|---|
| 701 | + if (put_user(NULL, argv)) |
|---|
| 702 | + return -EFAULT; |
|---|
| 703 | 703 | current->mm->arg_end = (unsigned long) p; |
|---|
| 704 | 704 | |
|---|
| 705 | 705 | /* fill in the envv[] array */ |
|---|
| 706 | 706 | current->mm->env_start = (unsigned long) p; |
|---|
| 707 | 707 | for (loop = bprm->envc; loop > 0; loop--) { |
|---|
| 708 | | - __put_user((elf_caddr_t)(unsigned long) p, envp++); |
|---|
| 708 | + if (put_user((elf_caddr_t)(unsigned long) p, envp++)) |
|---|
| 709 | + return -EFAULT; |
|---|
| 709 | 710 | len = strnlen_user(p, MAX_ARG_STRLEN); |
|---|
| 710 | 711 | if (!len || len > MAX_ARG_STRLEN) |
|---|
| 711 | 712 | return -EINVAL; |
|---|
| 712 | 713 | p += len; |
|---|
| 713 | 714 | } |
|---|
| 714 | | - __put_user(NULL, envp); |
|---|
| 715 | + if (put_user(NULL, envp)) |
|---|
| 716 | + return -EFAULT; |
|---|
| 715 | 717 | current->mm->env_end = (unsigned long) p; |
|---|
| 716 | 718 | |
|---|
| 717 | 719 | mm->start_stack = (unsigned long) sp; |
|---|
| .. | .. |
|---|
| 853 | 855 | |
|---|
| 854 | 856 | tmp = phdr->p_memsz / sizeof(Elf32_Dyn); |
|---|
| 855 | 857 | dyn = (Elf32_Dyn __user *)params->dynamic_addr; |
|---|
| 856 | | - __get_user(d_tag, &dyn[tmp - 1].d_tag); |
|---|
| 857 | | - if (d_tag != 0) |
|---|
| 858 | + if (get_user(d_tag, &dyn[tmp - 1].d_tag) || |
|---|
| 859 | + d_tag != 0) |
|---|
| 858 | 860 | goto dynamic_error; |
|---|
| 859 | 861 | break; |
|---|
| 860 | 862 | } |
|---|
| .. | .. |
|---|
| 1187 | 1189 | */ |
|---|
| 1188 | 1190 | #ifdef CONFIG_ELF_CORE |
|---|
| 1189 | 1191 | |
|---|
| 1190 | | -/* |
|---|
| 1191 | | - * Decide whether a segment is worth dumping; default is yes to be |
|---|
| 1192 | | - * sure (missing info is worse than too much; etc). |
|---|
| 1193 | | - * Personally I'd include everything, and use the coredump limit... |
|---|
| 1194 | | - * |
|---|
| 1195 | | - * I think we should skip something. But I am not sure how. H.J. |
|---|
| 1196 | | - */ |
|---|
| 1197 | | -static int maydump(struct vm_area_struct *vma, unsigned long mm_flags) |
|---|
| 1192 | +struct elf_prstatus_fdpic |
|---|
| 1198 | 1193 | { |
|---|
| 1199 | | - int dump_ok; |
|---|
| 1200 | | - |
|---|
| 1201 | | - /* Do not dump I/O mapped devices or special mappings */ |
|---|
| 1202 | | - if (vma->vm_flags & VM_IO) { |
|---|
| 1203 | | - kdcore("%08lx: %08lx: no (IO)", vma->vm_start, vma->vm_flags); |
|---|
| 1204 | | - return 0; |
|---|
| 1205 | | - } |
|---|
| 1206 | | - |
|---|
| 1207 | | - /* If we may not read the contents, don't allow us to dump |
|---|
| 1208 | | - * them either. "dump_write()" can't handle it anyway. |
|---|
| 1194 | + struct elf_siginfo pr_info; /* Info associated with signal */ |
|---|
| 1195 | + short pr_cursig; /* Current signal */ |
|---|
| 1196 | + unsigned long pr_sigpend; /* Set of pending signals */ |
|---|
| 1197 | + unsigned long pr_sighold; /* Set of held signals */ |
|---|
| 1198 | + pid_t pr_pid; |
|---|
| 1199 | + pid_t pr_ppid; |
|---|
| 1200 | + pid_t pr_pgrp; |
|---|
| 1201 | + pid_t pr_sid; |
|---|
| 1202 | + struct __kernel_old_timeval pr_utime; /* User time */ |
|---|
| 1203 | + struct __kernel_old_timeval pr_stime; /* System time */ |
|---|
| 1204 | + struct __kernel_old_timeval pr_cutime; /* Cumulative user time */ |
|---|
| 1205 | + struct __kernel_old_timeval pr_cstime; /* Cumulative system time */ |
|---|
| 1206 | + elf_gregset_t pr_reg; /* GP registers */ |
|---|
| 1207 | + /* When using FDPIC, the loadmap addresses need to be communicated |
|---|
| 1208 | + * to GDB in order for GDB to do the necessary relocations. The |
|---|
| 1209 | + * fields (below) used to communicate this information are placed |
|---|
| 1210 | + * immediately after ``pr_reg'', so that the loadmap addresses may |
|---|
| 1211 | + * be viewed as part of the register set if so desired. |
|---|
| 1209 | 1212 | */ |
|---|
| 1210 | | - if (!(vma->vm_flags & VM_READ)) { |
|---|
| 1211 | | - kdcore("%08lx: %08lx: no (!read)", vma->vm_start, vma->vm_flags); |
|---|
| 1212 | | - return 0; |
|---|
| 1213 | | - } |
|---|
| 1214 | | - |
|---|
| 1215 | | - /* support for DAX */ |
|---|
| 1216 | | - if (vma_is_dax(vma)) { |
|---|
| 1217 | | - if (vma->vm_flags & VM_SHARED) { |
|---|
| 1218 | | - dump_ok = test_bit(MMF_DUMP_DAX_SHARED, &mm_flags); |
|---|
| 1219 | | - kdcore("%08lx: %08lx: %s (DAX shared)", vma->vm_start, |
|---|
| 1220 | | - vma->vm_flags, dump_ok ? "yes" : "no"); |
|---|
| 1221 | | - } else { |
|---|
| 1222 | | - dump_ok = test_bit(MMF_DUMP_DAX_PRIVATE, &mm_flags); |
|---|
| 1223 | | - kdcore("%08lx: %08lx: %s (DAX private)", vma->vm_start, |
|---|
| 1224 | | - vma->vm_flags, dump_ok ? "yes" : "no"); |
|---|
| 1225 | | - } |
|---|
| 1226 | | - return dump_ok; |
|---|
| 1227 | | - } |
|---|
| 1228 | | - |
|---|
| 1229 | | - /* By default, dump shared memory if mapped from an anonymous file. */ |
|---|
| 1230 | | - if (vma->vm_flags & VM_SHARED) { |
|---|
| 1231 | | - if (file_inode(vma->vm_file)->i_nlink == 0) { |
|---|
| 1232 | | - dump_ok = test_bit(MMF_DUMP_ANON_SHARED, &mm_flags); |
|---|
| 1233 | | - kdcore("%08lx: %08lx: %s (share)", vma->vm_start, |
|---|
| 1234 | | - vma->vm_flags, dump_ok ? "yes" : "no"); |
|---|
| 1235 | | - return dump_ok; |
|---|
| 1236 | | - } |
|---|
| 1237 | | - |
|---|
| 1238 | | - dump_ok = test_bit(MMF_DUMP_MAPPED_SHARED, &mm_flags); |
|---|
| 1239 | | - kdcore("%08lx: %08lx: %s (share)", vma->vm_start, |
|---|
| 1240 | | - vma->vm_flags, dump_ok ? "yes" : "no"); |
|---|
| 1241 | | - return dump_ok; |
|---|
| 1242 | | - } |
|---|
| 1243 | | - |
|---|
| 1244 | | -#ifdef CONFIG_MMU |
|---|
| 1245 | | - /* By default, if it hasn't been written to, don't write it out */ |
|---|
| 1246 | | - if (!vma->anon_vma) { |
|---|
| 1247 | | - dump_ok = test_bit(MMF_DUMP_MAPPED_PRIVATE, &mm_flags); |
|---|
| 1248 | | - kdcore("%08lx: %08lx: %s (!anon)", vma->vm_start, |
|---|
| 1249 | | - vma->vm_flags, dump_ok ? "yes" : "no"); |
|---|
| 1250 | | - return dump_ok; |
|---|
| 1251 | | - } |
|---|
| 1252 | | -#endif |
|---|
| 1253 | | - |
|---|
| 1254 | | - dump_ok = test_bit(MMF_DUMP_ANON_PRIVATE, &mm_flags); |
|---|
| 1255 | | - kdcore("%08lx: %08lx: %s", vma->vm_start, vma->vm_flags, |
|---|
| 1256 | | - dump_ok ? "yes" : "no"); |
|---|
| 1257 | | - return dump_ok; |
|---|
| 1258 | | -} |
|---|
| 1213 | + unsigned long pr_exec_fdpic_loadmap; |
|---|
| 1214 | + unsigned long pr_interp_fdpic_loadmap; |
|---|
| 1215 | + int pr_fpvalid; /* True if math co-processor being used. */ |
|---|
| 1216 | +}; |
|---|
| 1259 | 1217 | |
|---|
| 1260 | 1218 | /* An ELF note in memory */ |
|---|
| 1261 | 1219 | struct memelfnote |
|---|
| .. | .. |
|---|
| 1343 | 1301 | * fill up all the fields in prstatus from the given task struct, except |
|---|
| 1344 | 1302 | * registers which need to be filled up separately. |
|---|
| 1345 | 1303 | */ |
|---|
| 1346 | | -static void fill_prstatus(struct elf_prstatus *prstatus, |
|---|
| 1304 | +static void fill_prstatus(struct elf_prstatus_fdpic *prstatus, |
|---|
| 1347 | 1305 | struct task_struct *p, long signr) |
|---|
| 1348 | 1306 | { |
|---|
| 1349 | 1307 | prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; |
|---|
| .. | .. |
|---|
| 1363 | 1321 | * group-wide total, not its individual thread total. |
|---|
| 1364 | 1322 | */ |
|---|
| 1365 | 1323 | thread_group_cputime(p, &cputime); |
|---|
| 1366 | | - prstatus->pr_utime = ns_to_timeval(cputime.utime); |
|---|
| 1367 | | - prstatus->pr_stime = ns_to_timeval(cputime.stime); |
|---|
| 1324 | + prstatus->pr_utime = ns_to_kernel_old_timeval(cputime.utime); |
|---|
| 1325 | + prstatus->pr_stime = ns_to_kernel_old_timeval(cputime.stime); |
|---|
| 1368 | 1326 | } else { |
|---|
| 1369 | 1327 | u64 utime, stime; |
|---|
| 1370 | 1328 | |
|---|
| 1371 | 1329 | task_cputime(p, &utime, &stime); |
|---|
| 1372 | | - prstatus->pr_utime = ns_to_timeval(utime); |
|---|
| 1373 | | - prstatus->pr_stime = ns_to_timeval(stime); |
|---|
| 1330 | + prstatus->pr_utime = ns_to_kernel_old_timeval(utime); |
|---|
| 1331 | + prstatus->pr_stime = ns_to_kernel_old_timeval(stime); |
|---|
| 1374 | 1332 | } |
|---|
| 1375 | | - prstatus->pr_cutime = ns_to_timeval(p->signal->cutime); |
|---|
| 1376 | | - prstatus->pr_cstime = ns_to_timeval(p->signal->cstime); |
|---|
| 1333 | + prstatus->pr_cutime = ns_to_kernel_old_timeval(p->signal->cutime); |
|---|
| 1334 | + prstatus->pr_cstime = ns_to_kernel_old_timeval(p->signal->cstime); |
|---|
| 1377 | 1335 | |
|---|
| 1378 | 1336 | prstatus->pr_exec_fdpic_loadmap = p->mm->context.exec_fdpic_loadmap; |
|---|
| 1379 | 1337 | prstatus->pr_interp_fdpic_loadmap = p->mm->context.interp_fdpic_loadmap; |
|---|
| .. | .. |
|---|
| 1425 | 1383 | /* Here is the structure in which status of each thread is captured. */ |
|---|
| 1426 | 1384 | struct elf_thread_status |
|---|
| 1427 | 1385 | { |
|---|
| 1428 | | - struct list_head list; |
|---|
| 1429 | | - struct elf_prstatus prstatus; /* NT_PRSTATUS */ |
|---|
| 1386 | + struct elf_thread_status *next; |
|---|
| 1387 | + struct elf_prstatus_fdpic prstatus; /* NT_PRSTATUS */ |
|---|
| 1430 | 1388 | elf_fpregset_t fpu; /* NT_PRFPREG */ |
|---|
| 1431 | | - struct task_struct *thread; |
|---|
| 1432 | | -#ifdef ELF_CORE_COPY_XFPREGS |
|---|
| 1433 | | - elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ |
|---|
| 1434 | | -#endif |
|---|
| 1435 | | - struct memelfnote notes[3]; |
|---|
| 1389 | + struct memelfnote notes[2]; |
|---|
| 1436 | 1390 | int num_notes; |
|---|
| 1437 | 1391 | }; |
|---|
| 1438 | 1392 | |
|---|
| .. | .. |
|---|
| 1441 | 1395 | * we need to keep a linked list of every thread's pr_status and then create |
|---|
| 1442 | 1396 | * a single section for them in the final core file. |
|---|
| 1443 | 1397 | */ |
|---|
| 1444 | | -static int elf_dump_thread_status(long signr, struct elf_thread_status *t) |
|---|
| 1398 | +static struct elf_thread_status *elf_dump_thread_status(long signr, struct task_struct *p, int *sz) |
|---|
| 1445 | 1399 | { |
|---|
| 1446 | | - struct task_struct *p = t->thread; |
|---|
| 1447 | | - int sz = 0; |
|---|
| 1400 | + const struct user_regset_view *view = task_user_regset_view(p); |
|---|
| 1401 | + struct elf_thread_status *t; |
|---|
| 1402 | + int i, ret; |
|---|
| 1448 | 1403 | |
|---|
| 1449 | | - t->num_notes = 0; |
|---|
| 1404 | + t = kzalloc(sizeof(struct elf_thread_status), GFP_KERNEL); |
|---|
| 1405 | + if (!t) |
|---|
| 1406 | + return t; |
|---|
| 1450 | 1407 | |
|---|
| 1451 | 1408 | fill_prstatus(&t->prstatus, p, signr); |
|---|
| 1452 | | - elf_core_copy_task_regs(p, &t->prstatus.pr_reg); |
|---|
| 1409 | + regset_get(p, &view->regsets[0], |
|---|
| 1410 | + sizeof(t->prstatus.pr_reg), &t->prstatus.pr_reg); |
|---|
| 1453 | 1411 | |
|---|
| 1454 | 1412 | fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus), |
|---|
| 1455 | 1413 | &t->prstatus); |
|---|
| 1456 | 1414 | t->num_notes++; |
|---|
| 1457 | | - sz += notesize(&t->notes[0]); |
|---|
| 1415 | + *sz += notesize(&t->notes[0]); |
|---|
| 1458 | 1416 | |
|---|
| 1459 | | - t->prstatus.pr_fpvalid = elf_core_copy_task_fpregs(p, NULL, &t->fpu); |
|---|
| 1417 | + for (i = 1; i < view->n; ++i) { |
|---|
| 1418 | + const struct user_regset *regset = &view->regsets[i]; |
|---|
| 1419 | + if (regset->core_note_type != NT_PRFPREG) |
|---|
| 1420 | + continue; |
|---|
| 1421 | + if (regset->active && regset->active(p, regset) <= 0) |
|---|
| 1422 | + continue; |
|---|
| 1423 | + ret = regset_get(p, regset, sizeof(t->fpu), &t->fpu); |
|---|
| 1424 | + if (ret >= 0) |
|---|
| 1425 | + t->prstatus.pr_fpvalid = 1; |
|---|
| 1426 | + break; |
|---|
| 1427 | + } |
|---|
| 1428 | + |
|---|
| 1460 | 1429 | if (t->prstatus.pr_fpvalid) { |
|---|
| 1461 | 1430 | fill_note(&t->notes[1], "CORE", NT_PRFPREG, sizeof(t->fpu), |
|---|
| 1462 | 1431 | &t->fpu); |
|---|
| 1463 | 1432 | t->num_notes++; |
|---|
| 1464 | | - sz += notesize(&t->notes[1]); |
|---|
| 1433 | + *sz += notesize(&t->notes[1]); |
|---|
| 1465 | 1434 | } |
|---|
| 1466 | | - |
|---|
| 1467 | | -#ifdef ELF_CORE_COPY_XFPREGS |
|---|
| 1468 | | - if (elf_core_copy_task_xfpregs(p, &t->xfpu)) { |
|---|
| 1469 | | - fill_note(&t->notes[2], "LINUX", ELF_CORE_XFPREG_TYPE, |
|---|
| 1470 | | - sizeof(t->xfpu), &t->xfpu); |
|---|
| 1471 | | - t->num_notes++; |
|---|
| 1472 | | - sz += notesize(&t->notes[2]); |
|---|
| 1473 | | - } |
|---|
| 1474 | | -#endif |
|---|
| 1475 | | - return sz; |
|---|
| 1435 | + return t; |
|---|
| 1476 | 1436 | } |
|---|
| 1477 | 1437 | |
|---|
| 1478 | 1438 | static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum, |
|---|
| .. | .. |
|---|
| 1494 | 1454 | /* |
|---|
| 1495 | 1455 | * dump the segments for an MMU process |
|---|
| 1496 | 1456 | */ |
|---|
| 1497 | | -static bool elf_fdpic_dump_segments(struct coredump_params *cprm) |
|---|
| 1457 | +static bool elf_fdpic_dump_segments(struct coredump_params *cprm, |
|---|
| 1458 | + struct core_vma_metadata *vma_meta, |
|---|
| 1459 | + int vma_count) |
|---|
| 1498 | 1460 | { |
|---|
| 1499 | | - struct vm_area_struct *vma; |
|---|
| 1461 | + int i; |
|---|
| 1500 | 1462 | |
|---|
| 1501 | | - for (vma = current->mm->mmap; vma; vma = vma->vm_next) { |
|---|
| 1502 | | -#ifdef CONFIG_MMU |
|---|
| 1503 | | - unsigned long addr; |
|---|
| 1504 | | -#endif |
|---|
| 1463 | + for (i = 0; i < vma_count; i++) { |
|---|
| 1464 | + struct core_vma_metadata *meta = vma_meta + i; |
|---|
| 1505 | 1465 | |
|---|
| 1506 | | - if (!maydump(vma, cprm->mm_flags)) |
|---|
| 1507 | | - continue; |
|---|
| 1508 | | - |
|---|
| 1509 | | -#ifdef CONFIG_MMU |
|---|
| 1510 | | - for (addr = vma->vm_start; addr < vma->vm_end; |
|---|
| 1511 | | - addr += PAGE_SIZE) { |
|---|
| 1512 | | - bool res; |
|---|
| 1513 | | - struct page *page = get_dump_page(addr); |
|---|
| 1514 | | - if (page) { |
|---|
| 1515 | | - void *kaddr = kmap(page); |
|---|
| 1516 | | - res = dump_emit(cprm, kaddr, PAGE_SIZE); |
|---|
| 1517 | | - kunmap(page); |
|---|
| 1518 | | - put_page(page); |
|---|
| 1519 | | - } else { |
|---|
| 1520 | | - res = dump_skip(cprm, PAGE_SIZE); |
|---|
| 1521 | | - } |
|---|
| 1522 | | - if (!res) |
|---|
| 1523 | | - return false; |
|---|
| 1524 | | - } |
|---|
| 1525 | | -#else |
|---|
| 1526 | | - if (!dump_emit(cprm, (void *) vma->vm_start, |
|---|
| 1527 | | - vma->vm_end - vma->vm_start)) |
|---|
| 1466 | + if (!dump_user_range(cprm, meta->start, meta->dump_size)) |
|---|
| 1528 | 1467 | return false; |
|---|
| 1529 | | -#endif |
|---|
| 1530 | 1468 | } |
|---|
| 1531 | 1469 | return true; |
|---|
| 1532 | | -} |
|---|
| 1533 | | - |
|---|
| 1534 | | -static size_t elf_core_vma_data_size(unsigned long mm_flags) |
|---|
| 1535 | | -{ |
|---|
| 1536 | | - struct vm_area_struct *vma; |
|---|
| 1537 | | - size_t size = 0; |
|---|
| 1538 | | - |
|---|
| 1539 | | - for (vma = current->mm->mmap; vma; vma = vma->vm_next) |
|---|
| 1540 | | - if (maydump(vma, mm_flags)) |
|---|
| 1541 | | - size += vma->vm_end - vma->vm_start; |
|---|
| 1542 | | - return size; |
|---|
| 1543 | 1470 | } |
|---|
| 1544 | 1471 | |
|---|
| 1545 | 1472 | /* |
|---|
| .. | .. |
|---|
| 1551 | 1478 | */ |
|---|
| 1552 | 1479 | static int elf_fdpic_core_dump(struct coredump_params *cprm) |
|---|
| 1553 | 1480 | { |
|---|
| 1554 | | -#define NUM_NOTES 6 |
|---|
| 1555 | 1481 | int has_dumped = 0; |
|---|
| 1556 | | - mm_segment_t fs; |
|---|
| 1557 | | - int segs; |
|---|
| 1482 | + int vma_count, segs; |
|---|
| 1558 | 1483 | int i; |
|---|
| 1559 | | - struct vm_area_struct *vma; |
|---|
| 1560 | 1484 | struct elfhdr *elf = NULL; |
|---|
| 1561 | 1485 | loff_t offset = 0, dataoff; |
|---|
| 1562 | | - int numnote; |
|---|
| 1563 | | - struct memelfnote *notes = NULL; |
|---|
| 1564 | | - struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ |
|---|
| 1486 | + struct memelfnote psinfo_note, auxv_note; |
|---|
| 1565 | 1487 | struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */ |
|---|
| 1566 | | - LIST_HEAD(thread_list); |
|---|
| 1567 | | - struct list_head *t; |
|---|
| 1568 | | - elf_fpregset_t *fpu = NULL; |
|---|
| 1569 | | -#ifdef ELF_CORE_COPY_XFPREGS |
|---|
| 1570 | | - elf_fpxregset_t *xfpu = NULL; |
|---|
| 1571 | | -#endif |
|---|
| 1488 | + struct elf_thread_status *thread_list = NULL; |
|---|
| 1572 | 1489 | int thread_status_size = 0; |
|---|
| 1573 | 1490 | elf_addr_t *auxv; |
|---|
| 1574 | 1491 | struct elf_phdr *phdr4note = NULL; |
|---|
| .. | .. |
|---|
| 1577 | 1494 | elf_addr_t e_shoff; |
|---|
| 1578 | 1495 | struct core_thread *ct; |
|---|
| 1579 | 1496 | struct elf_thread_status *tmp; |
|---|
| 1580 | | - |
|---|
| 1581 | | - /* |
|---|
| 1582 | | - * We no longer stop all VM operations. |
|---|
| 1583 | | - * |
|---|
| 1584 | | - * This is because those proceses that could possibly change map_count |
|---|
| 1585 | | - * or the mmap / vma pages are now blocked in do_exit on current |
|---|
| 1586 | | - * finishing this core dump. |
|---|
| 1587 | | - * |
|---|
| 1588 | | - * Only ptrace can touch these memory addresses, but it doesn't change |
|---|
| 1589 | | - * the map_count or the pages allocated. So no possibility of crashing |
|---|
| 1590 | | - * exists while dumping the mm->vm_next areas to the core file. |
|---|
| 1591 | | - */ |
|---|
| 1497 | + struct core_vma_metadata *vma_meta = NULL; |
|---|
| 1498 | + size_t vma_data_size; |
|---|
| 1592 | 1499 | |
|---|
| 1593 | 1500 | /* alloc memory for large data structures: too large to be on stack */ |
|---|
| 1594 | 1501 | elf = kmalloc(sizeof(*elf), GFP_KERNEL); |
|---|
| 1595 | 1502 | if (!elf) |
|---|
| 1596 | | - goto cleanup; |
|---|
| 1597 | | - prstatus = kzalloc(sizeof(*prstatus), GFP_KERNEL); |
|---|
| 1598 | | - if (!prstatus) |
|---|
| 1599 | | - goto cleanup; |
|---|
| 1503 | + goto end_coredump; |
|---|
| 1600 | 1504 | psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); |
|---|
| 1601 | 1505 | if (!psinfo) |
|---|
| 1602 | | - goto cleanup; |
|---|
| 1603 | | - notes = kmalloc_array(NUM_NOTES, sizeof(struct memelfnote), |
|---|
| 1604 | | - GFP_KERNEL); |
|---|
| 1605 | | - if (!notes) |
|---|
| 1606 | | - goto cleanup; |
|---|
| 1607 | | - fpu = kmalloc(sizeof(*fpu), GFP_KERNEL); |
|---|
| 1608 | | - if (!fpu) |
|---|
| 1609 | | - goto cleanup; |
|---|
| 1610 | | -#ifdef ELF_CORE_COPY_XFPREGS |
|---|
| 1611 | | - xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL); |
|---|
| 1612 | | - if (!xfpu) |
|---|
| 1613 | | - goto cleanup; |
|---|
| 1614 | | -#endif |
|---|
| 1506 | + goto end_coredump; |
|---|
| 1507 | + |
|---|
| 1508 | + if (dump_vma_snapshot(cprm, &vma_count, &vma_meta, &vma_data_size)) |
|---|
| 1509 | + goto end_coredump; |
|---|
| 1615 | 1510 | |
|---|
| 1616 | 1511 | for (ct = current->mm->core_state->dumper.next; |
|---|
| 1617 | 1512 | ct; ct = ct->next) { |
|---|
| 1618 | | - tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); |
|---|
| 1513 | + tmp = elf_dump_thread_status(cprm->siginfo->si_signo, |
|---|
| 1514 | + ct->task, &thread_status_size); |
|---|
| 1619 | 1515 | if (!tmp) |
|---|
| 1620 | | - goto cleanup; |
|---|
| 1516 | + goto end_coredump; |
|---|
| 1621 | 1517 | |
|---|
| 1622 | | - tmp->thread = ct->task; |
|---|
| 1623 | | - list_add(&tmp->list, &thread_list); |
|---|
| 1624 | | - } |
|---|
| 1625 | | - |
|---|
| 1626 | | - list_for_each(t, &thread_list) { |
|---|
| 1627 | | - struct elf_thread_status *tmp; |
|---|
| 1628 | | - int sz; |
|---|
| 1629 | | - |
|---|
| 1630 | | - tmp = list_entry(t, struct elf_thread_status, list); |
|---|
| 1631 | | - sz = elf_dump_thread_status(cprm->siginfo->si_signo, tmp); |
|---|
| 1632 | | - thread_status_size += sz; |
|---|
| 1518 | + tmp->next = thread_list; |
|---|
| 1519 | + thread_list = tmp; |
|---|
| 1633 | 1520 | } |
|---|
| 1634 | 1521 | |
|---|
| 1635 | 1522 | /* now collect the dump for the current */ |
|---|
| 1636 | | - fill_prstatus(prstatus, current, cprm->siginfo->si_signo); |
|---|
| 1637 | | - elf_core_copy_regs(&prstatus->pr_reg, cprm->regs); |
|---|
| 1523 | + tmp = elf_dump_thread_status(cprm->siginfo->si_signo, |
|---|
| 1524 | + current, &thread_status_size); |
|---|
| 1525 | + if (!tmp) |
|---|
| 1526 | + goto end_coredump; |
|---|
| 1527 | + tmp->next = thread_list; |
|---|
| 1528 | + thread_list = tmp; |
|---|
| 1638 | 1529 | |
|---|
| 1639 | | - segs = current->mm->map_count; |
|---|
| 1640 | | - segs += elf_core_extra_phdrs(); |
|---|
| 1530 | + segs = vma_count + elf_core_extra_phdrs(); |
|---|
| 1641 | 1531 | |
|---|
| 1642 | 1532 | /* for notes section */ |
|---|
| 1643 | 1533 | segs++; |
|---|
| .. | .. |
|---|
| 1656 | 1546 | * with info from their /proc. |
|---|
| 1657 | 1547 | */ |
|---|
| 1658 | 1548 | |
|---|
| 1659 | | - fill_note(notes + 0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus); |
|---|
| 1660 | 1549 | fill_psinfo(psinfo, current->group_leader, current->mm); |
|---|
| 1661 | | - fill_note(notes + 1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); |
|---|
| 1662 | | - |
|---|
| 1663 | | - numnote = 2; |
|---|
| 1550 | + fill_note(&psinfo_note, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); |
|---|
| 1551 | + thread_status_size += notesize(&psinfo_note); |
|---|
| 1664 | 1552 | |
|---|
| 1665 | 1553 | auxv = (elf_addr_t *) current->mm->saved_auxv; |
|---|
| 1666 | | - |
|---|
| 1667 | 1554 | i = 0; |
|---|
| 1668 | 1555 | do |
|---|
| 1669 | 1556 | i += 2; |
|---|
| 1670 | 1557 | while (auxv[i - 2] != AT_NULL); |
|---|
| 1671 | | - fill_note(¬es[numnote++], "CORE", NT_AUXV, |
|---|
| 1672 | | - i * sizeof(elf_addr_t), auxv); |
|---|
| 1558 | + fill_note(&auxv_note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv); |
|---|
| 1559 | + thread_status_size += notesize(&auxv_note); |
|---|
| 1673 | 1560 | |
|---|
| 1674 | | - /* Try to dump the FPU. */ |
|---|
| 1675 | | - if ((prstatus->pr_fpvalid = |
|---|
| 1676 | | - elf_core_copy_task_fpregs(current, cprm->regs, fpu))) |
|---|
| 1677 | | - fill_note(notes + numnote++, |
|---|
| 1678 | | - "CORE", NT_PRFPREG, sizeof(*fpu), fpu); |
|---|
| 1679 | | -#ifdef ELF_CORE_COPY_XFPREGS |
|---|
| 1680 | | - if (elf_core_copy_task_xfpregs(current, xfpu)) |
|---|
| 1681 | | - fill_note(notes + numnote++, |
|---|
| 1682 | | - "LINUX", ELF_CORE_XFPREG_TYPE, sizeof(*xfpu), xfpu); |
|---|
| 1683 | | -#endif |
|---|
| 1684 | | - |
|---|
| 1685 | | - fs = get_fs(); |
|---|
| 1686 | | - set_fs(KERNEL_DS); |
|---|
| 1687 | | - |
|---|
| 1688 | | - offset += sizeof(*elf); /* Elf header */ |
|---|
| 1561 | + offset = sizeof(*elf); /* Elf header */ |
|---|
| 1689 | 1562 | offset += segs * sizeof(struct elf_phdr); /* Program headers */ |
|---|
| 1690 | 1563 | |
|---|
| 1691 | 1564 | /* Write notes phdr entry */ |
|---|
| 1692 | | - { |
|---|
| 1693 | | - int sz = 0; |
|---|
| 1565 | + phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); |
|---|
| 1566 | + if (!phdr4note) |
|---|
| 1567 | + goto end_coredump; |
|---|
| 1694 | 1568 | |
|---|
| 1695 | | - for (i = 0; i < numnote; i++) |
|---|
| 1696 | | - sz += notesize(notes + i); |
|---|
| 1697 | | - |
|---|
| 1698 | | - sz += thread_status_size; |
|---|
| 1699 | | - |
|---|
| 1700 | | - phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); |
|---|
| 1701 | | - if (!phdr4note) |
|---|
| 1702 | | - goto end_coredump; |
|---|
| 1703 | | - |
|---|
| 1704 | | - fill_elf_note_phdr(phdr4note, sz, offset); |
|---|
| 1705 | | - offset += sz; |
|---|
| 1706 | | - } |
|---|
| 1569 | + fill_elf_note_phdr(phdr4note, thread_status_size, offset); |
|---|
| 1570 | + offset += thread_status_size; |
|---|
| 1707 | 1571 | |
|---|
| 1708 | 1572 | /* Page-align dumped data */ |
|---|
| 1709 | 1573 | dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); |
|---|
| 1710 | 1574 | |
|---|
| 1711 | | - offset += elf_core_vma_data_size(cprm->mm_flags); |
|---|
| 1575 | + offset += vma_data_size; |
|---|
| 1712 | 1576 | offset += elf_core_extra_data_size(); |
|---|
| 1713 | 1577 | e_shoff = offset; |
|---|
| 1714 | 1578 | |
|---|
| .. | .. |
|---|
| 1728 | 1592 | goto end_coredump; |
|---|
| 1729 | 1593 | |
|---|
| 1730 | 1594 | /* write program headers for segments dump */ |
|---|
| 1731 | | - for (vma = current->mm->mmap; vma; vma = vma->vm_next) { |
|---|
| 1595 | + for (i = 0; i < vma_count; i++) { |
|---|
| 1596 | + struct core_vma_metadata *meta = vma_meta + i; |
|---|
| 1732 | 1597 | struct elf_phdr phdr; |
|---|
| 1733 | 1598 | size_t sz; |
|---|
| 1734 | 1599 | |
|---|
| 1735 | | - sz = vma->vm_end - vma->vm_start; |
|---|
| 1600 | + sz = meta->end - meta->start; |
|---|
| 1736 | 1601 | |
|---|
| 1737 | 1602 | phdr.p_type = PT_LOAD; |
|---|
| 1738 | 1603 | phdr.p_offset = offset; |
|---|
| 1739 | | - phdr.p_vaddr = vma->vm_start; |
|---|
| 1604 | + phdr.p_vaddr = meta->start; |
|---|
| 1740 | 1605 | phdr.p_paddr = 0; |
|---|
| 1741 | | - phdr.p_filesz = maydump(vma, cprm->mm_flags) ? sz : 0; |
|---|
| 1606 | + phdr.p_filesz = meta->dump_size; |
|---|
| 1742 | 1607 | phdr.p_memsz = sz; |
|---|
| 1743 | 1608 | offset += phdr.p_filesz; |
|---|
| 1744 | | - phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; |
|---|
| 1745 | | - if (vma->vm_flags & VM_WRITE) |
|---|
| 1609 | + phdr.p_flags = 0; |
|---|
| 1610 | + if (meta->flags & VM_READ) |
|---|
| 1611 | + phdr.p_flags |= PF_R; |
|---|
| 1612 | + if (meta->flags & VM_WRITE) |
|---|
| 1746 | 1613 | phdr.p_flags |= PF_W; |
|---|
| 1747 | | - if (vma->vm_flags & VM_EXEC) |
|---|
| 1614 | + if (meta->flags & VM_EXEC) |
|---|
| 1748 | 1615 | phdr.p_flags |= PF_X; |
|---|
| 1749 | 1616 | phdr.p_align = ELF_EXEC_PAGESIZE; |
|---|
| 1750 | 1617 | |
|---|
| .. | .. |
|---|
| 1756 | 1623 | goto end_coredump; |
|---|
| 1757 | 1624 | |
|---|
| 1758 | 1625 | /* write out the notes section */ |
|---|
| 1759 | | - for (i = 0; i < numnote; i++) |
|---|
| 1760 | | - if (!writenote(notes + i, cprm)) |
|---|
| 1626 | + if (!writenote(thread_list->notes, cprm)) |
|---|
| 1627 | + goto end_coredump; |
|---|
| 1628 | + if (!writenote(&psinfo_note, cprm)) |
|---|
| 1629 | + goto end_coredump; |
|---|
| 1630 | + if (!writenote(&auxv_note, cprm)) |
|---|
| 1631 | + goto end_coredump; |
|---|
| 1632 | + for (i = 1; i < thread_list->num_notes; i++) |
|---|
| 1633 | + if (!writenote(thread_list->notes + i, cprm)) |
|---|
| 1761 | 1634 | goto end_coredump; |
|---|
| 1762 | 1635 | |
|---|
| 1763 | 1636 | /* write out the thread status notes section */ |
|---|
| 1764 | | - list_for_each(t, &thread_list) { |
|---|
| 1765 | | - struct elf_thread_status *tmp = |
|---|
| 1766 | | - list_entry(t, struct elf_thread_status, list); |
|---|
| 1767 | | - |
|---|
| 1637 | + for (tmp = thread_list->next; tmp; tmp = tmp->next) { |
|---|
| 1768 | 1638 | for (i = 0; i < tmp->num_notes; i++) |
|---|
| 1769 | 1639 | if (!writenote(&tmp->notes[i], cprm)) |
|---|
| 1770 | 1640 | goto end_coredump; |
|---|
| .. | .. |
|---|
| 1773 | 1643 | if (!dump_skip(cprm, dataoff - cprm->pos)) |
|---|
| 1774 | 1644 | goto end_coredump; |
|---|
| 1775 | 1645 | |
|---|
| 1776 | | - if (!elf_fdpic_dump_segments(cprm)) |
|---|
| 1646 | + if (!elf_fdpic_dump_segments(cprm, vma_meta, vma_count)) |
|---|
| 1777 | 1647 | goto end_coredump; |
|---|
| 1778 | 1648 | |
|---|
| 1779 | 1649 | if (!elf_core_write_extra_data(cprm)) |
|---|
| .. | .. |
|---|
| 1792 | 1662 | } |
|---|
| 1793 | 1663 | |
|---|
| 1794 | 1664 | end_coredump: |
|---|
| 1795 | | - set_fs(fs); |
|---|
| 1796 | | - |
|---|
| 1797 | | -cleanup: |
|---|
| 1798 | | - while (!list_empty(&thread_list)) { |
|---|
| 1799 | | - struct list_head *tmp = thread_list.next; |
|---|
| 1800 | | - list_del(tmp); |
|---|
| 1801 | | - kfree(list_entry(tmp, struct elf_thread_status, list)); |
|---|
| 1665 | + while (thread_list) { |
|---|
| 1666 | + tmp = thread_list; |
|---|
| 1667 | + thread_list = thread_list->next; |
|---|
| 1668 | + kfree(tmp); |
|---|
| 1802 | 1669 | } |
|---|
| 1670 | + kvfree(vma_meta); |
|---|
| 1803 | 1671 | kfree(phdr4note); |
|---|
| 1804 | 1672 | kfree(elf); |
|---|
| 1805 | | - kfree(prstatus); |
|---|
| 1806 | 1673 | kfree(psinfo); |
|---|
| 1807 | | - kfree(notes); |
|---|
| 1808 | | - kfree(fpu); |
|---|
| 1809 | 1674 | kfree(shdr4extnum); |
|---|
| 1810 | | -#ifdef ELF_CORE_COPY_XFPREGS |
|---|
| 1811 | | - kfree(xfpu); |
|---|
| 1812 | | -#endif |
|---|
| 1813 | 1675 | return has_dumped; |
|---|
| 1814 | | -#undef NUM_NOTES |
|---|
| 1815 | 1676 | } |
|---|
| 1816 | 1677 | |
|---|
| 1817 | 1678 | #endif /* CONFIG_ELF_CORE */ |
|---|