When opening /dev/null internally, check that it really is a null device, and refuse to use it if not (#192).

This commit is contained in:
Andrew Wood
2026-05-03 19:37:27 +01:00
parent 6b19d8e31c
commit 10895792d2
3 changed files with 75 additions and 0 deletions
+7
View File
@@ -205,6 +205,13 @@ extern void pv_error(char *, ...);
*/
extern void pv_perror(char *, ...);
/*
* Check that a file descriptor is open on /dev/null, returning true if so.
* Otherwise return false; if the second argument is true, reports the
* problem with pv_error() before returning.
*/
extern bool pv_fd_is_dev_null(int, bool);
/*
* Create a new state structure, and return it, or 0 (NULL) on error.
+3
View File
@@ -216,6 +216,9 @@ static bool opts_watchfd_processname(opts_t opts, const char *process_name)
pv_perror("%s", "/dev/null");
exit(EXIT_FAILURE);
}
if (!pv_fd_is_dev_null(nullfd, true)) {
exit(EXIT_FAILURE);
}
if (dup2(nullfd, STDIN_FILENO) < 0) {
pv_error("%s", "dup2");
exit(EXIT_FAILURE);
+65
View File
@@ -18,11 +18,72 @@
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/utsname.h>
/*@-type@*/
/* splint has trouble with off_t and mode_t throughout this file. */
/*
* Check that a file descriptor is open on /dev/null, returning true if so.
* Otherwise return false; if the second argument is true, reports the
* problem with pv_error() before returning.
*
* No error is reported if the file descriptor is less than zero.
*/
bool pv_fd_is_dev_null(int fd, bool report_error)
{
struct stat sb;
struct utsname uts;
unsigned long expected_rdev;
bool is_dev_null;
if (fd < 0)
return false;
memset(&sb, 0, sizeof(sb));
if (fstat(fd, &sb) < 0) {
if (report_error)
pv_perror("%s", "/dev/null");
return false;
}
expected_rdev = 0x0103; /* device 1,3 on Linux. */
is_dev_null = true;
if (!S_ISCHR(sb.st_mode)) {
debug("%s", "/dev/null: not a character device");
is_dev_null = false;
}
memset(&uts, 0, sizeof(uts));
if (uname(&uts) >= 0) {
if (0 == strncmp(uts.sysname, "OpenBSD", 7)) {
expected_rdev = 0x0202;
} else if (0 == strncmp(uts.sysname, "FreeBSD", 7)) {
expected_rdev = 0x0022;
} else if (0 == strncmp(uts.sysname, "NetBSD", 6)) {
expected_rdev = 0x0202;
} else if (0 == strncmp(uts.sysname, "Darwin", 6)) {
expected_rdev = 0x03000002;
}
}
if ((unsigned long) (sb.st_rdev) != expected_rdev) {
debug("/dev/null: fd is %08lx, expected %08lx", sb.st_rdev, expected_rdev);
is_dev_null = false;
}
if (is_dev_null)
return true;
if (report_error)
pv_error("%s", _("/dev/null is not usable"));
return false;
}
/*
* Calculate the total number of bytes to be transferred by adding up the
* sizes of all input files. If any of the input files are of indeterminate
@@ -481,6 +542,10 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd)
(void) close(fd);
fd = -1;
}
if (!pv_fd_is_dev_null(state->transfer.discard_fd, true)) {
(void) close(fd);
fd = -1;
}
}
#endif /* HAVE_SPLICE */