1b211b368SWarner Losh /*
2b211b368SWarner Losh * Load BSD executables.
3b211b368SWarner Losh *
4b211b368SWarner Losh * This program is free software; you can redistribute it and/or modify
5b211b368SWarner Losh * it under the terms of the GNU General Public License as published by
6b211b368SWarner Losh * the Free Software Foundation; either version 2 of the License, or
7b211b368SWarner Losh * (at your option) any later version.
8b211b368SWarner Losh *
9b211b368SWarner Losh * This program is distributed in the hope that it will be useful,
10b211b368SWarner Losh * but WITHOUT ANY WARRANTY; without even the implied warranty of
11b211b368SWarner Losh * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12b211b368SWarner Losh * GNU General Public License for more details.
13b211b368SWarner Losh *
14b211b368SWarner Losh * You should have received a copy of the GNU General Public License
15b211b368SWarner Losh * along with this program; if not, see <http://www.gnu.org/licenses/>.
16b211b368SWarner Losh */
1784778508Sblueswir1
182231197cSPeter Maydell #include "qemu/osdep.h"
1984778508Sblueswir1
2084778508Sblueswir1 #include "qemu.h"
2184778508Sblueswir1
2284778508Sblueswir1 /* ??? This should really be somewhere else. */
memcpy_to_target(abi_ulong dest,const void * src,unsigned long len)2384778508Sblueswir1 abi_long memcpy_to_target(abi_ulong dest, const void *src,
2484778508Sblueswir1 unsigned long len)
2584778508Sblueswir1 {
2684778508Sblueswir1 void *host_ptr;
2784778508Sblueswir1
2884778508Sblueswir1 host_ptr = lock_user(VERIFY_WRITE, dest, len, 0);
2958b3beb4SWarner Losh if (!host_ptr) {
3084778508Sblueswir1 return -TARGET_EFAULT;
3158b3beb4SWarner Losh }
3284778508Sblueswir1 memcpy(host_ptr, src, len);
3384778508Sblueswir1 unlock_user(host_ptr, dest, 1);
3484778508Sblueswir1 return 0;
3584778508Sblueswir1 }
3684778508Sblueswir1
count(char ** vec)3784778508Sblueswir1 static int count(char **vec)
3884778508Sblueswir1 {
3984778508Sblueswir1 int i;
4084778508Sblueswir1
4184778508Sblueswir1 for (i = 0; *vec; i++) {
4284778508Sblueswir1 vec++;
4384778508Sblueswir1 }
4484778508Sblueswir1
45fa054637SWarner Losh return i;
4684778508Sblueswir1 }
4784778508Sblueswir1
prepare_binprm(struct bsd_binprm * bprm)48afcbcff8SWarner Losh static int prepare_binprm(struct bsd_binprm *bprm)
4984778508Sblueswir1 {
5084778508Sblueswir1 struct stat st;
5184778508Sblueswir1 int mode;
524a65a86aSPeter Maydell int retval;
5384778508Sblueswir1
5484778508Sblueswir1 if (fstat(bprm->fd, &st) < 0) {
55fa054637SWarner Losh return -errno;
5684778508Sblueswir1 }
5784778508Sblueswir1
5884778508Sblueswir1 mode = st.st_mode;
5984778508Sblueswir1 if (!S_ISREG(mode)) { /* Must be regular file */
60fa054637SWarner Losh return -EACCES;
6184778508Sblueswir1 }
6284778508Sblueswir1 if (!(mode & 0111)) { /* Must have at least one execute bit set */
63fa054637SWarner Losh return -EACCES;
6484778508Sblueswir1 }
6584778508Sblueswir1
6684778508Sblueswir1 bprm->e_uid = geteuid();
6784778508Sblueswir1 bprm->e_gid = getegid();
6884778508Sblueswir1
6984778508Sblueswir1 /* Set-uid? */
7084778508Sblueswir1 if (mode & S_ISUID) {
7184778508Sblueswir1 bprm->e_uid = st.st_uid;
7284778508Sblueswir1 }
7384778508Sblueswir1
7484778508Sblueswir1 /* Set-gid? */
7584778508Sblueswir1 /*
7684778508Sblueswir1 * If setgid is set but no group execute bit then this
7784778508Sblueswir1 * is a candidate for mandatory locking, not a setgid
7884778508Sblueswir1 * executable.
7984778508Sblueswir1 */
8084778508Sblueswir1 if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
8184778508Sblueswir1 bprm->e_gid = st.st_gid;
8284778508Sblueswir1 }
8384778508Sblueswir1
8484778508Sblueswir1 memset(bprm->buf, 0, sizeof(bprm->buf));
8584778508Sblueswir1 retval = lseek(bprm->fd, 0L, SEEK_SET);
8684778508Sblueswir1 if (retval >= 0) {
8784778508Sblueswir1 retval = read(bprm->fd, bprm->buf, 128);
8884778508Sblueswir1 }
8984778508Sblueswir1 if (retval < 0) {
9084778508Sblueswir1 perror("prepare_binprm");
9184778508Sblueswir1 exit(-1);
9258b3beb4SWarner Losh } else {
93fa054637SWarner Losh return retval;
9484778508Sblueswir1 }
9584778508Sblueswir1 }
9684778508Sblueswir1
9784778508Sblueswir1 /* Construct the envp and argv tables on the target stack. */
loader_build_argptr(int envc,int argc,abi_ulong sp,abi_ulong stringp)9884778508Sblueswir1 abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp,
99*ffa03665SWarner Losh abi_ulong stringp)
10084778508Sblueswir1 {
10184778508Sblueswir1 int n = sizeof(abi_ulong);
10284778508Sblueswir1 abi_ulong envp;
10384778508Sblueswir1 abi_ulong argv;
10484778508Sblueswir1
10584778508Sblueswir1 sp -= (envc + 1) * n;
10684778508Sblueswir1 envp = sp;
10784778508Sblueswir1 sp -= (argc + 1) * n;
10884778508Sblueswir1 argv = sp;
10984778508Sblueswir1 sp -= n;
11084778508Sblueswir1 /* FIXME - handle put_user() failures */
11184778508Sblueswir1 put_user_ual(argc, sp);
11284778508Sblueswir1
11384778508Sblueswir1 while (argc-- > 0) {
11484778508Sblueswir1 /* FIXME - handle put_user() failures */
11584778508Sblueswir1 put_user_ual(stringp, argv);
11684778508Sblueswir1 argv += n;
11784778508Sblueswir1 stringp += target_strlen(stringp) + 1;
11884778508Sblueswir1 }
11984778508Sblueswir1 /* FIXME - handle put_user() failures */
12084778508Sblueswir1 put_user_ual(0, argv);
12184778508Sblueswir1 while (envc-- > 0) {
12284778508Sblueswir1 /* FIXME - handle put_user() failures */
12384778508Sblueswir1 put_user_ual(stringp, envp);
12484778508Sblueswir1 envp += n;
12584778508Sblueswir1 stringp += target_strlen(stringp) + 1;
12684778508Sblueswir1 }
12784778508Sblueswir1 /* FIXME - handle put_user() failures */
12884778508Sblueswir1 put_user_ual(0, envp);
12984778508Sblueswir1
13084778508Sblueswir1 return sp;
13184778508Sblueswir1 }
13284778508Sblueswir1
is_there(const char * candidate)1331b50ff64SWarner Losh static bool is_there(const char *candidate)
1341b50ff64SWarner Losh {
1351b50ff64SWarner Losh struct stat fin;
1361b50ff64SWarner Losh
1371b50ff64SWarner Losh /* XXX work around access(2) false positives for superuser */
1381b50ff64SWarner Losh if (access(candidate, X_OK) == 0 && stat(candidate, &fin) == 0 &&
1391b50ff64SWarner Losh S_ISREG(fin.st_mode) && (getuid() != 0 ||
1401b50ff64SWarner Losh (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
1411b50ff64SWarner Losh return true;
1421b50ff64SWarner Losh }
1431b50ff64SWarner Losh
1441b50ff64SWarner Losh return false;
1451b50ff64SWarner Losh }
1461b50ff64SWarner Losh
loader_exec(const char * filename,char ** argv,char ** envp,struct target_pt_regs * regs,struct image_info * infop,struct bsd_binprm * bprm)14784778508Sblueswir1 int loader_exec(const char *filename, char **argv, char **envp,
148d37853f9SWarner Losh struct target_pt_regs *regs, struct image_info *infop,
149d37853f9SWarner Losh struct bsd_binprm *bprm)
15084778508Sblueswir1 {
1511b50ff64SWarner Losh char *path, fullpath[PATH_MAX];
152223005f0SWarner Losh int retval, i;
15384778508Sblueswir1
154223005f0SWarner Losh bprm->p = TARGET_PAGE_SIZE * MAX_ARG_PAGES;
15558b3beb4SWarner Losh for (i = 0; i < MAX_ARG_PAGES; i++) { /* clear page-table */
156d37853f9SWarner Losh bprm->page[i] = NULL;
15758b3beb4SWarner Losh }
1581b50ff64SWarner Losh
1591b50ff64SWarner Losh if (strchr(filename, '/') != NULL) {
1601b50ff64SWarner Losh path = realpath(filename, fullpath);
1611b50ff64SWarner Losh if (path == NULL) {
1621b50ff64SWarner Losh /* Failed to resolve. */
1631b50ff64SWarner Losh return -1;
1641b50ff64SWarner Losh }
1651b50ff64SWarner Losh if (!is_there(path)) {
1661b50ff64SWarner Losh return -1;
1671b50ff64SWarner Losh }
1681b50ff64SWarner Losh } else {
1691b50ff64SWarner Losh path = g_find_program_in_path(filename);
1701b50ff64SWarner Losh if (path == NULL) {
1711b50ff64SWarner Losh return -1;
1721b50ff64SWarner Losh }
1731b50ff64SWarner Losh }
1741b50ff64SWarner Losh
1751b50ff64SWarner Losh retval = open(path, O_RDONLY);
17658b3beb4SWarner Losh if (retval < 0) {
1771b50ff64SWarner Losh g_free(path);
17884778508Sblueswir1 return retval;
17958b3beb4SWarner Losh }
18084778508Sblueswir1
1811b50ff64SWarner Losh bprm->fullpath = path;
182d37853f9SWarner Losh bprm->fd = retval;
183d37853f9SWarner Losh bprm->filename = (char *)filename;
184d37853f9SWarner Losh bprm->argc = count(argv);
185d37853f9SWarner Losh bprm->argv = argv;
186d37853f9SWarner Losh bprm->envc = count(envp);
187d37853f9SWarner Losh bprm->envp = envp;
188d37853f9SWarner Losh
189d37853f9SWarner Losh retval = prepare_binprm(bprm);
19084778508Sblueswir1
19184778508Sblueswir1 if (retval >= 0) {
192d37853f9SWarner Losh if (bprm->buf[0] == 0x7f
193d37853f9SWarner Losh && bprm->buf[1] == 'E'
194d37853f9SWarner Losh && bprm->buf[2] == 'L'
195d37853f9SWarner Losh && bprm->buf[3] == 'F') {
196d37853f9SWarner Losh retval = load_elf_binary(bprm, regs, infop);
19784778508Sblueswir1 } else {
1989bb93180SPeter Maydell fprintf(stderr, "Unknown binary format\n");
19984778508Sblueswir1 return -1;
20084778508Sblueswir1 }
20184778508Sblueswir1 }
20284778508Sblueswir1
20384778508Sblueswir1 if (retval >= 0) {
20484778508Sblueswir1 /* success. Initialize important registers */
20584778508Sblueswir1 do_init_thread(regs, infop);
20684778508Sblueswir1 return retval;
20784778508Sblueswir1 }
20884778508Sblueswir1
20984778508Sblueswir1 /* Something went wrong, return the inode and free the argument pages*/
21084778508Sblueswir1 for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
211d37853f9SWarner Losh g_free(bprm->page[i]);
21284778508Sblueswir1 }
213fa054637SWarner Losh return retval;
21484778508Sblueswir1 }
215