NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

bin/59431: install(1) may report bogus write(2) error



>Number:         59431
>Category:       bin
>Synopsis:       install(1) may report bogus write(2) error
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat May 17 16:00:00 +0000 2025
>Originator:     Taylor R Campbell
>Release:        current, 10, 9, ...
>Organization:
The WriteBSD Installation
>Environment:
>Description:
install(1) will sometimes mmap the source file and write from the mmapped buffer:

    905 		/*
    906 		 * Mmap and write if less than 8M (the limit is so we
    907 		 * don't totally trash memory on big files).  This is
    908 		 * really a minor hack, but it wins some CPU back.
    909 		 */
    910 
    911 		if (size <= 8 * 1048576) {
    912 			if ((p = mmap(NULL, (size_t)size, PROT_READ,
    913 			    MAP_FILE|MAP_SHARED, from_fd, (off_t)0))
    914 			    == MAP_FAILED) {
    915 				goto mmap_failed;
    916 			}
    917 #if defined(MADV_SEQUENTIAL) && !defined(__APPLE__)
    918 			if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1
    919 			    && errno != EOPNOTSUPP)
    920 				warn("madvise");
    921 #endif
    922 
    923 			if (to_fd >= 0 && write(to_fd, p, size) != size) {
    924 				serrno = errno;
    925 				(void)unlink(to_name);
    926 				errc(EXIT_FAILURE, serrno, "%s: write",
    927 				    to_name);
    928 			}

https://nxr.netbsd.org/xref/src/usr.bin/xinstall/xinstall.c?r=1.130#905

If the source file is truncated, however, _during write(2)_, the write may _partially_ succeed, short of the requested size.  (The next write will probably fail with EFAULT.)
>How-To-Repeat:
concurrently truncate a source file and install it to a target file, as in PR toolchain/57241: mips64el--netbsd-install core dumps randomly (https://gnats.NetBSD.org/57241)
>Fix:
ssize_t nwrit;

if (to_fd >= 0) {
        if ((nwrit = write(to_fd, p, size)) == -1) {
                serror = errno;
                (void)unlink(to_name);
                errc(EXIT_FAILURE, serrno, "%s: write", to_name);
        } else if ((size_t)nwrit != size) {
                (void)unlink(to_name);
                errx(EXIT_FAILURE, "%s: write truncated, %zu != %zu",
                    (size_t)nwrit, size);
        }
}

Actually this should maybe do write in a loop -- I'm not sure POSIX guarantees complete write(2) even on success except for PIPE_BUF-sized writes to pipes.  But this change should be good for now to mitigate the red herrings.



Home | Main Index | Thread Index | Old Index