+// Run do_search_file() in a child process.
+//
+// The reason for this is that we're not robust against malicious input, so we need
+// to drop privileges after opening the file. (Otherwise, we could fall prey to an attack
+// where a user does locate -d badfile.db:/var/lib/plocate/plocate.db, badfile.db contains
+// a buffer overflow that takes over the process, and then uses the elevated privileges
+// to print out otherwise inaccessible paths.) We solve this by forking and treating the
+// child process as untrusted after it has dropped its privileges (which it does before
+// reading any data from the file); it returns a single 64-bit number over a pipe,
+// and that's it. The parent keeps its privileges, and can then fork out new children
+// without fear of being taken over. (The child keeps stdout for outputting results.)
+//
+// The count is returned over the pipe, because it's needed both for --limit and --count.
+uint64_t do_search_file_in_child(const vector<Needle> &needles, const std::string &filename)
+{
+ int pipefd[2];
+ if (pipe(pipefd) == -1) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ }
+
+ pid_t child_pid = fork();
+ switch (child_pid) {
+ case 0: {
+ // Child.
+ close(pipefd[0]);
+ in_forked_child = true;
+ uint64_t matched = do_search_file(needles, filename);
+ int ret;
+ do {
+ ret = write(pipefd[1], &matched, sizeof(matched));
+ } while (ret == -1 && errno == EINTR);
+ if (ret != sizeof(matched)) {
+ perror("write");
+ _exit(EXIT_FAILURE);
+ }
+ _exit(EXIT_SUCCESS);
+ }
+ case -1:
+ // Error.
+ perror("fork");
+ exit(EXIT_FAILURE);
+ default:
+ // Parent.
+ close(pipefd[1]);
+ break;
+ }
+
+ // Wait for the child to finish.
+ int wstatus;
+ pid_t err;
+ do {
+ err = waitpid(child_pid, &wstatus, 0);
+ } while (err == -1 && errno == EINTR);
+ if (err == -1) {
+ perror("waitpid");
+ exit(EXIT_FAILURE);
+ }
+ if (WIFEXITED(wstatus)) {
+ if (WEXITSTATUS(wstatus) != 0) {
+ // The child has probably already printed out its error, so just propagate the exit status.
+ exit(WEXITSTATUS(wstatus));
+ }
+ // Success!
+ } else if (!WIFEXITED(wstatus)) {
+ fprintf(stderr, "FATAL: Child died unexpectedly while processing %s\n", filename.c_str());
+ exit(1);