pkgsrc-Bugs archive

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

pkg/60796: pkgsrc: distinfo.awk writes a corrupt distinfo when a tool produces no output



>Number:         60796
>Category:       pkg
>Synopsis:       pkgsrc: distinfo.awk writes a corrupt distinfo when a tool produces no output
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    pkg-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Sep 26 10:15:00 +0000 2026
>Originator:     Showta Ishizaki
>Release:        NetBSD 11.0, pkgsrc as of 2026-09-26
>Organization:
>Environment:
System: NetBSD 11.0 amd64
Architecture: x86_64
Machine: amd64
>Description:
	mk/checksum/distinfo.awk reads each digest and each size from a
	command with getline, and does not check what getline returned:

	  patchsum():  cmd | getline; close(cmd)
	  distsum():   cmd | getline lines[L++]; close(cmd)    (digest)
	               cmd | getline; close(cmd)               (wc -c)

	When the command prints nothing, $0 keeps the previous record, and
	that is what goes into the distinfo.  For patches the result is
	entries run together on one line:

	  SHA1 (a) = SHA1 (b) = <digest of b>

	For the size of a distfile it is whatever was read last, which is
	the RCS id at that point:

	  Size (a.tar.gz) = $NetBSD$ bytes

	and a distfile digest simply goes missing.

	The script exits 1 in all of these cases, which makesum and
	makepatchsum in mk/checksum/checksum.mk read as "the distinfo
	changed", so the damaged file is installed.  A patch whose line was
	damaged is then skipped with "Ignoring patch file ... invalid
	checksum", and the package fails with the very errors the patch
	exists to fix.

>How-To-Repeat:
	Run the script the way checksum.mk does, with a tool that prints
	nothing.  With security/polkit's five patches and two small
	distfiles, on NetBSD 11.0:

	  DIGEST=true ...:  8 lines, "SHA1 (" 15 times, 4 lines damaged,
	                    the SHA512 lines gone, exit 1
	  WC=true ...:      "Size (a.tar.gz) = $NetBSD$ bytes" twice, exit 1

	In a package directory the patch case is

	  make TOOLS_DIGEST=true makepatchsum

	and make returns 0.

>Fix:
	Read all three through one function that checks getline and, if
	nothing came back, says so on stderr and exits 0.  The exit status
	cannot carry the failure, because a non-zero status is what makes
	the caller install the output; and since everything is printed at
	the end of BEGIN, nothing has been written yet when it exits, so
	the existing distinfo is left alone.

	Measured on NetBSD 11.0 against today's distinfo.awk.  With digest
	or wc silent, the patched script exits 0 with a message and no
	output.  With working tools its output and exit status are the same
	as before, and in the patch-only form it reproduces
	security/polkit's distinfo.

	header() reads the first line of the existing distinfo the same
	way, but there an empty result falls through to a fresh $NetBSD$
	line, and I have not seen it go wrong, so I left it alone.

--- mk/checksum/distinfo.awk.orig
+++ mk/checksum/distinfo.awk
@@ -274,6 +274,36 @@
 	}
 	lines[L++] = "$" "NetBSD" "$"
 	lines[L++] = ""
+}
+
+###
+###	first_line(cmd)
+###		Run cmd and return the one line it prints.  Every digest
+###		and size in the distinfo comes through here.
+###
+###		getline's return value is checked because, when the
+###		command prints nothing, $0 (or the variable being read
+###		into) keeps whatever it held before, and that stale value
+###		would be written into the distinfo:
+###
+###		  SHA1 (a) = SHA1 (b) = <digest of b>
+###		  Size (a.tar.gz) = $NetBSD$ bytes
+###
+###		or, for a distfile digest, the line would silently go
+###		missing.  The exit status cannot report this: makesum and
+###		makepatchsum read it as "did the distinfo change" and
+###		install the output when it is non-zero.  Nothing has been
+###		printed yet at this point, so exiting zero leaves the
+###		existing distinfo alone, and the message says why.
+###
+function first_line(cmd,		line) {
+	if ((cmd | getline line) <= 0) {
+		close(cmd)
+		print self ": no output from: " cmd > "/dev/stderr"
+		exit(0)
+	}
+	close(cmd)
+	return line
 }
 
 ###
@@ -301,14 +331,14 @@
 			alg = algorithms[a]
 			if (cksumfiles[file] == 1) {
 				cmd = "cd " distdir " && " DIGEST " " alg " " file
-				cmd | getline lines[L++]; close(cmd)
+				lines[L++] = first_line(cmd)
 			} else if (ignorefiles[file] == 1) {
 				lines[L++] = alg " (" file ") = IGNORE"
 			}
 		}
 		if (cksumfiles[file] == 1) {
 			cmd = "cd " distdir " && " WC " -c " file
-			cmd | getline; close(cmd)
+			$0 = first_line(cmd)
 			sub("^[ ]+", ""); sub("[ ].*", "")
 			lines[L++] = "Size (" file ") = " $0 " bytes"
 		}
@@ -330,7 +360,7 @@
 		for (a = 0; a < P; a++) {
 			alg = patch_algorithms[a]
 			cmd = SED " -e '/[$]NetBSD.*/d' " patch " | " DIGEST " " alg
-			cmd | getline; close(cmd)
+			$0 = first_line(cmd)
 			sub("^", alg " (" file ") = ")
 			lines[L++] = $0
 		}




Home | Main Index | Thread Index | Old Index