~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/binfmt_script.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  *  linux/fs/binfmt_script.c
  4  *
  5  *  Copyright (C) 1996  Martin von Löwis
  6  *  original #!-checking implemented by tytso.
  7  */
  8 
  9 #include <linux/module.h>
 10 #include <linux/string.h>
 11 #include <linux/stat.h>
 12 #include <linux/binfmts.h>
 13 #include <linux/init.h>
 14 #include <linux/file.h>
 15 #include <linux/err.h>
 16 #include <linux/fs.h>
 17 
 18 static inline bool spacetab(char c) { return c == ' ' || c == '\t'; }
 19 static inline const char *next_non_spacetab(const char *first, const char *last)
 20 {
 21         for (; first <= last; first++)
 22                 if (!spacetab(*first))
 23                         return first;
 24         return NULL;
 25 }
 26 static inline const char *next_terminator(const char *first, const char *last)
 27 {
 28         for (; first <= last; first++)
 29                 if (spacetab(*first) || !*first)
 30                         return first;
 31         return NULL;
 32 }
 33 
 34 static int load_script(struct linux_binprm *bprm)
 35 {
 36         const char *i_name, *i_sep, *i_arg, *i_end, *buf_end;
 37         struct file *file;
 38         int retval;
 39 
 40         /* Not ours to exec if we don't start with "#!". */
 41         if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
 42                 return -ENOEXEC;
 43 
 44         /*
 45          * This section handles parsing the #! line into separate
 46          * interpreter path and argument strings. We must be careful
 47          * because bprm->buf is not yet guaranteed to be NUL-terminated
 48          * (though the buffer will have trailing NUL padding when the
 49          * file size was smaller than the buffer size).
 50          *
 51          * We do not want to exec a truncated interpreter path, so either
 52          * we find a newline (which indicates nothing is truncated), or
 53          * we find a space/tab/NUL after the interpreter path (which
 54          * itself may be preceded by spaces/tabs). Truncating the
 55          * arguments is fine: the interpreter can re-read the script to
 56          * parse them on its own.
 57          */
 58         buf_end = bprm->buf + sizeof(bprm->buf) - 1;
 59         i_end = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
 60         if (!i_end) {
 61                 i_end = next_non_spacetab(bprm->buf + 2, buf_end);
 62                 if (!i_end)
 63                         return -ENOEXEC; /* Entire buf is spaces/tabs */
 64                 /*
 65                  * If there is no later space/tab/NUL we must assume the
 66                  * interpreter path is truncated.
 67                  */
 68                 if (!next_terminator(i_end, buf_end))
 69                         return -ENOEXEC;
 70                 i_end = buf_end;
 71         }
 72         /* Trim any trailing spaces/tabs from i_end */
 73         while (spacetab(i_end[-1]))
 74                 i_end--;
 75 
 76         /* Skip over leading spaces/tabs */
 77         i_name = next_non_spacetab(bprm->buf+2, i_end);
 78         if (!i_name || (i_name == i_end))
 79                 return -ENOEXEC; /* No interpreter name found */
 80 
 81         /* Is there an optional argument? */
 82         i_arg = NULL;
 83         i_sep = next_terminator(i_name, i_end);
 84         if (i_sep && (*i_sep != '\0'))
 85                 i_arg = next_non_spacetab(i_sep, i_end);
 86 
 87         /*
 88          * If the script filename will be inaccessible after exec, typically
 89          * because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give
 90          * up now (on the assumption that the interpreter will want to load
 91          * this file).
 92          */
 93         if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
 94                 return -ENOENT;
 95 
 96         /*
 97          * OK, we've parsed out the interpreter name and
 98          * (optional) argument.
 99          * Splice in (1) the interpreter's name for argv[0]
100          *           (2) (optional) argument to interpreter
101          *           (3) filename of shell script (replace argv[0])
102          *
103          * This is done in reverse order, because of how the
104          * user environment and arguments are stored.
105          */
106         retval = remove_arg_zero(bprm);
107         if (retval)
108                 return retval;
109         retval = copy_string_kernel(bprm->interp, bprm);
110         if (retval < 0)
111                 return retval;
112         bprm->argc++;
113         *((char *)i_end) = '\0';
114         if (i_arg) {
115                 *((char *)i_sep) = '\0';
116                 retval = copy_string_kernel(i_arg, bprm);
117                 if (retval < 0)
118                         return retval;
119                 bprm->argc++;
120         }
121         retval = copy_string_kernel(i_name, bprm);
122         if (retval)
123                 return retval;
124         bprm->argc++;
125         retval = bprm_change_interp(i_name, bprm);
126         if (retval < 0)
127                 return retval;
128 
129         /*
130          * OK, now restart the process with the interpreter's dentry.
131          */
132         file = open_exec(i_name);
133         if (IS_ERR(file))
134                 return PTR_ERR(file);
135 
136         bprm->interpreter = file;
137         return 0;
138 }
139 
140 static struct linux_binfmt script_format = {
141         .module         = THIS_MODULE,
142         .load_binary    = load_script,
143 };
144 
145 static int __init init_script_binfmt(void)
146 {
147         register_binfmt(&script_format);
148         return 0;
149 }
150 
151 static void __exit exit_script_binfmt(void)
152 {
153         unregister_binfmt(&script_format);
154 }
155 
156 core_initcall(init_script_binfmt);
157 module_exit(exit_script_binfmt);
158 MODULE_DESCRIPTION("Kernel support for scripts starting with #!");
159 MODULE_LICENSE("GPL");
160 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php