| ... | @@ -9,9 +9,10 @@ const elf = std.elf; | ... | @@ -9,9 +9,10 @@ const elf = std.elf; |
| 9 | const windows = std.os.windows; | 9 | const windows = std.os.windows; |
| 10 | const system = std.os.system; | 10 | const system = std.os.system; |
| 11 | const maxInt = std.math.maxInt; | 11 | const maxInt = std.math.maxInt; |
| | 12 | const max = std.math.max; |
| 12 | | 13 | |
| 13 | pub const DynLib = switch (builtin.os) { | 14 | pub const DynLib = switch (builtin.os) { |
| 14 | .linux => if (builtin.link_libc) DlDynlib else LinuxDynLib, | 15 | .linux => if (builtin.link_libc) DlDynlib else ElfDynLib, |
| 15 | .windows => WindowsDynLib, | 16 | .windows => WindowsDynLib, |
| 16 | .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib, | 17 | .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib, |
| 17 | else => void, | 18 | else => void, |
| ... | @@ -100,102 +101,127 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { | ... | @@ -100,102 +101,127 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { |
| 100 | return LinkMap.Iterator{ .current = link_map_ptr }; | 101 | return LinkMap.Iterator{ .current = link_map_ptr }; |
| 101 | } | 102 | } |
| 102 | | 103 | |
| 103 | pub const LinuxDynLib = struct { | 104 | pub const ElfDynLib = struct { |
| 104 | pub const Error = ElfLib.Error; | 105 | strings: [*:0]u8, |
| 105 | | 106 | syms: [*]elf.Sym, |
| 106 | elf_lib: ElfLib, | 107 | hashtab: [*]os.Elf_Symndx, |
| 107 | fd: i32, | 108 | versym: ?[*]u16, |
| | 109 | verdef: ?*elf.Verdef, |
| 108 | memory: []align(mem.page_size) u8, | 110 | memory: []align(mem.page_size) u8, |
| 109 | | 111 | |
| 110 | /// Trusts the file | | |
| 111 | pub fn open(path: []const u8) !LinuxDynLib { | | |
| 112 | const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC); | | |
| 113 | errdefer os.close(fd); | | |
| 114 | | | |
| 115 | // TODO remove this @intCast | | |
| 116 | const size = @intCast(usize, (try os.fstat(fd)).size); | | |
| 117 | | | |
| 118 | const bytes = try os.mmap( | | |
| 119 | null, | | |
| 120 | mem.alignForward(size, mem.page_size), | | |
| 121 | os.PROT_READ | os.PROT_EXEC, | | |
| 122 | os.MAP_PRIVATE, | | |
| 123 | fd, | | |
| 124 | 0, | | |
| 125 | ); | | |
| 126 | errdefer os.munmap(bytes); | | |
| 127 | | | |
| 128 | return LinuxDynLib{ | | |
| 129 | .elf_lib = try ElfLib.init(bytes), | | |
| 130 | .fd = fd, | | |
| 131 | .memory = bytes, | | |
| 132 | }; | | |
| 133 | } | | |
| 134 | | | |
| 135 | pub fn openC(path_c: [*:0]const u8) !LinuxDynLib { | | |
| 136 | return open(mem.toSlice(u8, path_c)); | | |
| 137 | } | | |
| 138 | | | |
| 139 | pub fn close(self: *LinuxDynLib) void { | | |
| 140 | os.munmap(self.memory); | | |
| 141 | os.close(self.fd); | | |
| 142 | self.* = undefined; | | |
| 143 | } | | |
| 144 | | | |
| 145 | pub fn lookup(self: *LinuxDynLib, comptime T: type, name: [:0]const u8) ?T { | | |
| 146 | if (self.elf_lib.lookup("", name)) |symbol| { | | |
| 147 | return @intToPtr(T, symbol); | | |
| 148 | } else { | | |
| 149 | return null; | | |
| 150 | } | | |
| 151 | } | | |
| 152 | }; | | |
| 153 | | | |
| 154 | pub const ElfLib = struct { | | |
| 155 | pub const Error = error{ | 112 | pub const Error = error{ |
| 156 | NotElfFile, | 113 | NotElfFile, |
| 157 | NotDynamicLibrary, | 114 | NotDynamicLibrary, |
| 158 | MissingDynamicLinkingInformation, | 115 | MissingDynamicLinkingInformation, |
| 159 | BaseNotFound, | | |
| 160 | ElfStringSectionNotFound, | 116 | ElfStringSectionNotFound, |
| 161 | ElfSymSectionNotFound, | 117 | ElfSymSectionNotFound, |
| 162 | ElfHashTableNotFound, | 118 | ElfHashTableNotFound, |
| 163 | }; | 119 | }; |
| 164 | | 120 | |
| 165 | strings: [*:0]u8, | 121 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 166 | syms: [*]elf.Sym, | 122 | pub fn open(path: []const u8) !ElfDynLib { |
| 167 | hashtab: [*]os.Elf_Symndx, | 123 | const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC); |
| 168 | versym: ?[*]u16, | 124 | defer os.close(fd); |
| 169 | verdef: ?*elf.Verdef, | 125 | |
| 170 | base: usize, | 126 | const stat = try os.fstat(fd); |
| | 127 | const size = try std.math.cast(usize, stat.size); |
| 171 | | 128 | |
| 172 | // Trusts the memory | 129 | // This one is to read the ELF info. We do more mmapping later |
| 173 | pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib { | 130 | // corresponding to the actual LOAD sections. |
| 174 | const eh = @ptrCast(*elf.Ehdr, bytes.ptr); | 131 | const file_bytes = try os.mmap( |
| | 132 | null, |
| | 133 | mem.alignForward(size, mem.page_size), |
| | 134 | os.PROT_READ, |
| | 135 | os.MAP_PRIVATE, |
| | 136 | fd, |
| | 137 | 0, |
| | 138 | ); |
| | 139 | defer os.munmap(file_bytes); |
| | 140 | |
| | 141 | const eh = @ptrCast(*elf.Ehdr, file_bytes.ptr); |
| 175 | if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; | 142 | if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; |
| 176 | if (eh.e_type != elf.ET.DYN) return error.NotDynamicLibrary; | 143 | if (eh.e_type != elf.ET.DYN) return error.NotDynamicLibrary; |
| 177 | | 144 | |
| 178 | const elf_addr = @ptrToInt(bytes.ptr); | 145 | const elf_addr = @ptrToInt(file_bytes.ptr); |
| 179 | var ph_addr: usize = elf_addr + eh.e_phoff; | | |
| 180 | | 146 | |
| 181 | var base: usize = maxInt(usize); | 147 | // Iterate over the program header entries to find out the |
| | 148 | // dynamic vector as well as the total size of the virtual memory. |
| 182 | var maybe_dynv: ?[*]usize = null; | 149 | var maybe_dynv: ?[*]usize = null; |
| | 150 | var virt_addr_end: usize = 0; |
| 183 | { | 151 | { |
| 184 | var i: usize = 0; | 152 | var i: usize = 0; |
| | 153 | var ph_addr: usize = elf_addr + eh.e_phoff; |
| 185 | while (i < eh.e_phnum) : ({ | 154 | while (i < eh.e_phnum) : ({ |
| 186 | i += 1; | 155 | i += 1; |
| 187 | ph_addr += eh.e_phentsize; | 156 | ph_addr += eh.e_phentsize; |
| 188 | }) { | 157 | }) { |
| 189 | const ph = @intToPtr(*elf.Phdr, ph_addr); | 158 | const ph = @intToPtr(*elf.Phdr, ph_addr); |
| 190 | switch (ph.p_type) { | 159 | switch (ph.p_type) { |
| 191 | elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr, | 160 | elf.PT_LOAD => virt_addr_end = max(virt_addr_end, ph.p_vaddr + ph.p_memsz), |
| 192 | elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset), | 161 | elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset), |
| 193 | else => {}, | 162 | else => {}, |
| 194 | } | 163 | } |
| 195 | } | 164 | } |
| 196 | } | 165 | } |
| 197 | const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; | 166 | const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; |
| 198 | if (base == maxInt(usize)) return error.BaseNotFound; | 167 | |
| | 168 | // Reserve the entire range (with no permissions) so that we can do MAP_FIXED below. |
| | 169 | const all_loaded_mem = try os.mmap( |
| | 170 | null, |
| | 171 | virt_addr_end, |
| | 172 | os.PROT_NONE, |
| | 173 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| | 174 | -1, |
| | 175 | 0, |
| | 176 | ); |
| | 177 | errdefer os.munmap(all_loaded_mem); |
| | 178 | |
| | 179 | const base = @ptrToInt(all_loaded_mem.ptr); |
| | 180 | |
| | 181 | // Now iterate again and actually load all the program sections. |
| | 182 | { |
| | 183 | var i: usize = 0; |
| | 184 | var ph_addr: usize = elf_addr + eh.e_phoff; |
| | 185 | while (i < eh.e_phnum) : ({ |
| | 186 | i += 1; |
| | 187 | ph_addr += eh.e_phentsize; |
| | 188 | }) { |
| | 189 | const ph = @intToPtr(*elf.Phdr, ph_addr); |
| | 190 | switch (ph.p_type) { |
| | 191 | elf.PT_LOAD => { |
| | 192 | // The VirtAddr may not be page-aligned; in such case there will be |
| | 193 | // extra nonsense mapped before/after the VirtAddr,MemSiz |
| | 194 | const aligned_addr = (base + ph.p_vaddr) & ~(@as(usize, mem.page_size) - 1); |
| | 195 | const extra_bytes = (base + ph.p_vaddr) - aligned_addr; |
| | 196 | const extended_memsz = mem.alignForward(ph.p_memsz + extra_bytes, mem.page_size); |
| | 197 | const ptr = @intToPtr([*]align(mem.page_size) u8, aligned_addr); |
| | 198 | const prot = elfToMmapProt(ph.p_flags); |
| | 199 | if ((ph.p_flags & elf.PF_W) == 0) { |
| | 200 | // If it does not need write access, it can be mapped from the fd. |
| | 201 | _ = try os.mmap( |
| | 202 | ptr, |
| | 203 | extended_memsz, |
| | 204 | prot, |
| | 205 | os.MAP_PRIVATE | os.MAP_FIXED, |
| | 206 | fd, |
| | 207 | ph.p_offset - extra_bytes, |
| | 208 | ); |
| | 209 | } else { |
| | 210 | const sect_mem = try os.mmap( |
| | 211 | ptr, |
| | 212 | extended_memsz, |
| | 213 | prot, |
| | 214 | os.MAP_PRIVATE | os.MAP_FIXED | os.MAP_ANONYMOUS, |
| | 215 | -1, |
| | 216 | 0, |
| | 217 | ); |
| | 218 | mem.copy(u8, sect_mem, file_bytes[0..ph.p_filesz]); |
| | 219 | } |
| | 220 | }, |
| | 221 | else => {}, |
| | 222 | } |
| | 223 | } |
| | 224 | } |
| 199 | | 225 | |
| 200 | var maybe_strings: ?[*:0]u8 = null; | 226 | var maybe_strings: ?[*:0]u8 = null; |
| 201 | var maybe_syms: ?[*]elf.Sym = null; | 227 | var maybe_syms: ?[*]elf.Sym = null; |
| ... | @@ -218,8 +244,8 @@ pub const ElfLib = struct { | ... | @@ -218,8 +244,8 @@ pub const ElfLib = struct { |
| 218 | } | 244 | } |
| 219 | } | 245 | } |
| 220 | | 246 | |
| 221 | return ElfLib{ | 247 | return ElfDynLib{ |
| 222 | .base = base, | 248 | .memory = all_loaded_mem, |
| 223 | .strings = maybe_strings orelse return error.ElfStringSectionNotFound, | 249 | .strings = maybe_strings orelse return error.ElfStringSectionNotFound, |
| 224 | .syms = maybe_syms orelse return error.ElfSymSectionNotFound, | 250 | .syms = maybe_syms orelse return error.ElfSymSectionNotFound, |
| 225 | .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, | 251 | .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, |
| ... | @@ -228,8 +254,27 @@ pub const ElfLib = struct { | ... | @@ -228,8 +254,27 @@ pub const ElfLib = struct { |
| 228 | }; | 254 | }; |
| 229 | } | 255 | } |
| 230 | | 256 | |
| | 257 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| | 258 | pub fn openC(path_c: [*:0]const u8) !ElfDynLib { |
| | 259 | return open(mem.toSlice(u8, path_c)); |
| | 260 | } |
| | 261 | |
| | 262 | /// Trusts the file |
| | 263 | pub fn close(self: *ElfDynLib) void { |
| | 264 | os.munmap(self.memory); |
| | 265 | self.* = undefined; |
| | 266 | } |
| | 267 | |
| | 268 | pub fn lookup(self: *ElfDynLib, comptime T: type, name: [:0]const u8) ?T { |
| | 269 | if (self.lookupAddress("", name)) |symbol| { |
| | 270 | return @intToPtr(T, symbol); |
| | 271 | } else { |
| | 272 | return null; |
| | 273 | } |
| | 274 | } |
| | 275 | |
| 231 | /// Returns the address of the symbol | 276 | /// Returns the address of the symbol |
| 232 | pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize { | 277 | pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize { |
| 233 | const maybe_versym = if (self.verdef == null) null else self.versym; | 278 | const maybe_versym = if (self.verdef == null) null else self.versym; |
| 234 | | 279 | |
| 235 | const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); | 280 | const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); |
| ... | @@ -245,11 +290,19 @@ pub const ElfLib = struct { | ... | @@ -245,11 +290,19 @@ pub const ElfLib = struct { |
| 245 | if (!checkver(self.verdef.?, versym[i], vername, self.strings)) | 290 | if (!checkver(self.verdef.?, versym[i], vername, self.strings)) |
| 246 | continue; | 291 | continue; |
| 247 | } | 292 | } |
| 248 | return self.base + self.syms[i].st_value; | 293 | return @ptrToInt(self.memory.ptr) + self.syms[i].st_value; |
| 249 | } | 294 | } |
| 250 | | 295 | |
| 251 | return null; | 296 | return null; |
| 252 | } | 297 | } |
| | 298 | |
| | 299 | fn elfToMmapProt(elf_prot: u64) u32 { |
| | 300 | var result: u32 = os.PROT_NONE; |
| | 301 | if ((elf_prot & elf.PF_R) != 0) result |= os.PROT_READ; |
| | 302 | if ((elf_prot & elf.PF_W) != 0) result |= os.PROT_WRITE; |
| | 303 | if ((elf_prot & elf.PF_X) != 0) result |= os.PROT_EXEC; |
| | 304 | return result; |
| | 305 | } |
| 253 | }; | 306 | }; |
| 254 | | 307 | |
| 255 | fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*:0]u8) bool { | 308 | fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*:0]u8) bool { |