| ... | @@ -147,9 +147,72 @@ pub const ElfDynLib = struct { | ... | @@ -147,9 +147,72 @@ pub const ElfDynLib = struct { |
| 147 | | 147 | |
| 148 | pub const Error = ElfDynLibError; | 148 | pub const Error = ElfDynLibError; |
| 149 | | 149 | |
| | 150 | fn openPath(path: []const u8) !std.fs.Dir { |
| | 151 | if (path.len == 0) return error.NotDir; |
| | 152 | var parts = std.mem.tokenizeScalar(u8, path, '/'); |
| | 153 | var parent = if (path[0] == '/') try std.fs.cwd().openDir("/", .{}) else std.fs.cwd(); |
| | 154 | while (parts.next()) |part| { |
| | 155 | const child = try parent.openDir(part, .{}); |
| | 156 | parent.close(); |
| | 157 | parent = child; |
| | 158 | } |
| | 159 | return parent; |
| | 160 | } |
| | 161 | |
| | 162 | fn resolveFromSearchPath(search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { |
| | 163 | var paths = std.mem.tokenizeScalar(u8, search_path, delim); |
| | 164 | while (paths.next()) |p| { |
| | 165 | var dir = openPath(p) catch continue; |
| | 166 | defer dir.close(); |
| | 167 | const fd = posix.openat(dir.fd, file_name, .{ |
| | 168 | .ACCMODE = .RDONLY, |
| | 169 | .CLOEXEC = true, |
| | 170 | }, 0) catch continue; |
| | 171 | return fd; |
| | 172 | } |
| | 173 | return null; |
| | 174 | } |
| | 175 | |
| | 176 | fn resolveFromParent(dir_path: []const u8, file_name: []const u8) ?posix.fd_t { |
| | 177 | var dir = std.fs.cwd().openDir(dir_path, .{}) catch return null; |
| | 178 | defer dir.close(); |
| | 179 | return posix.openat(dir.fd, file_name, .{ |
| | 180 | .ACCMODE = .RDONLY, |
| | 181 | .CLOEXEC = true, |
| | 182 | }, 0) catch null; |
| | 183 | } |
| | 184 | |
| | 185 | // This implements enough to be able to load system libraries in general |
| | 186 | // Places where it differs from dlopen: |
| | 187 | // - DT_RPATH of the calling binary is not used as a search path |
| | 188 | // - DT_RUNPATH of the calling binary is not used as a search path |
| | 189 | // - /etc/ld.so.cache is not read |
| | 190 | fn resolveFromName(path_or_name: []const u8) !posix.fd_t { |
| | 191 | // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname |
| | 192 | if (std.mem.indexOfScalarPos(u8, path_or_name, 0, '/')) |_| { |
| | 193 | return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); |
| | 194 | } |
| | 195 | |
| | 196 | // Only read LD_LIBRARY_PATH if the binary is not setuid/setgid |
| | 197 | if (std.os.linux.geteuid() == std.os.linux.getuid() and |
| | 198 | std.os.linux.getegid() == std.os.linux.getgid()) |
| | 199 | { |
| | 200 | if (posix.getenvZ("LD_LIBRARY_PATH")) |ld_library_path| { |
| | 201 | if (resolveFromSearchPath(ld_library_path, path_or_name, ':')) |fd| { |
| | 202 | return fd; |
| | 203 | } |
| | 204 | } |
| | 205 | } |
| | 206 | |
| | 207 | // Lastly the directories /lib and /usr/lib are searched (in this exact order) |
| | 208 | if (resolveFromParent("/lib", path_or_name)) |fd| return fd; |
| | 209 | if (resolveFromParent("/usr/lib", path_or_name)) |fd| return fd; |
| | 210 | return error.FileNotFound; |
| | 211 | } |
| | 212 | |
| 150 | /// Trusts the file. Malicious file will be able to execute arbitrary code. | 213 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 151 | pub fn open(path: []const u8) Error!ElfDynLib { | 214 | pub fn open(path: []const u8) Error!ElfDynLib { |
| 152 | const fd = try posix.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | 215 | const fd = try resolveFromName(path); |
| 153 | defer posix.close(fd); | 216 | defer posix.close(fd); |
| 154 | | 217 | |
| 155 | const stat = try posix.fstat(fd); | 218 | const stat = try posix.fstat(fd); |