Warner Losh | b211b36 | 2021-04-29 18:34:08 -0600 | [diff] [blame] | 1 | /* |
| 2 | * Load BSD executables. |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License as published by |
| 6 | * the Free Software Foundation; either version 2 of the License, or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
| 16 | */ |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 17 | |
Peter Maydell | 2231197 | 2016-01-29 17:49:53 +0000 | [diff] [blame] | 18 | #include "qemu/osdep.h" |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 19 | |
| 20 | #include "qemu.h" |
| 21 | |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 22 | /* ??? This should really be somewhere else. */ |
| 23 | abi_long memcpy_to_target(abi_ulong dest, const void *src, |
| 24 | unsigned long len) |
| 25 | { |
| 26 | void *host_ptr; |
| 27 | |
| 28 | host_ptr = lock_user(VERIFY_WRITE, dest, len, 0); |
Warner Losh | 58b3beb | 2021-04-23 16:23:53 -0600 | [diff] [blame] | 29 | if (!host_ptr) { |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 30 | return -TARGET_EFAULT; |
Warner Losh | 58b3beb | 2021-04-23 16:23:53 -0600 | [diff] [blame] | 31 | } |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 32 | memcpy(host_ptr, src, len); |
| 33 | unlock_user(host_ptr, dest, 1); |
| 34 | return 0; |
| 35 | } |
| 36 | |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 37 | static int count(char **vec) |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 38 | { |
| 39 | int i; |
| 40 | |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 41 | for (i = 0; *vec; i++) { |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 42 | vec++; |
| 43 | } |
| 44 | |
Warner Losh | fa05463 | 2021-04-23 16:22:19 -0600 | [diff] [blame] | 45 | return i; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 46 | } |
| 47 | |
Warner Losh | afcbcff | 2021-04-29 10:04:28 -0600 | [diff] [blame] | 48 | static int prepare_binprm(struct bsd_binprm *bprm) |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 49 | { |
| 50 | struct stat st; |
| 51 | int mode; |
Peter Maydell | 4a65a86 | 2017-07-18 17:26:33 +0100 | [diff] [blame] | 52 | int retval; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 53 | |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 54 | if (fstat(bprm->fd, &st) < 0) { |
Warner Losh | fa05463 | 2021-04-23 16:22:19 -0600 | [diff] [blame] | 55 | return -errno; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | mode = st.st_mode; |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 59 | if (!S_ISREG(mode)) { /* Must be regular file */ |
Warner Losh | fa05463 | 2021-04-23 16:22:19 -0600 | [diff] [blame] | 60 | return -EACCES; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 61 | } |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 62 | if (!(mode & 0111)) { /* Must have at least one execute bit set */ |
Warner Losh | fa05463 | 2021-04-23 16:22:19 -0600 | [diff] [blame] | 63 | return -EACCES; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | bprm->e_uid = geteuid(); |
| 67 | bprm->e_gid = getegid(); |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 68 | |
| 69 | /* Set-uid? */ |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 70 | if (mode & S_ISUID) { |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 71 | bprm->e_uid = st.st_uid; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 72 | } |
| 73 | |
| 74 | /* Set-gid? */ |
| 75 | /* |
| 76 | * If setgid is set but no group execute bit then this |
| 77 | * is a candidate for mandatory locking, not a setgid |
| 78 | * executable. |
| 79 | */ |
| 80 | if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { |
| 81 | bprm->e_gid = st.st_gid; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | memset(bprm->buf, 0, sizeof(bprm->buf)); |
| 85 | retval = lseek(bprm->fd, 0L, SEEK_SET); |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 86 | if (retval >= 0) { |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 87 | retval = read(bprm->fd, bprm->buf, 128); |
| 88 | } |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 89 | if (retval < 0) { |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 90 | perror("prepare_binprm"); |
| 91 | exit(-1); |
Warner Losh | 58b3beb | 2021-04-23 16:23:53 -0600 | [diff] [blame] | 92 | } else { |
Warner Losh | fa05463 | 2021-04-23 16:22:19 -0600 | [diff] [blame] | 93 | return retval; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 94 | } |
| 95 | } |
| 96 | |
| 97 | /* Construct the envp and argv tables on the target stack. */ |
| 98 | abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, |
Warner Losh | ffa0366 | 2021-04-30 08:17:23 -0600 | [diff] [blame] | 99 | abi_ulong stringp) |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 100 | { |
| 101 | int n = sizeof(abi_ulong); |
| 102 | abi_ulong envp; |
| 103 | abi_ulong argv; |
| 104 | |
| 105 | sp -= (envc + 1) * n; |
| 106 | envp = sp; |
| 107 | sp -= (argc + 1) * n; |
| 108 | argv = sp; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 109 | sp -= n; |
| 110 | /* FIXME - handle put_user() failures */ |
| 111 | put_user_ual(argc, sp); |
| 112 | |
| 113 | while (argc-- > 0) { |
| 114 | /* FIXME - handle put_user() failures */ |
| 115 | put_user_ual(stringp, argv); |
| 116 | argv += n; |
| 117 | stringp += target_strlen(stringp) + 1; |
| 118 | } |
| 119 | /* FIXME - handle put_user() failures */ |
| 120 | put_user_ual(0, argv); |
| 121 | while (envc-- > 0) { |
| 122 | /* FIXME - handle put_user() failures */ |
| 123 | put_user_ual(stringp, envp); |
| 124 | envp += n; |
| 125 | stringp += target_strlen(stringp) + 1; |
| 126 | } |
| 127 | /* FIXME - handle put_user() failures */ |
| 128 | put_user_ual(0, envp); |
| 129 | |
| 130 | return sp; |
| 131 | } |
| 132 | |
Warner Losh | 1b50ff6 | 2021-04-29 19:34:34 -0600 | [diff] [blame] | 133 | static bool is_there(const char *candidate) |
| 134 | { |
| 135 | struct stat fin; |
| 136 | |
| 137 | /* XXX work around access(2) false positives for superuser */ |
| 138 | if (access(candidate, X_OK) == 0 && stat(candidate, &fin) == 0 && |
| 139 | S_ISREG(fin.st_mode) && (getuid() != 0 || |
| 140 | (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) { |
| 141 | return true; |
| 142 | } |
| 143 | |
| 144 | return false; |
| 145 | } |
| 146 | |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 147 | int loader_exec(const char *filename, char **argv, char **envp, |
Warner Losh | d37853f | 2021-04-29 18:45:13 -0600 | [diff] [blame] | 148 | struct target_pt_regs *regs, struct image_info *infop, |
| 149 | struct bsd_binprm *bprm) |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 150 | { |
Warner Losh | 1b50ff6 | 2021-04-29 19:34:34 -0600 | [diff] [blame] | 151 | char *path, fullpath[PATH_MAX]; |
Warner Losh | 223005f | 2021-04-29 18:47:51 -0600 | [diff] [blame] | 152 | int retval, i; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 153 | |
Warner Losh | 223005f | 2021-04-29 18:47:51 -0600 | [diff] [blame] | 154 | bprm->p = TARGET_PAGE_SIZE * MAX_ARG_PAGES; |
Warner Losh | d37853f | 2021-04-29 18:45:13 -0600 | [diff] [blame] | 155 | for (i = 0; i < MAX_ARG_PAGES; i++) { /* clear page-table */ |
| 156 | bprm->page[i] = NULL; |
Warner Losh | 58b3beb | 2021-04-23 16:23:53 -0600 | [diff] [blame] | 157 | } |
Warner Losh | 1b50ff6 | 2021-04-29 19:34:34 -0600 | [diff] [blame] | 158 | |
| 159 | if (strchr(filename, '/') != NULL) { |
| 160 | path = realpath(filename, fullpath); |
| 161 | if (path == NULL) { |
| 162 | /* Failed to resolve. */ |
| 163 | return -1; |
| 164 | } |
| 165 | if (!is_there(path)) { |
| 166 | return -1; |
| 167 | } |
| 168 | } else { |
| 169 | path = g_find_program_in_path(filename); |
| 170 | if (path == NULL) { |
| 171 | return -1; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | retval = open(path, O_RDONLY); |
Warner Losh | 58b3beb | 2021-04-23 16:23:53 -0600 | [diff] [blame] | 176 | if (retval < 0) { |
Warner Losh | 1b50ff6 | 2021-04-29 19:34:34 -0600 | [diff] [blame] | 177 | g_free(path); |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 178 | return retval; |
Warner Losh | 58b3beb | 2021-04-23 16:23:53 -0600 | [diff] [blame] | 179 | } |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 180 | |
Warner Losh | 1b50ff6 | 2021-04-29 19:34:34 -0600 | [diff] [blame] | 181 | bprm->fullpath = path; |
Warner Losh | d37853f | 2021-04-29 18:45:13 -0600 | [diff] [blame] | 182 | bprm->fd = retval; |
| 183 | bprm->filename = (char *)filename; |
| 184 | bprm->argc = count(argv); |
| 185 | bprm->argv = argv; |
| 186 | bprm->envc = count(envp); |
| 187 | bprm->envp = envp; |
| 188 | |
| 189 | retval = prepare_binprm(bprm); |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 190 | |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 191 | if (retval >= 0) { |
Warner Losh | d37853f | 2021-04-29 18:45:13 -0600 | [diff] [blame] | 192 | if (bprm->buf[0] == 0x7f |
| 193 | && bprm->buf[1] == 'E' |
| 194 | && bprm->buf[2] == 'L' |
| 195 | && bprm->buf[3] == 'F') { |
| 196 | retval = load_elf_binary(bprm, regs, infop); |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 197 | } else { |
Peter Maydell | 9bb9318 | 2014-06-02 13:24:37 +0100 | [diff] [blame] | 198 | fprintf(stderr, "Unknown binary format\n"); |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 199 | return -1; |
| 200 | } |
| 201 | } |
| 202 | |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 203 | if (retval >= 0) { |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 204 | /* success. Initialize important registers */ |
| 205 | do_init_thread(regs, infop); |
| 206 | return retval; |
| 207 | } |
| 208 | |
| 209 | /* Something went wrong, return the inode and free the argument pages*/ |
Warner Losh | ca0fd2e | 2021-04-23 09:05:14 -0600 | [diff] [blame] | 210 | for (i = 0 ; i < MAX_ARG_PAGES ; i++) { |
Warner Losh | d37853f | 2021-04-29 18:45:13 -0600 | [diff] [blame] | 211 | g_free(bprm->page[i]); |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 212 | } |
Warner Losh | fa05463 | 2021-04-23 16:22:19 -0600 | [diff] [blame] | 213 | return retval; |
blueswir1 | 8477850 | 2008-10-26 20:33:16 +0000 | [diff] [blame] | 214 | } |