1 /* Verify that getcwd returns ERANGE for size 1 byte and does not underflow
2 buffer when the CWD is too long and is also a mount target of /. See bug
3 #28769 or CVE-2021-3999 for more context.
4 Copyright The GNU Toolchain Authors.
5 This file is part of the GNU C Library.
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, see
19 <https://www.gnu.org/licenses/>. */
28 #include <sys/mount.h>
30 #include <sys/types.h>
33 #include <sys/socket.h>
35 #include <support/check.h>
36 #include <support/temp_file.h>
37 #include <support/test-driver.h>
38 #include <support/xsched.h>
39 #include <support/xunistd.h>
42 #define BASENAME "tst-getcwd-smallbuff"
43 #define MOUNT_NAME "mpoint"
49 support_chdir_toolong_temp_directory (base
);
50 TEST_VERIFY_EXIT (rmdir (MOUNT_NAME
) == 0);
55 send_fd (const int sock
, const int fd
)
57 struct msghdr msg
= {0};
61 char buf
[CMSG_SPACE (sizeof (int))];
68 msg
.msg_control
= &cmsgbuf
.buf
;
69 msg
.msg_controllen
= sizeof (cmsgbuf
.buf
);
71 cmsg
= CMSG_FIRSTHDR (&msg
);
72 cmsg
->cmsg_len
= CMSG_LEN (sizeof (int));
73 cmsg
->cmsg_level
= SOL_SOCKET
;
74 cmsg
->cmsg_type
= SCM_RIGHTS
;
75 memcpy (CMSG_DATA (cmsg
), &fd
, sizeof (fd
));
82 while ((n
= sendmsg (sock
, &msg
, 0)) == -1 && errno
== EINTR
);
84 TEST_VERIFY_EXIT (n
== 1);
88 recv_fd (const int sock
)
90 struct msghdr msg
= {0};
94 char buf
[CMSG_SPACE(sizeof(int))];
107 msg
.msg_control
= &cmsgbuf
.buf
;
108 msg
.msg_controllen
= sizeof (cmsgbuf
.buf
);
110 while ((n
= recvmsg (sock
, &msg
, 0)) == -1 && errno
== EINTR
);
111 if (n
!= 1 || ch
!= 'A')
114 cmsg
= CMSG_FIRSTHDR (&msg
);
117 if (cmsg
->cmsg_type
!= SCM_RIGHTS
)
119 memcpy (&fd
, CMSG_DATA (cmsg
), sizeof (fd
));
126 child_func (void * const arg
)
129 const int sock
= sockfd
[1];
132 TEST_VERIFY_EXIT (read (sock
, &ch
, 1) == 1);
133 TEST_VERIFY_EXIT (ch
== '1');
135 if (mount ("/", MOUNT_NAME
, NULL
, MS_BIND
| MS_REC
, NULL
))
136 FAIL_EXIT1 ("mount failed: %m\n");
137 const int fd
= xopen ("mpoint",
138 O_RDONLY
| O_PATH
| O_DIRECTORY
| O_NOFOLLOW
, 0);
143 TEST_VERIFY_EXIT (read (sock
, &ch
, 1) == 1);
144 TEST_VERIFY_EXIT (ch
== 'a');
151 update_map (char * const mapping
, const char * const map_file
)
153 const size_t map_len
= strlen (mapping
);
155 const int fd
= xopen (map_file
, O_WRONLY
, 0);
156 xwrite (fd
, mapping
, map_len
);
161 proc_setgroups_write (const long child_pid
, const char * const str
)
163 const size_t str_len
= strlen(str
);
165 char setgroups_path
[sizeof ("/proc//setgroups") + INT_STRLEN_BOUND (long)];
167 snprintf (setgroups_path
, sizeof (setgroups_path
),
168 "/proc/%ld/setgroups", child_pid
);
170 const int fd
= open (setgroups_path
, O_WRONLY
);
174 TEST_VERIFY_EXIT (errno
== ENOENT
);
175 FAIL_UNSUPPORTED ("/proc/%ld/setgroups not found\n", child_pid
);
178 xwrite (fd
, str
, str_len
);
182 static char child_stack
[1024 * 1024];
187 base
= support_create_and_chdir_toolong_temp_directory (BASENAME
);
189 xmkdir (MOUNT_NAME
, S_IRWXU
);
192 /* Check whether user namespaces are supported. */
194 pid_t pid
= xfork ();
197 if (unshare (CLONE_NEWUSER
| CLONE_NEWNS
) != 0)
198 _exit (EXIT_UNSUPPORTED
);
203 xwaitpid (pid
, &status
, 0);
204 TEST_VERIFY_EXIT (WIFEXITED (status
));
205 if (WEXITSTATUS (status
) != 0)
206 return WEXITSTATUS (status
);
209 TEST_VERIFY_EXIT (socketpair (AF_UNIX
, SOCK_STREAM
, 0, sockfd
) == 0);
210 pid_t child_pid
= xclone (child_func
, NULL
, child_stack
,
211 sizeof (child_stack
),
212 CLONE_NEWUSER
| CLONE_NEWNS
| SIGCHLD
);
215 const int sock
= sockfd
[0];
217 char map_path
[sizeof ("/proc//uid_map") + INT_STRLEN_BOUND (long)];
218 char map_buf
[sizeof ("0 1") + INT_STRLEN_BOUND (long)];
220 snprintf (map_path
, sizeof (map_path
), "/proc/%ld/uid_map",
222 snprintf (map_buf
, sizeof (map_buf
), "0 %ld 1", (long) getuid());
223 update_map (map_buf
, map_path
);
225 proc_setgroups_write ((long) child_pid
, "deny");
226 snprintf (map_path
, sizeof (map_path
), "/proc/%ld/gid_map",
228 snprintf (map_buf
, sizeof (map_buf
), "0 %ld 1", (long) getgid());
229 update_map (map_buf
, map_path
);
231 TEST_VERIFY_EXIT (send (sock
, "1", 1, MSG_NOSIGNAL
) == 1);
232 const int fd
= recv_fd (sock
);
233 TEST_VERIFY_EXIT (fd
>= 0);
234 TEST_VERIFY_EXIT (fchdir (fd
) == 0);
236 static char buf
[2 * 10 + 1];
237 memset (buf
, 'A', sizeof (buf
));
239 /* Finally, call getcwd and check if it resulted in a buffer underflow. */
240 char * cwd
= getcwd (buf
+ sizeof (buf
) / 2, 1);
241 TEST_VERIFY (cwd
== NULL
);
242 TEST_VERIFY (errno
== ERANGE
);
244 for (int i
= 0; i
< sizeof (buf
); i
++)
247 printf ("buf[%d] = %02x\n", i
, (unsigned int) buf
[i
]);
248 support_record_failure ();
251 TEST_VERIFY_EXIT (send (sock
, "a", 1, MSG_NOSIGNAL
) == 1);
253 TEST_VERIFY_EXIT (xwaitpid (child_pid
, NULL
, 0) == child_pid
);
258 #define CLEANUP_HANDLER do_cleanup
259 #include <support/test-driver.c>