首页
社区
课程
招聘
[原创]NX实现机制浅析
发表于: 2021-10-12 22:00 21325

[原创]NX实现机制浅析

erfze 活跃值
12
2021-10-12 22:00
21325

本文首发于安全客——链接

是否开启NX取决于参数-z设置,而gcc仅仅是将-z keyword传递给linker——ld,并不会真正解析该参数:

可以使用-###参数(该参数并不会执行任何命令,仅仅是打印命令及参数。若要执行命令需使用-v参数)查看这一过程:

-v
Print (on standard error output) the commands executed to run the stages of compilation. Also print the version number of the compiler driver program and of the preprocessor and the compiler proper.

-###
Like -v except the commands are not executed and arguments are quoted unless they contain only alphanumeric characters or ./-_. This is useful for shell scripts to capture the driver-generated command lines.

上图中collect2会执行ld,并为其传递参数:

查看下-z参数含义:

下面从源码角度来分析。ld相关源码位于ld目录下,main函数(位于ldmain.c文件)如下:

parse_args函数负责解析参数,位于lexsup.c文件:

parse_args函数首先会由ld_options数组初始化shortoptslongopts以及really_longopts变量内容,这三个变量会传递给getopt_long_onlygetopt_long函数以解析命令行参数:

ldemul_handle_option函数(位于ldemul.c文件)定义如下:

ld_emulation定义为static ld_emulation_xfer_type *ld_emulation;,而ld_emulation_xfer_type结构针对不同架构及目标文件类型,其成员会有不同定义。以ELF文件为例,该函数定义位于elf.em文件中(由下图可以看到同目录下针对其他架构及目标文件类型的.em文件):

上述函数调用关系为:

之后main函数调用lang_process(),位于ldlang.c文件中。lang_process()函数会调用ldemul_before_allocation(),该函数同样位于ldemul.c文件,其定义如下:

查看elf.em

ldelf_before_allocation函数定义位于ldelf.c文件中,其调用bfd_elf_size_dynamic_sections函数:

而该函数会执行如下操作:

上述函数调用关系为:

main函数最终执行ldwrite()函数将stack_flags写入Segment的p_flags字段中:

调用的bfd_final_link函数针对不同目标文件会有不同实现,以ELF文件为例,该函数对应实现为bfd_elf_final_link,位于elflink.c文件。其调用_bfd_elf_compute_section_file_positions函数,该函数再调用assign_file_positions_except_relocs函数:

assign_file_positions_for_load_sections函数调用_bfd_elf_map_sections_to_segments将Sections映射到Segments:

_bfd_elf_map_sections_to_segments负责将stack_flags写入Segment的p_flags字段:

变量melf_segment_map结构类型,其定义位于internal.h文件:

上述函数调用关系为:

最终体现在目标文件中:

处理程序执行的系统调用为execve

查看do_execve函数定义

do_execveat_common函数定义如下:

bprm变量指向linux_binprm结构,该结构存储与可执行文件相关的信息,其定义如下:

do_execveat_common函数会填充bprm变量中内容,之后做一些权限检查,复制及初始化工作,最后调用exec_binprm函数执行可执行文件:

其调用search_binary_handler函数对formats链表进行扫描,并执行其load_binary函数,直到其中一个成功解析了可执行文件格式,否则会返回负值:

对于ELF文件,load_binary对应于load_elf_binary,其定义位于binfmt_elf.c文件:

该函数会执行如下操作:

setup_arg_pages函数定义位于exec.c文件中:

mprotect_fixup函数进行检查过后,会执行如下语句将vm_flags赋值给vma->vm_flags

如此一来,bprm->vma中的vm_flags值为newflags,确定了该虚拟地址空间的访问权限,权限定义位于mm.h文件中:

上述函数调用关系为:

关于NX在CPU层面的实现,以Intel为例。Intel SDM中描述如下:

只有CPUID.80000001H:EDX.NX [bit 20] = 1IA32_EFER.NXE才可以置位为1或是0,其支持PAE,4-level,5-level分页,不支持32位经典分页:

