LCOV - code coverage report
Current view: top level - fs - binfmt_script.c (source / functions) Hit Total Coverage
Test: fstests of 6.5.0-rc4-xfsa @ Mon Jul 31 20:08:27 PDT 2023 Lines: 42 53 79.2 %
Date: 2023-07-31 20:08:27 Functions: 2 4 50.0 %

          Line data    Source code
       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      260625 : 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       34227 :         for (; first <= last; first++)
      22       34228 :                 if (!spacetab(*first))
      23             :                         return first;
      24             :         return NULL;
      25             : }
      26       21603 : static inline const char *next_terminator(const char *first, const char *last)
      27             : {
      28      221410 :         for (; first <= last; first++)
      29      204800 :                 if (spacetab(*first) || !*first)
      30        4993 :                         return first;
      31             :         return NULL;
      32             : }
      33             : 
      34    15487963 : static int load_script(struct linux_binprm *bprm)
      35             : {
      36    15487963 :         const char *i_name, *i_sep, *i_arg, *i_end, *buf_end;
      37    15487963 :         struct file *file;
      38    15487963 :         int retval;
      39             : 
      40             :         /* Not ours to exec if we don't start with "#!". */
      41    15487963 :         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       21599 :         buf_end = bprm->buf + sizeof(bprm->buf) - 1;
      59       21599 :         i_end = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
      60       21597 :         if (!i_end) {
      61           0 :                 i_end = next_non_spacetab(bprm->buf + 2, buf_end);
      62           0 :                 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           0 :                 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       21597 :         while (spacetab(i_end[-1]))
      74           0 :                 i_end--;
      75             : 
      76             :         /* Skip over leading spaces/tabs */
      77       21597 :         i_name = next_non_spacetab(bprm->buf+2, i_end);
      78       21597 :         if (!i_name || (i_name == i_end))
      79             :                 return -ENOEXEC; /* No interpreter name found */
      80             : 
      81             :         /* Is there an optional argument? */
      82       21597 :         i_arg = NULL;
      83       21597 :         i_sep = next_terminator(i_name, i_end);
      84       21597 :         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       21597 :         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       21597 :         retval = remove_arg_zero(bprm);
     107       21605 :         if (retval)
     108             :                 return retval;
     109       21605 :         retval = copy_string_kernel(bprm->interp, bprm);
     110       21605 :         if (retval < 0)
     111             :                 return retval;
     112       21605 :         bprm->argc++;
     113       21605 :         *((char *)i_end) = '\0';
     114       21605 :         if (i_arg) {
     115        4995 :                 *((char *)i_sep) = '\0';
     116        4995 :                 retval = copy_string_kernel(i_arg, bprm);
     117        4996 :                 if (retval < 0)
     118             :                         return retval;
     119        4996 :                 bprm->argc++;
     120             :         }
     121       21606 :         retval = copy_string_kernel(i_name, bprm);
     122       21606 :         if (retval)
     123             :                 return retval;
     124       21606 :         bprm->argc++;
     125       21606 :         retval = bprm_change_interp(i_name, bprm);
     126       21600 :         if (retval < 0)
     127             :                 return retval;
     128             : 
     129             :         /*
     130             :          * OK, now restart the process with the interpreter's dentry.
     131             :          */
     132       21600 :         file = open_exec(i_name);
     133       21606 :         if (IS_ERR(file))
     134           0 :                 return PTR_ERR(file);
     135             : 
     136       21606 :         bprm->interpreter = file;
     137       21606 :         return 0;
     138             : }
     139             : 
     140             : static struct linux_binfmt script_format = {
     141             :         .module         = THIS_MODULE,
     142             :         .load_binary    = load_script,
     143             : };
     144             : 
     145           0 : static int __init init_script_binfmt(void)
     146             : {
     147           0 :         register_binfmt(&script_format);
     148           0 :         return 0;
     149             : }
     150             : 
     151           0 : static void __exit exit_script_binfmt(void)
     152             : {
     153           0 :         unregister_binfmt(&script_format);
     154           0 : }
     155             : 
     156             : core_initcall(init_script_binfmt);
     157             : module_exit(exit_script_binfmt);
     158             : MODULE_LICENSE("GPL");

Generated by: LCOV version 1.14