17
17
#include < errno.h>
18
18
#include < fcntl.h>
19
19
#include < sys/eventfd.h>
20
+ #include < sys/mman.h>
20
21
#include < sys/ptrace.h>
21
22
#include < sys/resource.h>
22
23
#include < sys/time.h>
33
34
#include " gmock/gmock.h"
34
35
#include " gtest/gtest.h"
35
36
#include " absl/strings/match.h"
37
+ #include " absl/strings/numbers.h"
36
38
#include " absl/strings/str_cat.h"
39
+ #include " absl/strings/str_format.h"
37
40
#include " absl/strings/str_split.h"
38
41
#include " absl/strings/string_view.h"
39
42
#include " absl/synchronization/mutex.h"
40
43
#include " absl/types/optional.h"
41
44
#include " test/util/file_descriptor.h"
42
45
#include " test/util/fs_util.h"
46
+ #include " test/util/logging.h"
47
+ #include " test/util/memory_util.h"
43
48
#include " test/util/multiprocess_util.h"
44
49
#include " test/util/posix_error.h"
50
+ #include " test/util/save_util.h"
45
51
#include " test/util/temp_path.h"
46
52
#include " test/util/test_util.h"
47
53
#include " test/util/thread_util.h"
@@ -63,6 +69,8 @@ constexpr char kPriorityWorkload[] = "test/syscalls/linux/priority_execve";
63
69
constexpr char kExit42 [] = " --exec_exit_42" ;
64
70
constexpr char kExecWithThread [] = " --exec_exec_with_thread" ;
65
71
constexpr char kExecFromThread [] = " --exec_exec_from_thread" ;
72
+ constexpr char kExecInParent [] = " --exec_exec_in_parent" ;
73
+ constexpr char kWriteAndWaitForPid [] = " --exec_write_and_wait_for_pid" ;
66
74
67
75
// Runs file specified by dirfd and pathname with argv and checks that the exit
68
76
// status is expect_status and that stderr contains expect_stderr.
@@ -851,6 +859,101 @@ TEST(ExecTest, Setpgid) {
851
859
EXPECT_THAT (setpgid (getpid (), pid), SyscallSucceeds ());
852
860
}
853
861
862
+ TEST (ExecTest, ReadProcMemAfterExecFromChild) {
863
+ // Fork and exec in the parent process.
864
+ CheckExec (" /proc/self/exe" , {" /proc/self/exe" , kExecInParent }, {},
865
+ W_EXITCODE (42 , 0 ), " " );
866
+ }
867
+
868
+ /*
869
+ This function, along with writeAndWaitForPid, sets up the test case to verify
870
+ that a /proc/self/mem file descriptor is not leaked across an execve similar to
871
+ b/382136040.
872
+
873
+ The setup is as follows:
874
+
875
+ This function opens /proc/self/mem in a process we'll call P1.
876
+ * P1 forks, creating a child process P2.
877
+ * P1 (the parent) then execves to become a new process, P3, which runs
878
+ writeAndWaitForPid.
879
+ * P2 (the child) holds the memfd from P1. It waits for a
880
+ signal from P3.
881
+ * P3 maps some memory with a secret value and then signals P2 via
882
+ a pipe.
883
+ * P2, upon receiving the signal, tries to read from the secret's memory
884
+ location using the memfd it inherited. The test asserts that this pread in P2
885
+ fails, because the memfd should be tied to the (now defunct) address space of
886
+ P1, not the new address space of P3.
887
+ */
888
+ void execInParent () {
889
+ auto mem_fd = ASSERT_NO_ERRNO_AND_VALUE (Open (" /proc/self/mem" , O_RDONLY));
890
+ int pipe_fd[2 ] = {};
891
+ TEST_PCHECK (pipe (pipe_fd) == 0 );
892
+
893
+ const auto readSecret = [&] {
894
+ // Close writing end of the pipe.
895
+ close (pipe_fd[1 ]);
896
+ // Await parent OK to read the secret.
897
+ uintptr_t addr;
898
+ TEST_PCHECK_MSG (ReadFd (pipe_fd[0 ], &addr, sizeof (addr)) == sizeof (addr),
899
+ " Failed to read mmap address from the pipe." );
900
+ // Close the reading end of the pipe.
901
+ TEST_PCHECK (close (pipe_fd[0 ]) == 0 );
902
+ // Parent sent the signal. Read the secret.
903
+ // Use /proc/mem fd from parent to try to read the secret.
904
+ char secret[] = " secret" ;
905
+ char output[sizeof (secret)];
906
+ TEST_PCHECK_MSG (
907
+ pread (mem_fd.get (), output, sizeof (output), addr) != sizeof (secret),
908
+ " pread succeeded. It should have failed." );
909
+ };
910
+ pid_t pid = fork ();
911
+ // In child process.
912
+ if (pid == 0 ) {
913
+ readSecret ();
914
+ TEST_CHECK_MSG (!::testing::Test::HasFailure (),
915
+ " EXPECT*/ASSERT* failed. These are not async-signal-safe "
916
+ " and must not be called from fn." );
917
+ _exit (0 );
918
+ }
919
+ // In parent process.
920
+ MaybeSave ();
921
+ TEST_PCHECK (close (pipe_fd[0 ]) == 0 );
922
+ TEST_CHECK (pid != -1 );
923
+
924
+ // Execve with args{function name, child_pid, pipe_fd[1]}
925
+ const ExecveArray argv = {" /proc/self/exe" , kWriteAndWaitForPid ,
926
+ absl::StrCat (pid) /* child_pid*/ ,
927
+ absl::StrCat (pipe_fd[1 ])};
928
+ const ExecveArray envv;
929
+ execve (" /proc/self/exe" , argv.get (), envv.get ());
930
+ }
931
+ /*
932
+ This function is the new program image after execve in execInParent. It maps a
933
+ "secret" value at a known address, signals the child of the original process to
934
+ proceed, and then waits for it to exit.
935
+ */
936
+ void writeAndWaitForPid (int child_pid, int pipe_fd) {
937
+ // mmap the same address that the child process will try to read.
938
+ const Mapping m = ASSERT_NO_ERRNO_AND_VALUE (
939
+ MmapAnon (kPageSize , PROT_READ | PROT_WRITE, MAP_PRIVATE));
940
+ const char secret[] = " secret" ;
941
+ absl::SNPrintF ((char *)m.addr (), sizeof (secret), " %s" , secret);
942
+
943
+ // Tell the child process to read the secret location.
944
+ uintptr_t addr = m.addr ();
945
+ TEST_PCHECK_MSG (WriteFd (pipe_fd, &addr, sizeof (addr)) == sizeof (addr),
946
+ " Failed to write mmap address to the pipe." );
947
+ TEST_PCHECK (close (pipe_fd) == 0 );
948
+
949
+ // Wait for child process to read before the exit.
950
+ int status;
951
+ TEST_PCHECK_MSG (waitpid (child_pid, &status, 0 ) == child_pid,
952
+ " waitpid failed." );
953
+ TEST_CHECK (WIFEXITED (status) && WEXITSTATUS (status) == 0 );
954
+ exit (42 );
955
+ }
956
+
854
957
void ExecWithThread () {
855
958
// Used to ensure that the thread has actually started.
856
959
absl::Mutex mu;
@@ -948,6 +1051,22 @@ int main(int argc, char** argv) {
948
1051
gvisor::testing::ExecFromThread ();
949
1052
return 1 ;
950
1053
}
1054
+ if (arg == gvisor::testing::kExecInParent ) {
1055
+ gvisor::testing::execInParent ();
1056
+ return 1 ;
1057
+ }
1058
+ if (arg == gvisor::testing::kWriteAndWaitForPid ) {
1059
+ int pid;
1060
+ if (!absl::SimpleAtoi (argv[i + 1 ], &pid)) {
1061
+ return 1 ;
1062
+ }
1063
+ int fd;
1064
+ if (!absl::SimpleAtoi (argv[i + 2 ], &fd)) {
1065
+ return 1 ;
1066
+ }
1067
+ gvisor::testing::writeAndWaitForPid (pid, fd);
1068
+ return 1 ;
1069
+ }
951
1070
}
952
1071
953
1072
gvisor::testing::TestInit (&argc, &argv);
0 commit comments