xref: /qemu/bsd-user/bsdload.c (revision d8fcdad2)
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