• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "fd_utils.h"
18 
19 #include <algorithm>
20 
21 #include <fcntl.h>
22 #include <grp.h>
23 #include <stdlib.h>
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <sys/un.h>
27 #include <unistd.h>
28 
29 #include <android-base/file.h>
30 #include <android-base/logging.h>
31 #include <android-base/stringprintf.h>
32 #include <android-base/strings.h>
33 
34 // Static whitelist of open paths that the zygote is allowed to keep open.
35 static const char* kPathWhitelist[] = {
36   "/apex/com.android.conscrypt/javalib/conscrypt.jar",
37   "/apex/com.android.media/javalib/updatable-media.jar",
38   "/dev/null",
39   "/dev/socket/zygote",
40   "/dev/socket/zygote_secondary",
41   "/dev/socket/usap_pool_primary",
42   "/dev/socket/usap_pool_secondary",
43   "/dev/socket/webview_zygote",
44   "/dev/socket/heapprofd",
45   "/sys/kernel/debug/tracing/trace_marker",
46   "/system/framework/framework-res.apk",
47   "/dev/urandom",
48   "/dev/ion",
49   "/dev/dri/renderD129", // Fixes b/31172436
50 };
51 
52 static const char kFdPath[] = "/proc/self/fd";
53 
54 // static
Get()55 FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
56   if (instance_ == nullptr) {
57     instance_ = new FileDescriptorWhitelist();
58   }
59   return instance_;
60 }
61 
IsAllowed(const std::string & path) const62 bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
63   // Check the static whitelist path.
64   for (const auto& whitelist_path : kPathWhitelist) {
65     if (path == whitelist_path)
66       return true;
67   }
68 
69   // Check any paths added to the dynamic whitelist.
70   for (const auto& whitelist_path : whitelist_) {
71     if (path == whitelist_path)
72       return true;
73   }
74 
75   // Framework jars are allowed.
76   static const char* kFrameworksPrefix = "/system/framework/";
77   static const char* kJarSuffix = ".jar";
78   if (android::base::StartsWith(path, kFrameworksPrefix)
79       && android::base::EndsWith(path, kJarSuffix)) {
80     return true;
81   }
82 
83   // Jars from the runtime apex are allowed.
84   static const char* kRuntimeApexPrefix = "/apex/com.android.runtime/javalib/";
85   if (android::base::StartsWith(path, kRuntimeApexPrefix)
86       && android::base::EndsWith(path, kJarSuffix)) {
87     return true;
88   }
89 
90   // Whitelist files needed for Runtime Resource Overlay, like these:
91   // /system/vendor/overlay/framework-res.apk
92   // /system/vendor/overlay-subdir/pg/framework-res.apk
93   // /vendor/overlay/framework-res.apk
94   // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
95   // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
96   // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
97   // See AssetManager.cpp for more details on overlay-subdir.
98   static const char* kOverlayDir = "/system/vendor/overlay/";
99   static const char* kVendorOverlayDir = "/vendor/overlay";
100   static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/";
101   static const char* kSystemProductOverlayDir = "/system/product/overlay/";
102   static const char* kProductOverlayDir = "/product/overlay";
103   static const char* kSystemProductServicesOverlayDir = "/system/product_services/overlay/";
104   static const char* kProductServicesOverlayDir = "/product_services/overlay";
105   static const char* kSystemOdmOverlayDir = "/system/odm/overlay";
106   static const char* kOdmOverlayDir = "/odm/overlay";
107   static const char* kSystemOemOverlayDir = "/system/oem/overlay";
108   static const char* kOemOverlayDir = "/oem/overlay";
109   static const char* kApkSuffix = ".apk";
110 
111   if ((android::base::StartsWith(path, kOverlayDir)
112        || android::base::StartsWith(path, kVendorOverlaySubdir)
113        || android::base::StartsWith(path, kVendorOverlayDir)
114        || android::base::StartsWith(path, kSystemProductOverlayDir)
115        || android::base::StartsWith(path, kProductOverlayDir)
116        || android::base::StartsWith(path, kSystemProductServicesOverlayDir)
117        || android::base::StartsWith(path, kProductServicesOverlayDir)
118        || android::base::StartsWith(path, kSystemOdmOverlayDir)
119        || android::base::StartsWith(path, kOdmOverlayDir)
120        || android::base::StartsWith(path, kSystemOemOverlayDir)
121        || android::base::StartsWith(path, kOemOverlayDir))
122       && android::base::EndsWith(path, kApkSuffix)
123       && path.find("/../") == std::string::npos) {
124     return true;
125   }
126 
127   static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
128   static const char* kOverlayIdmapSuffix = ".apk@idmap";
129   if (android::base::StartsWith(path, kOverlayIdmapPrefix)
130       && android::base::EndsWith(path, kOverlayIdmapSuffix)
131       && path.find("/../") == std::string::npos) {
132     return true;
133   }
134 
135   // All regular files that are placed under this path are whitelisted automatically.
136   static const char* kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
137   if (android::base::StartsWith(path, kZygoteWhitelistPath)
138       && path.find("/../") == std::string::npos) {
139     return true;
140   }
141 
142   return false;
143 }
144 
FileDescriptorWhitelist()145 FileDescriptorWhitelist::FileDescriptorWhitelist()
146     : whitelist_() {
147 }
148 
149 FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
150 
151 // Keeps track of all relevant information (flags, offset etc.) of an
152 // open zygote file descriptor.
153 class FileDescriptorInfo {
154  public:
155   // Create a FileDescriptorInfo for a given file descriptor.
156   static FileDescriptorInfo* CreateFromFd(int fd, fail_fn_t fail_fn);
157 
158   // Checks whether the file descriptor associated with this object
159   // refers to the same description.
160   bool RefersToSameFile() const;
161 
162   void ReopenOrDetach(fail_fn_t fail_fn) const;
163 
164   const int fd;
165   const struct stat stat;
166   const std::string file_path;
167   const int open_flags;
168   const int fd_flags;
169   const int fs_flags;
170   const off_t offset;
171   const bool is_sock;
172 
173  private:
174   explicit FileDescriptorInfo(int fd);
175 
176   FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
177                      int fd_flags, int fs_flags, off_t offset);
178 
179   // Returns the locally-bound name of the socket |fd|. Returns true
180   // iff. all of the following hold :
181   //
182   // - the socket's sa_family is AF_UNIX.
183   // - the length of the path is greater than zero (i.e, not an unnamed socket).
184   // - the first byte of the path isn't zero (i.e, not a socket with an abstract
185   //   address).
186   static bool GetSocketName(const int fd, std::string* result);
187 
188   void DetachSocket(fail_fn_t fail_fn) const;
189 
190   DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
191 };
192 
193 // static
CreateFromFd(int fd,fail_fn_t fail_fn)194 FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn) {
195   struct stat f_stat;
196   // This should never happen; the zygote should always have the right set
197   // of permissions required to stat all its open files.
198   if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
199     fail_fn(android::base::StringPrintf("Unable to stat %d", fd));
200   }
201 
202   const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
203 
204   if (S_ISSOCK(f_stat.st_mode)) {
205     std::string socket_name;
206     if (!GetSocketName(fd, &socket_name)) {
207       fail_fn("Unable to get socket name");
208     }
209 
210     if (!whitelist->IsAllowed(socket_name)) {
211       fail_fn(android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
212                                           socket_name.c_str(),
213                                           fd));
214     }
215 
216     return new FileDescriptorInfo(fd);
217   }
218 
219   // We only handle whitelisted regular files and character devices. Whitelisted
220   // character devices must provide a guarantee of sensible behaviour when
221   // reopened.
222   //
223   // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
224   // S_ISLINK : Not supported.
225   // S_ISBLK : Not supported.
226   // S_ISFIFO : Not supported. Note that the Zygote and USAPs use pipes to
227   // communicate with the child processes across forks but those should have been
228   // added to the redirection exemption list.
229   if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
230     std::string mode = "Unknown";
231 
232     if (S_ISDIR(f_stat.st_mode)) {
233       mode = "DIR";
234     } else if (S_ISLNK(f_stat.st_mode)) {
235       mode = "LINK";
236     } else if (S_ISBLK(f_stat.st_mode)) {
237       mode = "BLOCK";
238     } else if (S_ISFIFO(f_stat.st_mode)) {
239       mode = "FIFO";
240     }
241 
242     fail_fn(android::base::StringPrintf("Unsupported st_mode for FD %d:  %s", fd, mode.c_str()));
243   }
244 
245   std::string file_path;
246   const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
247   if (!android::base::Readlink(fd_path, &file_path)) {
248     fail_fn(android::base::StringPrintf("Could not read fd link %s: %s",
249                                         fd_path.c_str(),
250                                         strerror(errno)));
251   }
252 
253   if (!whitelist->IsAllowed(file_path)) {
254     fail_fn(std::string("Not whitelisted : ").append(file_path));
255   }
256 
257   // File descriptor flags : currently on FD_CLOEXEC. We can set these
258   // using F_SETFD - we're single threaded at this point of execution so
259   // there won't be any races.
260   const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
261   if (fd_flags == -1) {
262     fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
263                                         fd,
264                                         file_path.c_str(),
265                                         strerror(errno)));
266   }
267 
268   // File status flags :
269   // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
270   //   to the open() call.
271   //
272   // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
273   //   do about these, since the file has already been created. We shall ignore
274   //   them here.
275   //
276   // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
277   //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
278   //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
279   //   their presence and pass them in to open().
280   int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
281   if (fs_flags == -1) {
282     fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
283                                         fd,
284                                         file_path.c_str(),
285                                         strerror(errno)));
286   }
287 
288   // File offset : Ignore the offset for non seekable files.
289   const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
290 
291   // We pass the flags that open accepts to open, and use F_SETFL for
292   // the rest of them.
293   static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
294   int open_flags = fs_flags & (kOpenFlags);
295   fs_flags = fs_flags & (~(kOpenFlags));
296 
297   return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
298 }
299 
RefersToSameFile() const300 bool FileDescriptorInfo::RefersToSameFile() const {
301   struct stat f_stat;
302   if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
303     PLOG(ERROR) << "Unable to restat fd " << fd;
304     return false;
305   }
306 
307   return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
308 }
309 
ReopenOrDetach(fail_fn_t fail_fn) const310 void FileDescriptorInfo::ReopenOrDetach(fail_fn_t fail_fn) const {
311   if (is_sock) {
312     return DetachSocket(fail_fn);
313   }
314 
315   // NOTE: This might happen if the file was unlinked after being opened.
316   // It's a common pattern in the case of temporary files and the like but
317   // we should not allow such usage from the zygote.
318   const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
319 
320   if (new_fd == -1) {
321     fail_fn(android::base::StringPrintf("Failed open(%s, %i): %s",
322                                         file_path.c_str(),
323                                         open_flags,
324                                         strerror(errno)));
325   }
326 
327   if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
328     close(new_fd);
329     fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
330                                         new_fd,
331                                         fd_flags,
332                                         file_path.c_str(),
333                                         strerror(errno)));
334   }
335 
336   if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
337     close(new_fd);
338     fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
339                                         new_fd,
340                                         fs_flags,
341                                         file_path.c_str(),
342                                         strerror(errno)));
343   }
344 
345   if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
346     close(new_fd);
347     fail_fn(android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
348                                         new_fd,
349                                         file_path.c_str(),
350                                         strerror(errno)));
351   }
352 
353   int dup_flags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
354   if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dup_flags)) == -1) {
355     close(new_fd);
356     fail_fn(android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
357                                         fd,
358                                         new_fd,
359                                         dup_flags,
360                                         file_path.c_str(),
361                                         strerror(errno)));
362   }
363 
364   close(new_fd);
365 }
366 
FileDescriptorInfo(int fd)367 FileDescriptorInfo::FileDescriptorInfo(int fd) :
368   fd(fd),
369   stat(),
370   open_flags(0),
371   fd_flags(0),
372   fs_flags(0),
373   offset(0),
374   is_sock(true) {
375 }
376 
FileDescriptorInfo(struct stat stat,const std::string & file_path,int fd,int open_flags,int fd_flags,int fs_flags,off_t offset)377 FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file_path,
378                                        int fd, int open_flags, int fd_flags, int fs_flags,
379                                        off_t offset) :
380   fd(fd),
381   stat(stat),
382   file_path(file_path),
383   open_flags(open_flags),
384   fd_flags(fd_flags),
385   fs_flags(fs_flags),
386   offset(offset),
387   is_sock(false) {
388 }
389 
GetSocketName(const int fd,std::string * result)390 bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
391   sockaddr_storage ss;
392   sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
393   socklen_t addr_len = sizeof(ss);
394 
395   if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
396     PLOG(ERROR) << "Failed getsockname(" << fd << ")";
397     return false;
398   }
399 
400   if (addr->sa_family != AF_UNIX) {
401     LOG(ERROR) << "Unsupported socket (fd=" << fd << ") with family " << addr->sa_family;
402     return false;
403   }
404 
405   const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
406 
407   size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
408   // This is an unnamed local socket, we do not accept it.
409   if (path_len == 0) {
410     LOG(ERROR) << "Unsupported AF_UNIX socket (fd=" << fd << ") with empty path.";
411     return false;
412   }
413 
414   // This is a local socket with an abstract address. Remove the leading NUL byte and
415   // add a human-readable "ABSTRACT/" prefix.
416   if (unix_addr->sun_path[0] == '\0') {
417     *result = "ABSTRACT/";
418     result->append(&unix_addr->sun_path[1], path_len - 1);
419     return true;
420   }
421 
422   // If we're here, sun_path must refer to a null terminated filesystem
423   // pathname (man 7 unix). Remove the terminator before assigning it to an
424   // std::string.
425   if (unix_addr->sun_path[path_len - 1] ==  '\0') {
426     --path_len;
427   }
428 
429   result->assign(unix_addr->sun_path, path_len);
430   return true;
431 }
432 
DetachSocket(fail_fn_t fail_fn) const433 void FileDescriptorInfo::DetachSocket(fail_fn_t fail_fn) const {
434   const int dev_null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
435   if (dev_null_fd < 0) {
436     fail_fn(std::string("Failed to open /dev/null: ").append(strerror(errno)));
437   }
438 
439   if (dup3(dev_null_fd, fd, O_CLOEXEC) == -1) {
440     fail_fn(android::base::StringPrintf("Failed dup3 on socket descriptor %d: %s",
441                                         fd,
442                                         strerror(errno)));
443   }
444 
445   if (close(dev_null_fd) == -1) {
446     fail_fn(android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno)));
447   }
448 }
449 
450 // static
Create(const std::vector<int> & fds_to_ignore,fail_fn_t fail_fn)451 FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore,
452                                                  fail_fn_t fail_fn) {
453   DIR* proc_fd_dir = opendir(kFdPath);
454   if (proc_fd_dir == nullptr) {
455     fail_fn(std::string("Unable to open directory ").append(kFdPath));
456   }
457 
458   int dir_fd = dirfd(proc_fd_dir);
459   dirent* dir_entry;
460 
461   std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
462   while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
463     const int fd = ParseFd(dir_entry, dir_fd);
464     if (fd == -1) {
465       continue;
466     }
467 
468     if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
469       continue;
470     }
471 
472     open_fd_map[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
473   }
474 
475   if (closedir(proc_fd_dir) == -1) {
476     fail_fn("Unable to close directory");
477   }
478 
479   return new FileDescriptorTable(open_fd_map);
480 }
481 
Restat(const std::vector<int> & fds_to_ignore,fail_fn_t fail_fn)482 void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn) {
483   std::set<int> open_fds;
484 
485   // First get the list of open descriptors.
486   DIR* proc_fd_dir = opendir(kFdPath);
487   if (proc_fd_dir == nullptr) {
488     fail_fn(android::base::StringPrintf("Unable to open directory %s: %s",
489                                         kFdPath,
490                                         strerror(errno)));
491   }
492 
493   int dir_fd = dirfd(proc_fd_dir);
494   dirent* dir_entry;
495   while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
496     const int fd = ParseFd(dir_entry, dir_fd);
497     if (fd == -1) {
498       continue;
499     }
500 
501     if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
502       continue;
503     }
504 
505     open_fds.insert(fd);
506   }
507 
508   if (closedir(proc_fd_dir) == -1) {
509     fail_fn(android::base::StringPrintf("Unable to close directory: %s", strerror(errno)));
510   }
511 
512   RestatInternal(open_fds, fail_fn);
513 }
514 
515 // Reopens all file descriptors that are contained in the table.
ReopenOrDetach(fail_fn_t fail_fn)516 void FileDescriptorTable::ReopenOrDetach(fail_fn_t fail_fn) {
517   std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
518   for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
519     const FileDescriptorInfo* info = it->second;
520     if (info == nullptr) {
521       return;
522     } else {
523       info->ReopenOrDetach(fail_fn);
524     }
525   }
526 }
527 
FileDescriptorTable(const std::unordered_map<int,FileDescriptorInfo * > & map)528 FileDescriptorTable::FileDescriptorTable(
529     const std::unordered_map<int, FileDescriptorInfo*>& map)
530     : open_fd_map_(map) {
531 }
532 
RestatInternal(std::set<int> & open_fds,fail_fn_t fail_fn)533 void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) {
534   // Iterate through the list of file descriptors we've already recorded
535   // and check whether :
536   //
537   // (a) they continue to be open.
538   // (b) they refer to the same file.
539   //
540   // We'll only store the last error message.
541   std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
542   while (it != open_fd_map_.end()) {
543     std::set<int>::const_iterator element = open_fds.find(it->first);
544     if (element == open_fds.end()) {
545       // The entry from the file descriptor table is no longer in the list
546       // of open files. We warn about this condition and remove it from
547       // the list of FDs under consideration.
548       //
549       // TODO(narayan): This will be an error in a future android release.
550       // error = true;
551       // ALOGW("Zygote closed file descriptor %d.", it->first);
552       it = open_fd_map_.erase(it);
553     } else {
554       // The entry from the file descriptor table is still open. Restat
555       // it and check whether it refers to the same file.
556       if (!it->second->RefersToSameFile()) {
557         // The file descriptor refers to a different description. We must
558         // update our entry in the table.
559         delete it->second;
560         it->second = FileDescriptorInfo::CreateFromFd(*element, fail_fn);
561       } else {
562         // It's the same file. Nothing to do here. Move on to the next open
563         // FD.
564       }
565 
566       ++it;
567 
568       // Finally, remove the FD from the set of open_fds. We do this last because
569       // |element| will not remain valid after a call to erase.
570       open_fds.erase(element);
571     }
572   }
573 
574   if (open_fds.size() > 0) {
575     // The zygote has opened new file descriptors since our last inspection.
576     // We warn about this condition and add them to our table.
577     //
578     // TODO(narayan): This will be an error in a future android release.
579     // error = true;
580     // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
581 
582     // TODO(narayan): This code will be removed in a future android release.
583     std::set<int>::const_iterator it;
584     for (it = open_fds.begin(); it != open_fds.end(); ++it) {
585       const int fd = (*it);
586       open_fd_map_[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
587     }
588   }
589 }
590 
591 // static
ParseFd(dirent * dir_entry,int dir_fd)592 int FileDescriptorTable::ParseFd(dirent* dir_entry, int dir_fd) {
593   char* end;
594   const int fd = strtol(dir_entry->d_name, &end, 10);
595   if ((*end) != '\0') {
596     return -1;
597   }
598 
599   // Don't bother with the standard input/output/error, they're handled
600   // specially post-fork anyway.
601   if (fd <= STDERR_FILENO || fd == dir_fd) {
602     return -1;
603   }
604 
605   return fd;
606 }
607