@@ -84,6 +84,44 @@ impl fmt::Display for PIE {
8484 }
8585}
8686
87+ /// Fortification status: `Full`, `Partial`, `None` or `Undecidable`
88+ #[ derive( Debug , Deserialize , Serialize ) ]
89+ pub enum Fortify {
90+ Full ,
91+ Partial ,
92+ None ,
93+ Undecidable ,
94+ }
95+
96+ impl fmt:: Display for Fortify {
97+ #[ cfg( not( feature = "color" ) ) ]
98+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
99+ write ! (
100+ f,
101+ "{:<11}" ,
102+ match self {
103+ Self :: Full => "Full" ,
104+ Self :: Partial => "Partial" ,
105+ Self :: None => "None" ,
106+ Self :: Undecidable => "Undecidable" ,
107+ }
108+ )
109+ }
110+ #[ cfg( feature = "color" ) ]
111+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
112+ write ! (
113+ f,
114+ "{:<11}" ,
115+ match self {
116+ Self :: Full => "Full" . green( ) ,
117+ Self :: Partial => "Partial" . bright_green( ) ,
118+ Self :: None => "None" . red( ) ,
119+ Self :: Undecidable => "Undecidable" . yellow( ) ,
120+ }
121+ )
122+ }
123+ }
124+
87125/// Checksec result struct for ELF32/64 binaries
88126///
89127/// **Example**
@@ -111,10 +149,11 @@ pub struct CheckSecResults {
111149 /// Clang SafeStack (*CFLAGS=*`-fsanitize=safe-stack`)
112150 pub clang_safestack : bool ,
113151 /// Fortify (*CFLAGS=*`-D_FORTIFY_SOURCE`)
114- pub fortify : bool ,
152+ pub fortify : Fortify ,
115153 /// Fortified functions
116154 pub fortified : u32 ,
117- //fortifiable: Option<Vec<OsString>>,
155+ /// Fortifiable functions
156+ pub fortifiable : u32 ,
118157 /// No Execute
119158 pub nx : bool ,
120159 /// Position Inpendent Executable (*CFLAGS=*`-pie -fPIE`)
@@ -129,12 +168,20 @@ pub struct CheckSecResults {
129168impl CheckSecResults {
130169 #[ must_use]
131170 pub fn parse ( elf : & Elf ) -> Self {
171+ let ( fortified, fortifiable) = elf. has_fortified ( ) ;
172+ let fortify = match ( fortified, fortifiable) {
173+ ( 1 .., 0 ) => Fortify :: Full ,
174+ ( 1 .., 1 ..) => Fortify :: Partial ,
175+ ( 0 , 1 ..) => Fortify :: None ,
176+ ( 0 , 0 ) => Fortify :: Undecidable ,
177+ } ;
132178 Self {
133179 canary : elf. has_canary ( ) ,
134180 clang_cfi : elf. has_clang_cfi ( ) ,
135181 clang_safestack : elf. has_clang_safestack ( ) ,
136- fortify : elf. has_fortify ( ) ,
137- fortified : elf. has_fortified ( ) ,
182+ fortify,
183+ fortified,
184+ fortifiable,
138185 nx : elf. has_nx ( ) ,
139186 pie : elf. has_pie ( ) ,
140187 relro : elf. has_relro ( ) ,
@@ -151,12 +198,13 @@ impl fmt::Display for CheckSecResults {
151198 write ! (
152199 f,
153200 "Canary: {} CFI: {} SafeStack: {} Fortify: {} Fortified: {:2} \
154- NX: {} PIE: {} Relro: {} RPATH: {} RUNPATH: {}",
201+ Fortifiable: {:2} NX: {} PIE: {} Relro: {} RPATH: {} RUNPATH: {}",
155202 self . canary,
156203 self . clang_cfi,
157204 self . clang_safestack,
158205 self . fortify,
159206 self . fortified,
207+ self . fortifiable,
160208 self . nx,
161209 self . pie,
162210 self . relro,
@@ -169,17 +217,19 @@ impl fmt::Display for CheckSecResults {
169217 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
170218 write ! (
171219 f,
172- "{} {} {} {} {} {} {} {} {} {:2} {} {} {} {} {} {} {} {} {} {}" ,
220+ "{} {} {} {} {} {} {} {} {} {:2} {} {:2} {} { } {} {} {} {} {} {} {} {}" ,
173221 "Canary:" . bold( ) ,
174222 colorize_bool!( self . canary) ,
175223 "CFI:" . bold( ) ,
176224 colorize_bool!( self . clang_cfi) ,
177225 "SafeStack:" . bold( ) ,
178226 colorize_bool!( self . clang_safestack) ,
179227 "Fortify:" . bold( ) ,
180- colorize_bool! ( self . fortify) ,
228+ self . fortify,
181229 "Fortified:" . bold( ) ,
182230 self . fortified,
231+ "Fortifiable:" . bold( ) ,
232+ self . fortifiable,
183233 "NX:" . bold( ) ,
184234 colorize_bool!( self . nx) ,
185235 "PIE:" . bold( ) ,
@@ -221,10 +271,8 @@ pub trait Properties {
221271 fn has_clang_safestack ( & self ) -> bool ;
222272 /// check for symbols ending in `_chk` from dynstrtab
223273 fn has_fortify ( & self ) -> bool ;
224- /// counts symbols ending in `_chk` from dynstrtab
225- fn has_fortified ( & self ) -> u32 ;
226- // requires running platform to be Linux
227- //fn has_fortifiable(&self) -> u32;
274+ /// counts fortified and fortifiable symbols from dynstrtab
275+ fn has_fortified ( & self ) -> ( u32 , u32 ) ;
228276 /// check `p_flags` of the `PT_GNU_STACK` ELF header
229277 fn has_nx ( & self ) -> bool ;
230278 /// check `d_val` of `DT_FLAGS`/`DT_FLAGS_1` of the `PT_DYN ELF` header
@@ -242,6 +290,94 @@ pub trait Properties {
242290 fn get_dynstr_by_tag ( & self , tag : u64 ) -> Option < String > ;
243291}
244292
293+ // readelf -s -W /lib/x86_64-linux-gnu/libc.so.6 | grep _chk
294+ const FORTIFIABLE_FUNCTIONS : [ & str ; 79 ] = [
295+ "asprintf" ,
296+ "confstr" ,
297+ "dprintf" ,
298+ "explicit_bzero" ,
299+ "fdelt" ,
300+ "fgets" ,
301+ "fgets_unlocked" ,
302+ "fgetws" ,
303+ "fgetws_unlocked" ,
304+ "fprintf" ,
305+ "fread" ,
306+ "fread_unlocked" ,
307+ "fwprintf" ,
308+ "getcwd" ,
309+ "getdomainname" ,
310+ "getgroups" ,
311+ "gethostname" ,
312+ "getlogin_r" ,
313+ "gets" ,
314+ "getwd" ,
315+ "longjmp" ,
316+ "mbsnrtowcs" ,
317+ "mbsrtowcs" ,
318+ "mbstowcs" ,
319+ "memcpy" ,
320+ "memmove" ,
321+ "mempcpy" ,
322+ "memset" ,
323+ "obstack_printf" ,
324+ "obstack_vprintf" ,
325+ "poll" ,
326+ "ppoll" ,
327+ "pread64" ,
328+ "pread" ,
329+ "printf" ,
330+ "ptsname_r" ,
331+ "read" ,
332+ "readlinkat" ,
333+ "readlink" ,
334+ "realpath" ,
335+ "recv" ,
336+ "recvfrom" ,
337+ "snprintf" ,
338+ "sprintf" ,
339+ "stpcpy" ,
340+ "stpncpy" ,
341+ "strcat" ,
342+ "strcpy" ,
343+ "strncat" ,
344+ "strncpy" ,
345+ "swprintf" ,
346+ "syslog" ,
347+ "ttyname_r" ,
348+ "vasprintf" ,
349+ "vdprintf" ,
350+ "vfprintf" ,
351+ "vfwprintf" ,
352+ "vprintf" ,
353+ "vsnprintf" ,
354+ "vsprintf" ,
355+ "vswprintf" ,
356+ "vsyslog" ,
357+ "vwprintf" ,
358+ "wcpcpy" ,
359+ "wcpncpy" ,
360+ "wcrtomb" ,
361+ "wcscat" ,
362+ "wcscpy" ,
363+ "wcsncat" ,
364+ "wcsncpy" ,
365+ "wcsnrtombs" ,
366+ "wcsrtombs" ,
367+ "wcstombs" ,
368+ "wctomb" ,
369+ "wmemcpy" ,
370+ "wmemmove" ,
371+ "wmempcpy" ,
372+ "wmemset" ,
373+ "wprintf" ,
374+ ] ;
375+ /*
376+ * TODO: static assert that FORTIFIABLE_FUNCTIONS is sorted
377+ * unstable library feature 'is_sorted':
378+ * const _: () = assert!(FORTIFIABLE_FUNCTIONS.is_sorted(), "must be sorted for binary search");
379+ */
380+
245381impl Properties for Elf < ' _ > {
246382 fn has_canary ( & self ) -> bool {
247383 for sym in & self . dynsyms {
@@ -286,28 +422,44 @@ impl Properties for Elf<'_> {
286422 }
287423 fn has_fortify ( & self ) -> bool {
288424 for sym in & self . dynsyms {
425+ if !sym. is_function ( ) {
426+ continue ;
427+ }
289428 if let Some ( name) = self . dynstrtab . get_at ( sym. st_name ) {
290- if name. ends_with ( "_chk" ) {
429+ if name. starts_with ( "__" ) && name . ends_with ( "_chk" ) {
291430 return true ;
292431 }
293432 }
294433 }
295434 false
296435 }
297- fn has_fortified ( & self ) -> u32 {
436+ fn has_fortified ( & self ) -> ( u32 , u32 ) {
298437 let mut fortified_count: u32 = 0 ;
438+ let mut fortifiable_count: u32 = 0 ;
299439 for sym in & self . dynsyms {
440+ if !sym. is_function ( ) {
441+ continue ;
442+ }
300443 if let Some ( name) = self . dynstrtab . get_at ( sym. st_name ) {
301- if name. ends_with ( "_chk" ) {
444+ if name. starts_with ( "__" ) && name . ends_with ( "_chk" ) {
302445 fortified_count += 1 ;
446+ } else if FORTIFIABLE_FUNCTIONS . binary_search ( & name) . is_ok ( ) {
447+ fortifiable_count += 1 ;
303448 }
304449 }
305450 }
306- fortified_count
451+ ( fortified_count, fortifiable_count )
307452 }
308453 /*
309454 // requires running platform to be Linux
310- fn has_forifiable(&self) -> Option<Vec<OsString>> {
455+ fn has_fortifiable(&self) -> Vec<String> {
456+ self.dynsyms
457+ .iter()
458+ .filter(goblin::elf::Sym::is_function)
459+ .filter_map(|sym| self.dynstrtab.get_at(sym.st_name))
460+ .filter(|func| FORTIFIABLE_FUNCTIONS.binary_search(func).is_ok())
461+ .map(std::string::ToString::to_string)
462+ .collect()
311463 }
312464 */
313465 fn has_nx ( & self ) -> bool {
0 commit comments