1010 object:: {
1111 Architecture , Endianness , FileKind , Object , SectionIndex , SymbolScope ,
1212 elf:: {
13- ET_DYN , ET_EXEC , FileHeader32 , FileHeader64 , PF_X , PT_GNU_STACK , SHN_UNDEF , STB_GLOBAL ,
14- STB_WEAK , STV_DEFAULT , STV_HIDDEN ,
13+ DF_1_NOW , DF_BIND_NOW , DF_TEXTREL , DT_BIND_NOW , DT_FLAGS , DT_FLAGS_1 , DT_TEXTREL ,
14+ ET_DYN , ET_EXEC , FileHeader32 , FileHeader64 , PF_W , PF_X , PT_GNU_RELRO , PT_GNU_STACK ,
15+ PT_LOAD , SHN_UNDEF , STB_GLOBAL , STB_WEAK , STV_DEFAULT , STV_HIDDEN ,
1516 } ,
1617 macho:: { LC_CODE_SIGNATURE , MH_OBJECT , MH_TWOLEVEL , MachHeader32 , MachHeader64 } ,
1718 read:: {
@@ -1027,9 +1028,14 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
10271028
10281029 let versions = sections. versions ( endian, data) ?;
10291030
1031+ let mut has_dynamic_section = false ;
1032+ let mut has_bind_now = false ;
1033+ let mut has_text_relocations = false ;
1034+
10301035 for ( section_index, section) in sections. iter ( ) . enumerate ( ) {
10311036 // Dynamic sections defined needed libraries, which we validate.
10321037 if let Some ( ( entries, index) ) = section. dynamic ( endian, data) ? {
1038+ has_dynamic_section = true ;
10331039 let strings = sections. strings ( endian, data, index) . unwrap_or_default ( ) ;
10341040
10351041 for entry in entries {
@@ -1087,6 +1093,20 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
10871093 }
10881094 }
10891095 }
1096+
1097+ match entry. tag32 ( endian) {
1098+ Some ( DT_BIND_NOW ) => has_bind_now = true ,
1099+ Some ( DT_FLAGS ) => {
1100+ let flags = entry. val32 ( endian) . unwrap_or_default ( ) ;
1101+ has_bind_now |= flags & DF_BIND_NOW != 0 ;
1102+ has_text_relocations |= flags & DF_TEXTREL != 0 ;
1103+ }
1104+ Some ( DT_FLAGS_1 ) => {
1105+ has_bind_now |= entry. val32 ( endian) . unwrap_or_default ( ) & DF_1_NOW != 0 ;
1106+ }
1107+ Some ( DT_TEXTREL ) => has_text_relocations = true ,
1108+ _ => { }
1109+ }
10901110 }
10911111 }
10921112
@@ -1180,8 +1200,9 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
11801200 }
11811201 }
11821202
1183- // Verify that objects are not requesting an executable stack. For backwards compatibility,
1184- // Linux (the kernel when loading an executable, and glibc when loading a shared library)
1203+ // Verify that objects are not requesting an executable stack and that other hardening
1204+ // properties exist in the linked outputs. For backwards compatibility, Linux (the kernel
1205+ // when loading an executable, and glibc when loading a shared library)
11851206 // assumes you need an executable stack unless you request otherwise. In linked outputs
11861207 // (executables and shared libraries) this is in the program header: the flags of a
11871208 // PT_GNU_STACK entry specify stack permissions, and the default if unspecified is RWX. In
@@ -1193,16 +1214,35 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
11931214 // .note.GNU-stack section, which we are overriding with -Wl,-z,noexecstack.
11941215
11951216 if matches ! ( elf. e_type( endian) , ET_EXEC | ET_DYN ) {
1217+ if elf. e_type ( endian) == ET_EXEC {
1218+ context. errors . push ( format ! (
1219+ "{} is not position-independent (ELF type is ET_EXEC)" ,
1220+ path. display( ) ,
1221+ ) ) ;
1222+ }
1223+
11961224 let mut found_pt_gnu_stack = false ;
1225+ let mut found_pt_gnu_relro = false ;
11971226 for phdr in elf. program_headers ( endian, data) ? {
1198- if phdr. p_type ( endian) != PT_GNU_STACK {
1199- continue ;
1200- }
1201- found_pt_gnu_stack = true ;
1202- if ( phdr. p_flags ( endian) & PF_X ) != 0 {
1203- context
1204- . errors
1205- . push ( format ! ( "{} requests executable stack" , path. display( ) ) ) ;
1227+ let flags = phdr. p_flags ( endian) ;
1228+
1229+ match phdr. p_type ( endian) {
1230+ PT_GNU_STACK => {
1231+ found_pt_gnu_stack = true ;
1232+ if flags & PF_X != 0 {
1233+ context
1234+ . errors
1235+ . push ( format ! ( "{} requests executable stack" , path. display( ) ) ) ;
1236+ }
1237+ }
1238+ PT_GNU_RELRO => found_pt_gnu_relro = true ,
1239+ PT_LOAD if flags & ( PF_W | PF_X ) == ( PF_W | PF_X ) => {
1240+ context. errors . push ( format ! (
1241+ "{} has a writable and executable PT_LOAD segment" ,
1242+ path. display( ) ,
1243+ ) ) ;
1244+ }
1245+ _ => { }
12061246 }
12071247 }
12081248 if !found_pt_gnu_stack {
@@ -1211,6 +1251,23 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
12111251 path. display( ) ,
12121252 ) ) ;
12131253 }
1254+ if !found_pt_gnu_relro {
1255+ context. errors . push ( format ! (
1256+ "{} is missing a PT_GNU_RELRO segment" ,
1257+ path. display( ) ,
1258+ ) ) ;
1259+ }
1260+ if has_dynamic_section && !has_bind_now {
1261+ context. errors . push ( format ! (
1262+ "{} does not request immediate symbol binding" ,
1263+ path. display( ) ,
1264+ ) ) ;
1265+ }
1266+ if has_text_relocations {
1267+ context
1268+ . errors
1269+ . push ( format ! ( "{} contains text relocations" , path. display( ) ) ) ;
1270+ }
12141271 }
12151272
12161273 Ok ( ( ) )
0 commit comments