Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
0004  */
0005 
0006 #include <stdio.h>
0007 #include <stdlib.h>
0008 #include <stdarg.h>
0009 #include <unistd.h>
0010 #include <errno.h>
0011 #include <fcntl.h>
0012 #include <sched.h>
0013 #include <signal.h>
0014 #include <string.h>
0015 #include <sys/mman.h>
0016 #include <sys/stat.h>
0017 #include <sys/wait.h>
0018 #include <sys/time.h>
0019 #include <sys/resource.h>
0020 #include <asm/unistd.h>
0021 #include <init.h>
0022 #include <os.h>
0023 #include <mem_user.h>
0024 #include <ptrace_user.h>
0025 #include <registers.h>
0026 #include <skas.h>
0027 
0028 static void ptrace_child(void)
0029 {
0030     int ret;
0031     /* Calling os_getpid because some libcs cached getpid incorrectly */
0032     int pid = os_getpid(), ppid = getppid();
0033     int sc_result;
0034 
0035     if (change_sig(SIGWINCH, 0) < 0 ||
0036         ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
0037         perror("ptrace");
0038         kill(pid, SIGKILL);
0039     }
0040     kill(pid, SIGSTOP);
0041 
0042     /*
0043      * This syscall will be intercepted by the parent. Don't call more than
0044      * once, please.
0045      */
0046     sc_result = os_getpid();
0047 
0048     if (sc_result == pid)
0049         /* Nothing modified by the parent, we are running normally. */
0050         ret = 1;
0051     else if (sc_result == ppid)
0052         /*
0053          * Expected in check_ptrace and check_sysemu when they succeed
0054          * in modifying the stack frame
0055          */
0056         ret = 0;
0057     else
0058         /* Serious trouble! This could be caused by a bug in host 2.6
0059          * SKAS3/2.6 patch before release -V6, together with a bug in
0060          * the UML code itself.
0061          */
0062         ret = 2;
0063 
0064     exit(ret);
0065 }
0066 
0067 static void fatal_perror(const char *str)
0068 {
0069     perror(str);
0070     exit(1);
0071 }
0072 
0073 static void fatal(char *fmt, ...)
0074 {
0075     va_list list;
0076 
0077     va_start(list, fmt);
0078     vfprintf(stderr, fmt, list);
0079     va_end(list);
0080 
0081     exit(1);
0082 }
0083 
0084 static void non_fatal(char *fmt, ...)
0085 {
0086     va_list list;
0087 
0088     va_start(list, fmt);
0089     vfprintf(stderr, fmt, list);
0090     va_end(list);
0091 }
0092 
0093 static int start_ptraced_child(void)
0094 {
0095     int pid, n, status;
0096 
0097     fflush(stdout);
0098 
0099     pid = fork();
0100     if (pid == 0)
0101         ptrace_child();
0102     else if (pid < 0)
0103         fatal_perror("start_ptraced_child : fork failed");
0104 
0105     CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
0106     if (n < 0)
0107         fatal_perror("check_ptrace : waitpid failed");
0108     if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))
0109         fatal("check_ptrace : expected SIGSTOP, got status = %d",
0110               status);
0111 
0112     return pid;
0113 }
0114 
0115 /* When testing for SYSEMU support, if it is one of the broken versions, we
0116  * must just avoid using sysemu, not panic, but only if SYSEMU features are
0117  * broken.
0118  * So only for SYSEMU features we test mustpanic, while normal host features
0119  * must work anyway!
0120  */
0121 static int stop_ptraced_child(int pid, int exitcode, int mustexit)
0122 {
0123     int status, n, ret = 0;
0124 
0125     if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
0126         perror("stop_ptraced_child : ptrace failed");
0127         return -1;
0128     }
0129     CATCH_EINTR(n = waitpid(pid, &status, 0));
0130     if (!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) {
0131         int exit_with = WEXITSTATUS(status);
0132         if (exit_with == 2)
0133             non_fatal("check_ptrace : child exited with status 2. "
0134                   "\nDisabling SYSEMU support.\n");
0135         non_fatal("check_ptrace : child exited with exitcode %d, while "
0136               "expecting %d; status 0x%x\n", exit_with,
0137               exitcode, status);
0138         if (mustexit)
0139             exit(1);
0140         ret = -1;
0141     }
0142 
0143     return ret;
0144 }
0145 
0146 /* Changed only during early boot */
0147 static int force_sysemu_disabled = 0;
0148 
0149 static int __init nosysemu_cmd_param(char *str, int* add)
0150 {
0151     force_sysemu_disabled = 1;
0152     return 0;
0153 }
0154 
0155 __uml_setup("nosysemu", nosysemu_cmd_param,
0156 "nosysemu\n"
0157 "    Turns off syscall emulation patch for ptrace (SYSEMU).\n"
0158 "    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n"
0159 "    behaviour of ptrace() and helps reduce host context switch rates.\n"
0160 "    To make it work, you need a kernel patch for your host, too.\n"
0161 "    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further \n"
0162 "    information.\n\n");
0163 
0164 static void __init check_sysemu(void)
0165 {
0166     unsigned long regs[MAX_REG_NR];
0167     int pid, n, status, count=0;
0168 
0169     os_info("Checking syscall emulation patch for ptrace...");
0170     sysemu_supported = 0;
0171     pid = start_ptraced_child();
0172 
0173     if (ptrace(PTRACE_SYSEMU, pid, 0, 0) < 0)
0174         goto fail;
0175 
0176     CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
0177     if (n < 0)
0178         fatal_perror("check_sysemu : wait failed");
0179     if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP))
0180         fatal("check_sysemu : expected SIGTRAP, got status = %d\n",
0181               status);
0182 
0183     if (ptrace(PTRACE_GETREGS, pid, 0, regs) < 0)
0184         fatal_perror("check_sysemu : PTRACE_GETREGS failed");
0185     if (PT_SYSCALL_NR(regs) != __NR_getpid) {
0186         non_fatal("check_sysemu got system call number %d, "
0187               "expected %d...", PT_SYSCALL_NR(regs), __NR_getpid);
0188         goto fail;
0189     }
0190 
0191     n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET, os_getpid());
0192     if (n < 0) {
0193         non_fatal("check_sysemu : failed to modify system call "
0194               "return");
0195         goto fail;
0196     }
0197 
0198     if (stop_ptraced_child(pid, 0, 0) < 0)
0199         goto fail_stopped;
0200 
0201     sysemu_supported = 1;
0202     os_info("OK\n");
0203     set_using_sysemu(!force_sysemu_disabled);
0204 
0205     os_info("Checking advanced syscall emulation patch for ptrace...");
0206     pid = start_ptraced_child();
0207 
0208     if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
0209            (void *) PTRACE_O_TRACESYSGOOD) < 0))
0210         fatal_perror("check_sysemu: PTRACE_OLDSETOPTIONS failed");
0211 
0212     while (1) {
0213         count++;
0214         if (ptrace(PTRACE_SYSEMU_SINGLESTEP, pid, 0, 0) < 0)
0215             goto fail;
0216         CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
0217         if (n < 0)
0218             fatal_perror("check_sysemu: wait failed");
0219 
0220         if (WIFSTOPPED(status) &&
0221             (WSTOPSIG(status) == (SIGTRAP|0x80))) {
0222             if (!count) {
0223                 non_fatal("check_sysemu: SYSEMU_SINGLESTEP "
0224                       "doesn't singlestep");
0225                 goto fail;
0226             }
0227             n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_RET_OFFSET,
0228                    os_getpid());
0229             if (n < 0)
0230                 fatal_perror("check_sysemu : failed to modify "
0231                          "system call return");
0232             break;
0233         }
0234         else if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGTRAP))
0235             count++;
0236         else {
0237             non_fatal("check_sysemu: expected SIGTRAP or "
0238                   "(SIGTRAP | 0x80), got status = %d\n",
0239                   status);
0240             goto fail;
0241         }
0242     }
0243     if (stop_ptraced_child(pid, 0, 0) < 0)
0244         goto fail_stopped;
0245 
0246     sysemu_supported = 2;
0247     os_info("OK\n");
0248 
0249     if (!force_sysemu_disabled)
0250         set_using_sysemu(sysemu_supported);
0251     return;
0252 
0253 fail:
0254     stop_ptraced_child(pid, 1, 0);
0255 fail_stopped:
0256     non_fatal("missing\n");
0257 }
0258 
0259 static void __init check_ptrace(void)
0260 {
0261     int pid, syscall, n, status;
0262 
0263     os_info("Checking that ptrace can change system call numbers...");
0264     pid = start_ptraced_child();
0265 
0266     if ((ptrace(PTRACE_OLDSETOPTIONS, pid, 0,
0267            (void *) PTRACE_O_TRACESYSGOOD) < 0))
0268         fatal_perror("check_ptrace: PTRACE_OLDSETOPTIONS failed");
0269 
0270     while (1) {
0271         if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
0272             fatal_perror("check_ptrace : ptrace failed");
0273 
0274         CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
0275         if (n < 0)
0276             fatal_perror("check_ptrace : wait failed");
0277 
0278         if (!WIFSTOPPED(status) ||
0279            (WSTOPSIG(status) != (SIGTRAP | 0x80)))
0280             fatal("check_ptrace : expected (SIGTRAP|0x80), "
0281                    "got status = %d", status);
0282 
0283         syscall = ptrace(PTRACE_PEEKUSER, pid, PT_SYSCALL_NR_OFFSET,
0284                  0);
0285         if (syscall == __NR_getpid) {
0286             n = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET,
0287                    __NR_getppid);
0288             if (n < 0)
0289                 fatal_perror("check_ptrace : failed to modify "
0290                          "system call");
0291             break;
0292         }
0293     }
0294     stop_ptraced_child(pid, 0, 1);
0295     os_info("OK\n");
0296     check_sysemu();
0297 }
0298 
0299 extern void check_tmpexec(void);
0300 
0301 static void __init check_coredump_limit(void)
0302 {
0303     struct rlimit lim;
0304     int err = getrlimit(RLIMIT_CORE, &lim);
0305 
0306     if (err) {
0307         perror("Getting core dump limit");
0308         return;
0309     }
0310 
0311     os_info("Core dump limits :\n\tsoft - ");
0312     if (lim.rlim_cur == RLIM_INFINITY)
0313         os_info("NONE\n");
0314     else
0315         os_info("%llu\n", (unsigned long long)lim.rlim_cur);
0316 
0317     os_info("\thard - ");
0318     if (lim.rlim_max == RLIM_INFINITY)
0319         os_info("NONE\n");
0320     else
0321         os_info("%llu\n", (unsigned long long)lim.rlim_max);
0322 }
0323 
0324 void  __init get_host_cpu_features(
0325         void (*flags_helper_func)(char *line),
0326         void (*cache_helper_func)(char *line))
0327 {
0328     FILE *cpuinfo;
0329     char *line = NULL;
0330     size_t len = 0;
0331     int done_parsing = 0;
0332 
0333     cpuinfo = fopen("/proc/cpuinfo", "r");
0334     if (cpuinfo == NULL) {
0335         os_info("Failed to get host CPU features\n");
0336     } else {
0337         while ((getline(&line, &len, cpuinfo)) != -1) {
0338             if (strstr(line, "flags")) {
0339                 flags_helper_func(line);
0340                 done_parsing++;
0341             }
0342             if (strstr(line, "cache_alignment")) {
0343                 cache_helper_func(line);
0344                 done_parsing++;
0345             }
0346             free(line);
0347             line = NULL;
0348             if (done_parsing > 1)
0349                 break;
0350         }
0351         fclose(cpuinfo);
0352     }
0353 }
0354 
0355 
0356 void __init os_early_checks(void)
0357 {
0358     int pid;
0359 
0360     /* Print out the core dump limits early */
0361     check_coredump_limit();
0362 
0363     check_ptrace();
0364 
0365     /* Need to check this early because mmapping happens before the
0366      * kernel is running.
0367      */
0368     check_tmpexec();
0369 
0370     pid = start_ptraced_child();
0371     if (init_pid_registers(pid))
0372         fatal("Failed to initialize default registers");
0373     stop_ptraced_child(pid, 1, 1);
0374 }
0375 
0376 int __init parse_iomem(char *str, int *add)
0377 {
0378     struct iomem_region *new;
0379     struct stat64 buf;
0380     char *file, *driver;
0381     int fd, size;
0382 
0383     driver = str;
0384     file = strchr(str,',');
0385     if (file == NULL) {
0386         os_warn("parse_iomem : failed to parse iomem\n");
0387         goto out;
0388     }
0389     *file = '\0';
0390     file++;
0391     fd = open(file, O_RDWR, 0);
0392     if (fd < 0) {
0393         perror("parse_iomem - Couldn't open io file");
0394         goto out;
0395     }
0396 
0397     if (fstat64(fd, &buf) < 0) {
0398         perror("parse_iomem - cannot stat_fd file");
0399         goto out_close;
0400     }
0401 
0402     new = malloc(sizeof(*new));
0403     if (new == NULL) {
0404         perror("Couldn't allocate iomem_region struct");
0405         goto out_close;
0406     }
0407 
0408     size = (buf.st_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1);
0409 
0410     *new = ((struct iomem_region) { .next       = iomem_regions,
0411                     .driver     = driver,
0412                     .fd     = fd,
0413                     .size       = size,
0414                     .phys       = 0,
0415                     .virt       = 0 });
0416     iomem_regions = new;
0417     iomem_size += new->size + UM_KERN_PAGE_SIZE;
0418 
0419     return 0;
0420  out_close:
0421     close(fd);
0422  out:
0423     return 1;
0424 }