IA32_EFER.NXE置位为1,XD位才能被设置,否则保留:

内核可以通过noexec on|off来配置是否启用NX:

x86_configure_nx()函数:

其中X86_FEATURE_NX定义如下:

_PAGE_NX定义:

_PAGE_BIT_NX定义语句为#define _PAGE_BIT_NX 63,与Intel SDM中描述一致。__supported_pte_mask会在massage_pgprot函数中使用:

2.4版本的checksec对NX的检查一方面是文件:

一方面是/proc/cpuinfo

 
 
 
 
 
 
 
 
ld_config_type config;
......
struct bfd_link_info link_info;
......
int
main (int argc, char **argv)
{
......
  config.build_constructors = TRUE;
  config.rpath_separator = ':';
  config.split_by_reloc = (unsigned) -1;
  config.split_by_file = (bfd_size_type) -1;
  config.make_executable = TRUE;
  config.magic_demand_paged = TRUE;
  config.text_read_only = TRUE;
  config.print_map_discarded = TRUE;
  link_info.disable_target_specific_optimizations = -1;
 
  command_line.warn_mismatch = TRUE;
  command_line.warn_search_mismatch = TRUE;
  command_line.check_section_addresses = -1;
 
  /* We initialize DEMANGLING based on the environment variable
     COLLECT_NO_DEMANGLE.  The gcc collect2 program will demangle the
     output of the linker, unless COLLECT_NO_DEMANGLE is set in the
     environment.  Acting the same way here lets us provide the same
     interface by default.  */
  demangling = getenv ("COLLECT_NO_DEMANGLE") == NULL;
 
  link_info.allow_undefined_version = TRUE;
  ......
  parse_args (argc, argv);
ld_config_type config;
......
struct bfd_link_info link_info;
......
int
main (int argc, char **argv)
{
......
  config.build_constructors = TRUE;
  config.rpath_separator = ':';
  config.split_by_reloc = (unsigned) -1;
  config.split_by_file = (bfd_size_type) -1;
  config.make_executable = TRUE;
  config.magic_demand_paged = TRUE;
  config.text_read_only = TRUE;
  config.print_map_discarded = TRUE;
  link_info.disable_target_specific_optimizations = -1;
 
  command_line.warn_mismatch = TRUE;
  command_line.warn_search_mismatch = TRUE;
  command_line.check_section_addresses = -1;
 
  /* We initialize DEMANGLING based on the environment variable
     COLLECT_NO_DEMANGLE.  The gcc collect2 program will demangle the
     output of the linker, unless COLLECT_NO_DEMANGLE is set in the
     environment.  Acting the same way here lets us provide the same
     interface by default.  */
  demangling = getenv ("COLLECT_NO_DEMANGLE") == NULL;
 
  link_info.allow_undefined_version = TRUE;
  ......
  parse_args (argc, argv);
void
parse_args (unsigned argc, char **argv)
{
......
  char *shortopts;
  struct option *longopts;
  struct option *really_longopts;
......
  shortopts = (char *) xmalloc (OPTION_COUNT * 3 + 2);
  longopts = (struct option *)
      xmalloc (sizeof (*longopts) * (OPTION_COUNT + 1));
  really_longopts = (struct option *)
      malloc (sizeof (*really_longopts) * (OPTION_COUNT + 1));
 
  /* Starting the short option string with '-' is for programs that
     expect options and other ARGV-elements in any order and that care about
     the ordering of the two.  We describe each non-option ARGV-element
     as if it were the argument of an option with character code 1.  */
  shortopts[0] = '-';
  is = 1;
  il = 0;
  irl = 0;
  for (i = 0; i < OPTION_COUNT; i++)
    {
      if (ld_options[i].shortopt != '\0')
    {
      shortopts[is] = ld_options[i].shortopt;
      ++is;
      if (ld_options[i].opt.has_arg == required_argument
          || ld_options[i].opt.has_arg == optional_argument)
        {
          shortopts[is] = ':';
          ++is;
          if (ld_options[i].opt.has_arg == optional_argument)
        {
          shortopts[is] = ':';
          ++is;
        }
        }
    }
      if (ld_options[i].opt.name != NULL)
    {
      if (ld_options[i].control == EXACTLY_TWO_DASHES)
        {
          really_longopts[irl] = ld_options[i].opt;
          ++irl;
        }
      else
        {
          longopts[il] = ld_options[i].opt;
          ++il;
        }
    }
    }
  shortopts[is] = '\0';
  longopts[il].name = NULL;
  really_longopts[irl].name = NULL;
......
void
parse_args (unsigned argc, char **argv)
{
......
  char *shortopts;
  struct option *longopts;
  struct option *really_longopts;
......
  shortopts = (char *) xmalloc (OPTION_COUNT * 3 + 2);
  longopts = (struct option *)
      xmalloc (sizeof (*longopts) * (OPTION_COUNT + 1));
  really_longopts = (struct option *)
      malloc (sizeof (*really_longopts) * (OPTION_COUNT + 1));
 
  /* Starting the short option string with '-' is for programs that
     expect options and other ARGV-elements in any order and that care about
     the ordering of the two.  We describe each non-option ARGV-element
     as if it were the argument of an option with character code 1.  */
  shortopts[0] = '-';
  is = 1;
  il = 0;
  irl = 0;
  for (i = 0; i < OPTION_COUNT; i++)
    {
      if (ld_options[i].shortopt != '\0')
    {
      shortopts[is] = ld_options[i].shortopt;
      ++is;
      if (ld_options[i].opt.has_arg == required_argument
          || ld_options[i].opt.has_arg == optional_argument)
        {
          shortopts[is] = ':';
          ++is;
          if (ld_options[i].opt.has_arg == optional_argument)
        {
          shortopts[is] = ':';
          ++is;
        }
        }
    }
      if (ld_options[i].opt.name != NULL)
    {
      if (ld_options[i].control == EXACTLY_TWO_DASHES)
        {
          really_longopts[irl] = ld_options[i].opt;
          ++irl;
        }
      else
        {
          longopts[il] = ld_options[i].opt;
          ++il;
        }
    }
    }
  shortopts[is] = '\0';
  longopts[il].name = NULL;
  really_longopts[irl].name = NULL;
......
......
last_optind = -1;
  while (1)
    {
      int longind;
      int optc;
      static unsigned int defsym_count;
 
      /* Using last_optind lets us avoid calling ldemul_parse_args
     multiple times on a single option, which would lead to
     confusion in the internal static variables maintained by
     getopt.  This could otherwise happen for an argument like
     -nx, in which the -n is parsed as a single option, and we
     loop around to pick up the -x.  */
      if (optind != last_optind)
    if (ldemul_parse_args (argc, argv))
      continue;
 
      /* getopt_long_only is like getopt_long, but '-' as well as '--'
     can indicate a long option.  */
      opterr = 0;
      last_optind = optind;
      optc = getopt_long_only (argc, argv, shortopts, longopts, &longind);
      if (optc == '?')
    {
      optind = last_optind;
      optc = getopt_long (argc, argv, "-", really_longopts, &longind);
    }
 
      if (ldemul_handle_option (optc))
    continue;
 
      if (optc == -1)
    break;
......
......
last_optind = -1;
  while (1)
    {
      int longind;
      int optc;
      static unsigned int defsym_count;
 
      /* Using last_optind lets us avoid calling ldemul_parse_args
     multiple times on a single option, which would lead to
     confusion in the internal static variables maintained by
     getopt.  This could otherwise happen for an argument like
     -nx, in which the -n is parsed as a single option, and we
     loop around to pick up the -x.  */
      if (optind != last_optind)
    if (ldemul_parse_args (argc, argv))
      continue;
 
      /* getopt_long_only is like getopt_long, but '-' as well as '--'
     can indicate a long option.  */
      opterr = 0;
      last_optind = optind;
      optc = getopt_long_only (argc, argv, shortopts, longopts, &longind);
      if (optc == '?')
    {
      optind = last_optind;
      optc = getopt_long (argc, argv, "-", really_longopts, &longind);
    }
 
      if (ldemul_handle_option (optc))
    continue;
 
      if (optc == -1)
    break;
......
bfd_boolean
ldemul_handle_option (int optc)
{
  if (ld_emulation->handle_option)
    return (*ld_emulation->handle_option) (optc);
  return FALSE;
}
bfd_boolean
ldemul_handle_option (int optc)
{
  if (ld_emulation->handle_option)
    return (*ld_emulation->handle_option) (optc);
  return FALSE;
}
 
static bfd_boolean
gld${EMULATION_NAME}_handle_option (int optc)
{
  switch (optc)
    {
    default:
      return FALSE;
    ......
    case 'z':
    ......
      else if (strcmp (optarg, "execstack") == 0)
    {
      link_info.execstack = TRUE;
      link_info.noexecstack = FALSE;
    }
      else if (strcmp (optarg, "noexecstack") == 0)
    {
      link_info.noexecstack = TRUE;
      link_info.execstack = FALSE;
    }
static bfd_boolean
gld${EMULATION_NAME}_handle_option (int optc)
{
  switch (optc)
    {
    default:
      return FALSE;
    ......
    case 'z':
    ......
      else if (strcmp (optarg, "execstack") == 0)
    {
      link_info.execstack = TRUE;
      link_info.noexecstack = FALSE;
    }
      else if (strcmp (optarg, "noexecstack") == 0)
    {
      link_info.noexecstack = TRUE;
      link_info.execstack = FALSE;
    }
 
 
void
ldemul_before_allocation (void)
{
  ld_emulation->before_allocation ();
}
void
ldemul_before_allocation (void)
{
  ld_emulation->before_allocation ();
}
static void
gld${EMULATION_NAME}_before_allocation (void)
{
  ldelf_before_allocation (audit, depaudit, ${ELF_INTERPRETER_NAME});
}
static void
gld${EMULATION_NAME}_before_allocation (void)
{
  ldelf_before_allocation (audit, depaudit, ${ELF_INTERPRETER_NAME});
}
......
  if (! (bfd_elf_size_dynamic_sections
     (link_info.output_bfd, command_line.soname, rpath,
      command_line.filter_shlib, audit, depaudit,
      (const char * const *) command_line.auxiliary_filters,
      &link_info, &sinterp)))
    einfo (_("%F%P: failed to set dynamic section sizes: %E\n"));
......
......
  if (! (bfd_elf_size_dynamic_sections
     (link_info.output_bfd, command_line.soname, rpath,
      command_line.filter_shlib, audit, depaudit,
      (const char * const *) command_line.auxiliary_filters,
      &link_info, &sinterp)))
    einfo (_("%F%P: failed to set dynamic section sizes: %E\n"));
......
......
  if (info->execstack)
    elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
  else if (info->noexecstack)
    elf_stack_flags (output_bfd) = PF_R | PF_W;
......
......
  if (info->execstack)
    elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
  else if (info->noexecstack)
    elf_stack_flags (output_bfd) = PF_R | PF_W;
......
 
 
void
ldwrite (void)
{
  /* Reset error indicator, which can typically something like invalid
     format from opening up the .o files.  */
  bfd_set_error (bfd_error_no_error);
  lang_clear_os_map ();
  lang_for_each_statement (build_link_order);
 
  if (config.split_by_reloc != (unsigned) -1
      || config.split_by_file != (bfd_size_type) -1)
    split_sections (link_info.output_bfd, &link_info);
  if (!bfd_final_link (link_info.output_bfd, &link_info))
    {
      /* If there was an error recorded, print it out.  Otherwise assume
     an appropriate error message like unknown symbol was printed
     out.  */
 
      if (bfd_get_error () != bfd_error_no_error)
    einfo (_("%F%P: final link failed: %E\n"));
      else
    xexit (1);
    }
}
void
ldwrite (void)
{
  /* Reset error indicator, which can typically something like invalid
     format from opening up the .o files.  */
  bfd_set_error (bfd_error_no_error);
  lang_clear_os_map ();
  lang_for_each_statement (build_link_order);
 
  if (config.split_by_reloc != (unsigned) -1
      || config.split_by_file != (bfd_size_type) -1)
    split_sections (link_info.output_bfd, &link_info);
  if (!bfd_final_link (link_info.output_bfd, &link_info))
    {
      /* If there was an error recorded, print it out.  Otherwise assume
     an appropriate error message like unknown symbol was printed
     out.  */
 
      if (bfd_get_error () != bfd_error_no_error)
    einfo (_("%F%P: final link failed: %E\n"));
      else
    xexit (1);
    }
}
/* Work out the file positions of all the sections.  This is called by
   _bfd_elf_compute_section_file_positions.  All the section sizes and
   VMAs must be known before this is called.
 
   Reloc sections come in two flavours: Those processed specially as
   "side-channel" data attached to a section to which they apply, and those that
   bfd doesn't process as relocations.  The latter sort are stored in a normal
   bfd section by bfd_section_from_shdr.  We don't consider the former sort
   here, unless they form part of the loadable image.  Reloc sections not
   assigned here (and compressed debugging sections and CTF sections which
   nothing else in the file can rely upon) will be handled later by
   assign_file_positions_for_relocs.
 
   We also don't set the positions of the .symtab and .strtab here.  */
 
static bfd_boolean
assign_file_positions_except_relocs (bfd *abfd,
                     struct bfd_link_info *link_info)
{
  struct elf_obj_tdata *tdata = elf_tdata (abfd);
  Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd);
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  unsigned int alloc;
 
  if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0
      && bfd_get_format (abfd) != bfd_core)
    {
      ......
    }
  else
    {
      /* Assign file positions for the loaded sections based on the
     assignment of sections to segments.  */
      if (!assign_file_positions_for_load_sections (abfd, link_info))
    return FALSE;
 
      /* And for non-load sections.  */
      if (!assign_file_positions_for_non_load_sections (abfd, link_info))
    return FALSE;
    }
 
  if (!(*bed->elf_backend_modify_headers) (abfd, link_info))
    return FALSE;
 
  /* Write out the program headers.  */
  alloc = i_ehdrp->e_phnum;
  if (alloc != 0)
    {
      if (bfd_seek (abfd, i_ehdrp->e_phoff, SEEK_SET) != 0
      || bed->s->write_out_phdrs (abfd, tdata->phdr, alloc) != 0)
    return FALSE;
    }
 
  return TRUE;
}
/* Work out the file positions of all the sections.  This is called by
   _bfd_elf_compute_section_file_positions.  All the section sizes and
   VMAs must be known before this is called.
 
   Reloc sections come in two flavours: Those processed specially as
   "side-channel" data attached to a section to which they apply, and those that
   bfd doesn't process as relocations.  The latter sort are stored in a normal
   bfd section by bfd_section_from_shdr.  We don't consider the former sort
   here, unless they form part of the loadable image.  Reloc sections not
   assigned here (and compressed debugging sections and CTF sections which
   nothing else in the file can rely upon) will be handled later by
   assign_file_positions_for_relocs.
 
   We also don't set the positions of the .symtab and .strtab here.  */
 
static bfd_boolean
assign_file_positions_except_relocs (bfd *abfd,
                     struct bfd_link_info *link_info)
{
  struct elf_obj_tdata *tdata = elf_tdata (abfd);
  Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd);
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  unsigned int alloc;
 
  if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0
      && bfd_get_format (abfd) != bfd_core)
    {
      ......
    }
  else
    {
      /* Assign file positions for the loaded sections based on the
     assignment of sections to segments.  */
      if (!assign_file_positions_for_load_sections (abfd, link_info))
    return FALSE;
 
      /* And for non-load sections.  */
      if (!assign_file_positions_for_non_load_sections (abfd, link_info))
    return FALSE;
    }
 
  if (!(*bed->elf_backend_modify_headers) (abfd, link_info))
    return FALSE;
 
  /* Write out the program headers.  */
  alloc = i_ehdrp->e_phnum;
  if (alloc != 0)
    {
      if (bfd_seek (abfd, i_ehdrp->e_phoff, SEEK_SET) != 0
      || bed->s->write_out_phdrs (abfd, tdata->phdr, alloc) != 0)
    return FALSE;
    }
 
  return TRUE;
}
static bfd_boolean
assign_file_positions_for_load_sections (bfd *abfd,
                     struct bfd_link_info *link_info)
{
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  struct elf_segment_map *m;
  struct elf_segment_map *phdr_load_seg;
  Elf_Internal_Phdr *phdrs;
  Elf_Internal_Phdr *p;
  file_ptr off;
  bfd_size_type maxpagesize;
  unsigned int alloc, actual;
  unsigned int i, j;
  struct elf_segment_map **sorted_seg_map;
 
  if (link_info == NULL
      && !_bfd_elf_map_sections_to_segments (abfd, link_info))
    return FALSE;
......
static bfd_boolean
assign_file_positions_for_load_sections (bfd *abfd,
                     struct bfd_link_info *link_info)
{
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
  struct elf_segment_map *m;
  struct elf_segment_map *phdr_load_seg;
  Elf_Internal_Phdr *phdrs;
  Elf_Internal_Phdr *p;
  file_ptr off;
  bfd_size_type maxpagesize;
  unsigned int alloc, actual;
  unsigned int i, j;
  struct elf_segment_map **sorted_seg_map;
 
  if (link_info == NULL
      && !_bfd_elf_map_sections_to_segments (abfd, link_info))
    return FALSE;
......
......
if (elf_stack_flags (abfd))
    {
      amt = sizeof (struct elf_segment_map);
      m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
      if (m == NULL)
        goto error_return;
      m->next = NULL;
      m->p_type = PT_GNU_STACK;
      m->p_flags = elf_stack_flags (abfd);
      m->p_align = bed->stack_align;
      m->p_flags_valid = 1;
      m->p_align_valid = m->p_align != 0;
      if (info->stacksize > 0)
        {
          m->p_size = info->stacksize;
          m->p_size_valid = 1;
        }
 
      *pm = m;
      pm = &m->next;
    }
......
......
if (elf_stack_flags (abfd))
    {
      amt = sizeof (struct elf_segment_map);
      m = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
      if (m == NULL)
        goto error_return;
      m->next = NULL;
      m->p_type = PT_GNU_STACK;
      m->p_flags = elf_stack_flags (abfd);
      m->p_align = bed->stack_align;
      m->p_flags_valid = 1;
      m->p_align_valid = m->p_align != 0;
      if (info->stacksize > 0)
        {
          m->p_size = info->stacksize;
          m->p_size_valid = 1;
        }
 
      *pm = m;
      pm = &m->next;
    }
......
/* This structure is used to describe how sections should be assigned
   to program segments.  */
 
struct elf_segment_map
{
  /* Next program segment.  */
  struct elf_segment_map *next;
  /* Program segment type*/
  unsigned long p_type;
  /* Program segment flags.  */
  unsigned long p_flags;
  /* Program segment physical address.  */
  bfd_vma p_paddr;
  /* Program segment virtual address offset from section vma.  */
  bfd_vma p_vaddr_offset;
  /* Program segment alignment.  */
  bfd_vma p_align;
  /* Segment size in file and memory */
  bfd_vma p_size;
  /* Whether the p_flags field is valid; if not, the flags are based
     on the section flags.  */
  unsigned int p_flags_valid : 1;
  /* Whether the p_paddr field is valid; if not, the physical address
     is based on the section lma values.  */
  unsigned int p_paddr_valid : 1;
  /* Whether the p_align field is valid; if not, PT_LOAD segment
     alignment is based on the default maximum page size.  */
  unsigned int p_align_valid : 1;
  /* Whether the p_size field is valid; if not, the size are based
     on the section sizes.  */
  unsigned int p_size_valid : 1;
  /* Whether this segment includes the file header.  */
  unsigned int includes_filehdr : 1;
  /* Whether this segment includes the program headers.  */
  unsigned int includes_phdrs : 1;
  /* Assume this PT_LOAD header has an lma of zero when sorting
     headers before assigning file offsets.  PT_LOAD headers with this
     flag set are placed after one with includes_filehdr set, and
     before PT_LOAD headers without this flag set*/
  unsigned int no_sort_lma : 1;
  /* Index holding original order before sorting segments.  */
  unsigned int idx;
  /* Number of sections (may be 0).  */
  unsigned int count;
  /* Sections.  Actual number of elements is in count field.  */
  asection *sections[1];
};
/* This structure is used to describe how sections should be assigned
   to program segments.  */
 
struct elf_segment_map
{
  /* Next program segment.  */
  struct elf_segment_map *next;
  /* Program segment type*/
  unsigned long p_type;
  /* Program segment flags.  */
  unsigned long p_flags;
  /* Program segment physical address.  */
  bfd_vma p_paddr;
  /* Program segment virtual address offset from section vma.  */
  bfd_vma p_vaddr_offset;
  /* Program segment alignment.  */
  bfd_vma p_align;
  /* Segment size in file and memory */
  bfd_vma p_size;
  /* Whether the p_flags field is valid; if not, the flags are based
     on the section flags.  */
  unsigned int p_flags_valid : 1;
  /* Whether the p_paddr field is valid; if not, the physical address
     is based on the section lma values.  */
  unsigned int p_paddr_valid : 1;
  /* Whether the p_align field is valid; if not, PT_LOAD segment
     alignment is based on the default maximum page size.  */
  unsigned int p_align_valid : 1;
  /* Whether the p_size field is valid; if not, the size are based
     on the section sizes.  */
  unsigned int p_size_valid : 1;
  /* Whether this segment includes the file header.  */
  unsigned int includes_filehdr : 1;
  /* Whether this segment includes the program headers.  */
  unsigned int includes_phdrs : 1;
  /* Assume this PT_LOAD header has an lma of zero when sorting
     headers before assigning file offsets.  PT_LOAD headers with this
     flag set are placed after one with includes_filehdr set, and
     before PT_LOAD headers without this flag set*/
  unsigned int no_sort_lma : 1;
  /* Index holding original order before sorting segments.  */
  unsigned int idx;
  /* Number of sections (may be 0).  */
  unsigned int count;
  /* Sections.  Actual number of elements is in count field.  */
  asection *sections[1];
};
 
 
 
SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}
SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}
int do_execve(struct filename *filename,
    const char __user *const __user *__argv,
    const char __user *const __user *__envp)
{
    struct user_arg_ptr argv = { .ptr.native = __argv };
    struct user_arg_ptr envp = { .ptr.native = __envp };
    return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
int do_execve(struct filename *filename,
    const char __user *const __user *__argv,
    const char __user *const __user *__envp)
{
    struct user_arg_ptr argv = { .ptr.native = __argv };
    struct user_arg_ptr envp = { .ptr.native = __envp };
    return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
/*
 * sys_execve() executes a new program.
 */
static int do_execveat_common(int fd, struct filename *filename,
                  struct user_arg_ptr argv,
                  struct user_arg_ptr envp,
                  int flags)
{
    char *pathbuf = NULL;
    struct linux_binprm *bprm;
    struct file *file;
    struct files_struct *displaced;
    int retval;
 
    if (IS_ERR(filename))
        return PTR_ERR(filename);
 
    /*
     * We move the actual failure in case of RLIMIT_NPROC excess from
     * set*uid() to execve() because too many poorly written programs
     * don't check setuid() return code.  Here we additionally recheck
     * whether NPROC limit is still exceeded.
     */
    if ((current->flags & PF_NPROC_EXCEEDED) &&
        atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
        retval = -EAGAIN;
        goto out_ret;
    }
 
    /* We're below the limit (still or again), so we don't want to make
     * further execve() calls fail. */
    current->flags &= ~PF_NPROC_EXCEEDED;
 
    retval = unshare_files(&displaced);
    if (retval)
        goto out_ret;
 
    retval = -ENOMEM;
    bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
    if (!bprm)
        goto out_files;
 
    retval = prepare_bprm_creds(bprm);
    if (retval)
        goto out_free;
 
    check_unsafe_exec(bprm);
    current->in_execve = 1;
 
    file = do_open_execat(fd, filename, flags);
    retval = PTR_ERR(file);
    if (IS_ERR(file))
        goto out_unmark;
 
    sched_exec();
 
    bprm->file = file;
    if (fd == AT_FDCWD || filename->name[0] == '/') {
        bprm->filename = filename->name;
    } else {
        if (filename->name[0] == '\0')
            pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd);
        else
            pathbuf = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s",
                        fd, filename->name);
        if (!pathbuf) {
            retval = -ENOMEM;
            goto out_unmark;
        }
        /*
         * Record that a name derived from an O_CLOEXEC fd will be
         * inaccessible after exec. Relies on having exclusive access to
         * current->files (due to unshare_files above).
         */
        if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt)))
            bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE;
        bprm->filename = pathbuf;
    }
    bprm->interp = bprm->filename;
 
    retval = bprm_mm_init(bprm);
    if (retval)
        goto out_unmark;
 
    bprm->argc = count(argv, MAX_ARG_STRINGS);
    if ((retval = bprm->argc) < 0)
        goto out;
 
    bprm->envc = count(envp, MAX_ARG_STRINGS);
    if ((retval = bprm->envc) < 0)
        goto out;
 
    retval = prepare_binprm(bprm);
    if (retval < 0)
        goto out;
 
    retval = copy_strings_kernel(1, &bprm->filename, bprm);
    if (retval < 0)
        goto out;
 
    bprm->exec = bprm->p;
    retval = copy_strings(bprm->envc, envp, bprm);
    if (retval < 0)
        goto out;
 
    retval = copy_strings(bprm->argc, argv, bprm);
    if (retval < 0)
        goto out;
 
    would_dump(bprm, bprm->file);
 
    retval = exec_binprm(bprm);
    if (retval < 0)
        goto out;
 
    /* execve succeeded */
    current->fs->in_exec = 0;
    current->in_execve = 0;
    membarrier_execve(current);
    acct_update_integrals(current);
    task_numa_free(current);
    free_bprm(bprm);
    kfree(pathbuf);
    putname(filename);
    if (displaced)
        put_files_struct(displaced);
    return retval;
 
out:
/*
 * sys_execve() executes a new program.
 */
static int do_execveat_common(int fd, struct filename *filename,
                  struct user_arg_ptr argv,
                  struct user_arg_ptr envp,
                  int flags)
{
    char *pathbuf = NULL;
    struct linux_binprm *bprm;
    struct file *file;
    struct files_struct *displaced;
    int retval;
 
    if (IS_ERR(filename))
        return PTR_ERR(filename);
 
    /*
     * We move the actual failure in case of RLIMIT_NPROC excess from
     * set*uid() to execve() because too many poorly written programs
     * don't check setuid() return code.  Here we additionally recheck
     * whether NPROC limit is still exceeded.
     */
    if ((current->flags & PF_NPROC_EXCEEDED) &&
        atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
        retval = -EAGAIN;
        goto out_ret;
    }

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-11-29 14:56 被erfze编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (1)
雪    币: 14488
活跃值: (17488)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2021-10-13 15:34
0
游客
登录 | 注册 方可回帖
返回
//