1 use goblin::elf::section_header::SHT_GNU_HASH;
2 use goblin::elf::sym::Sym;
3 use goblin::elf::Elf;
4 use goblin::elf32::gnu_hash::GnuHash as GnuHash32;
5 use goblin::elf64::gnu_hash::GnuHash as GnuHash64;
6 
7 #[repr(C)]
8 #[repr(align(64))] // Align to cache lines
9 pub struct AlignedData<T: ?Sized>(T);
10 
parse_gnu_hash_section(base: &[u8], symbol_name: &str) -> Result<Sym, &'static str>11 fn parse_gnu_hash_section(base: &[u8], symbol_name: &str) -> Result<Sym, &'static str> {
12     let obj = Elf::parse(base).map_err(|_| "cannot parse ELF file")?;
13     let hash_section = obj
14         .section_headers
15         .iter()
16         .find(|s| s.sh_type == SHT_GNU_HASH)
17         .ok_or("object does not contain .gnu.hash section")?;
18     let (start, length) = (
19         hash_section.sh_offset as usize,
20         hash_section.sh_size as usize,
21     );
22     let hashtab: &[u8] = &base[start..(start + length)];
23     let dynsyms = obj.dynsyms.to_vec();
24     let section = unsafe {
25         if obj.is_64 {
26             GnuHash64::from_raw_table(hashtab, &dynsyms)?.find(symbol_name, &obj.dynstrtab)
27         } else {
28             GnuHash32::from_raw_table(hashtab, &dynsyms)?.find(symbol_name, &obj.dynstrtab)
29         }
30     };
31     section.copied().ok_or("cannot find symbol")
32 }
33 
34 // Use lazy_parse and assembles the Elf with only parts we care
parse_text_section_size_lazy(base: &[u8]) -> Result<u64, &'static str>35 fn parse_text_section_size_lazy(base: &[u8]) -> Result<u64, &'static str> {
36     let header = Elf::parse_header(base).map_err(|_| "parse elf header error")?;
37     // dummy Elf with only header
38     let mut obj = Elf::lazy_parse(header).map_err(|_| "cannot parse ELF file")?;
39 
40     use goblin::container::{Container, Ctx};
41     use goblin::elf::SectionHeader;
42     use goblin::strtab::Strtab;
43 
44     let ctx = Ctx {
45         le: scroll::Endian::Little,
46         container: Container::Big,
47     };
48 
49     obj.section_headers =
50         SectionHeader::parse(base, header.e_shoff as usize, header.e_shnum as usize, ctx)
51             .map_err(|_| "parse section headers error")?;
52 
53     let strtab_idx = header.e_shstrndx as usize;
54     let strtab_shdr = &obj.section_headers[strtab_idx];
55     let strtab = Strtab::parse(
56         base,
57         strtab_shdr.sh_offset as usize,
58         strtab_shdr.sh_size as usize,
59         0x0,
60     )
61     .map_err(|_| "parse string table error")?;
62     for (_, section) in obj.section_headers.iter().enumerate() {
63         let section_name = strtab.get_at(section.sh_name).unwrap();
64         if section_name == ".text" {
65             return Ok(section.sh_size);
66         }
67     }
68 
69     Err("Didn't find text section")
70 }
71 
72 #[test]
test_parse_gnu_hash_section_64bit()73 fn test_parse_gnu_hash_section_64bit() {
74     static ALIGNED_DATA: &AlignedData<[u8]> =
75         &AlignedData(*include_bytes!("bins/elf/gnu_hash/hello.so"));
76 
77     assert_eq!(
78         parse_gnu_hash_section(&ALIGNED_DATA.0, "helloWorld"),
79         Ok(Sym {
80             st_name: 97,
81             st_info: 0x12,
82             st_other: 0,
83             st_shndx: 12,
84             st_value: 0x65a,
85             st_size: 33,
86         })
87     );
88     assert_eq!(
89         parse_gnu_hash_section(&ALIGNED_DATA.0, "_edata"),
90         Ok(Sym {
91             st_name: 130,
92             st_info: 0x10,
93             st_other: 0,
94             st_shndx: 22,
95             st_value: 0x0020_1030,
96             st_size: 0,
97         })
98     );
99     assert_eq!(
100         parse_gnu_hash_section(&ALIGNED_DATA.0, "__gmon_start__"),
101         Err("cannot find symbol"),
102     );
103 }
104 
105 #[test]
test_parse_gnu_hash_section_32bit()106 fn test_parse_gnu_hash_section_32bit() {
107     static ALIGNED_DATA: &AlignedData<[u8]> =
108         &AlignedData(*include_bytes!("bins/elf/gnu_hash/hello32.so"));
109 
110     assert_eq!(
111         parse_gnu_hash_section(&ALIGNED_DATA.0, "helloWorld"),
112         Ok(Sym {
113             st_name: 97,
114             st_info: 0x12,
115             st_other: 0,
116             st_shndx: 12,
117             st_value: 0x4ed,
118             st_size: 49,
119         })
120     );
121     assert_eq!(
122         parse_gnu_hash_section(&ALIGNED_DATA.0, "_edata"),
123         Ok(Sym {
124             st_name: 130,
125             st_info: 0x10,
126             st_other: 0,
127             st_shndx: 22,
128             st_value: 0x2018,
129             st_size: 0,
130         })
131     );
132     assert_eq!(
133         parse_gnu_hash_section(&ALIGNED_DATA.0, "__gmon_start__"),
134         Err("cannot find symbol"),
135     );
136 }
137 
138 #[test]
test_parse_text_section_size_lazy()139 fn test_parse_text_section_size_lazy() {
140     static ALIGNED_DATA: &AlignedData<[u8]> =
141         &AlignedData(*include_bytes!("bins/elf/gnu_hash/hello.so"));
142     assert_eq!(parse_text_section_size_lazy(&ALIGNED_DATA.0), Ok(0x126));
143 }
144 
145 #[test]
test_oom()146 fn test_oom() {
147     use goblin::container::{Container, Ctx};
148     use scroll::Pwrite;
149 
150     fn test_oom(data: &mut [u8]) {
151         let mut modified_data = data.to_vec();
152         let mut elf = Elf::parse(&data).unwrap();
153         let endianness = elf.header.endianness().unwrap();
154         let ctx = Ctx::new(
155             if elf.is_64 {
156                 Container::Big
157             } else {
158                 Container::Little
159             },
160             endianness,
161         );
162         let original_e_phnum = elf.header.e_phnum;
163         let original_e_shnum = elf.header.e_shnum;
164 
165         // Way too many program headers
166         elf.header.e_phnum = 1000;
167         modified_data
168             .pwrite_with(elf.header, 0, endianness)
169             .unwrap();
170         assert!(Elf::parse(&modified_data).is_err());
171 
172         // Possible overflow of program headers
173         elf.header.e_phnum = std::u16::MAX;
174         modified_data
175             .pwrite_with(elf.header, 0, endianness)
176             .unwrap();
177         assert!(Elf::parse(&modified_data).is_err());
178 
179         // Back to original
180         elf.header.e_phnum = original_e_phnum;
181         modified_data
182             .pwrite_with(elf.header, 0, endianness)
183             .unwrap();
184         assert!(Elf::parse(&modified_data).is_ok());
185 
186         // Way too many section headers
187         elf.header.e_shnum = 1000;
188         modified_data
189             .pwrite_with(elf.header, 0, endianness)
190             .unwrap();
191         assert!(Elf::parse(&modified_data).is_err());
192 
193         // Fallback to large empty section header's sh_size
194         elf.header.e_shnum = 0;
195         elf.section_headers[0].sh_size = std::u64::MAX;
196         modified_data
197             .pwrite_with(elf.header, 0, endianness)
198             .unwrap();
199         modified_data
200             .pwrite_with(
201                 elf.section_headers[0].clone(),
202                 elf.header.e_shoff as usize,
203                 ctx,
204             )
205             .unwrap();
206         assert!(Elf::parse(&modified_data).is_err());
207 
208         // Possible overflow of section headers
209         elf.header.e_shnum = std::u16::MAX;
210         modified_data
211             .pwrite_with(elf.header, 0, endianness)
212             .unwrap();
213         assert!(Elf::parse(&modified_data).is_err());
214 
215         // Back to original
216         elf.header.e_shnum = original_e_shnum;
217         modified_data
218             .pwrite_with(elf.header, 0, endianness)
219             .unwrap();
220         assert!(Elf::parse(&modified_data).is_ok());
221     }
222 
223     let aligned_data: &mut AlignedData<[u8]> =
224         &mut AlignedData(*include_bytes!("bins/elf/gnu_hash/hello32.so"));
225     test_oom(&mut aligned_data.0);
226 
227     let aligned_data: &mut AlignedData<[u8]> =
228         &mut AlignedData(*include_bytes!("bins/elf/gnu_hash/hello.so"));
229     test_oom(&mut aligned_data.0);
230 }
231