-
-
[原创]NX实现机制浅析
-
发表于: 2021-10-12 22:00 21326
-
本文首发于安全客——链接
是否开启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
数组初始化shortopts
,longopts
以及really_longopts
变量内容,这三个变量会传递给getopt_long_only
与getopt_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
字段:
变量m
为elf_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] = 1
,IA32_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(¤t_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(¤t_user()
-
>processes) > rlimit(RLIMIT_NPROC)) {
retval
=
-
EAGAIN;
goto out_ret;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)