tech-pkg archive

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

Supporting MKPIE but without -pie embedded in wrapper scripts



Hello,

While trying to update lang/ghc90 to 9.0.2 and also enabling support for PKGSRC_MKPIE, I found the way how the current infrastructure enforces MKPIE rather cumbersome for packages that need to bootstrap from a pre-built bootkit.

GHC bootkits consist of a compiler executable and the standard Haskell library in the form of a set of static archives (lib*.a), which may or may not be built with -fPIC. When lang/ghc90 is to be built, with MKPIE_SUPPORTED set to yes, one of these things will happen:

1. Bootkit is built without -fPIC, and PKGSRC_MKPIE is set to no.

     This builds non-PIE GHC fine.

2. Bootkit is built with -fPIC, and PKGSRC_MKPIE is set to yes.

     This builds PIE GHC fine (with some tweaks applied to lang/ghc90).

3. Bootkit is built without -fPIC but PKGSRC_MKPIE is set to yes.

This fails to build the stage-1 compiler, which is an intermediate compiler to build the final, stage-2 compiler that will be installed. This is because pkgsrc forces -pie to be passed to the linker regardless of compilation stages (because it doesn't know GHC does a 2-stage bootstrapping), but the stage-1 compiler needs to be linked against static libraries coming from the non-PIC bootkit.

4. Bootkit is built with -fPIC but PKGSRC_MKPIE is set to no.

This means we have no choice but to link a non-PIC stage-1 compiler *.o against static libraries from the bootkit built with -fPIC. This apparently works, at least on NetBSD, but... you aren't supposed to do that right? I don't know if this works on all the platforms we currently support.

So, in order for packages like GHC to support MKPIE, we need a way to tell pkgsrc that we are going to build PIE on our own responsibility but not getting enforced by the infrastructure, such as injecting -pic in linker flags, on per-package basis. But this of course means packages that request an exemption from the enforcement can accidentally install non-PIE, so a post-build check is desired.

The attached patch introduces a new package-settable variable AUTOMATIC_MKPIE and a new check mentioned above. What do you think? Can I commit this?

? fetch/sites.mk.back
Index: bsd.prefs.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/bsd.prefs.mk,v
retrieving revision 1.415
diff -u -r1.415 bsd.prefs.mk
--- bsd.prefs.mk	30 Nov 2021 09:39:11 -0000	1.415
+++ bsd.prefs.mk	16 Jan 2022 10:14:32 -0000
@@ -27,6 +27,16 @@
 #	directory. Typical values look like editors/emacs or
 #	misc/openoffice-bin.
 #
+# Package-settable variables:
+#
+# AUTOMATIC_MKPIE
+#	When this variable is set to yes, MKPIE is enforced through tool
+#	wrappers. Setting it to no shifts the responsibility of building PIE
+#	from the pkgsrc infrastructure to an individual package.
+#
+#	Possible values: yes, no
+#	Default value: yes
+#
 # Keywords: mk.conf user platform
 #
 
@@ -736,11 +746,12 @@
 # Allows the security mitigation of ASLR to be used.
 # Impact: very small performance drop.
 #
-_PKGSRC_MKPIE=	no
+AUTOMATIC_MKPIE?=	yes
+_PKGSRC_MKPIE=		no
 .if ${PKGSRC_MKPIE:tl} == "yes" && \
     ${MKPIE_SUPPORTED:Uyes:tl} == "yes" && \
     ${_OPSYS_SUPPORTS_MKPIE:Uno} == "yes"
-_PKGSRC_MKPIE=	yes
+_PKGSRC_MKPIE=		yes
 .endif
 
 # Enable reproducible build flags
Index: cwrappers.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/cwrappers.mk,v
retrieving revision 1.31
diff -u -r1.31 cwrappers.mk
--- cwrappers.mk	7 May 2019 19:36:43 -0000	1.31
+++ cwrappers.mk	16 Jan 2022 10:14:32 -0000
@@ -89,9 +89,11 @@
 	${RUN}ln -s ${CWRAPPERS_SRC_DIR}/${CWRAPPERS_CONFIG.${wrappee}}-wrapper ${WRAPPER_BINDIR}/${alias}
 .  endfor
 . if ${_PKGSRC_MKPIE} == "yes"
-.  for arg in ${_MKPIE_LDFLAGS.gcc}
+.  if ${AUTOMATIC_MKPIE:tl} == "yes"
+.    for arg in ${_MKPIE_LDFLAGS.gcc}
 	${RUN}echo append_executable=${arg} >> ${CWRAPPERS_CONFIG_DIR}/${CWRAPPERS_CONFIG.${wrappee}}
-.  endfor
+.    endfor
+.  endif
 . endif
 .endfor
 
Index: check/bsd.check-vars.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/check/bsd.check-vars.mk,v
retrieving revision 1.10
diff -u -r1.10 bsd.check-vars.mk
--- check/bsd.check-vars.mk	3 Oct 2017 09:43:06 -0000	1.10
+++ check/bsd.check-vars.mk	16 Jan 2022 10:14:32 -0000
@@ -6,6 +6,7 @@
 #
 
 CHECK_FILES_SUPPORTED?=		yes
+CHECK_PIE_SUPPORTED?=		yes
 CHECK_RELRO_SUPPORTED?=		yes
 CHECK_SHLIBS_SUPPORTED?=	yes
 CHECK_SSP_SUPPORTED?=		yes
Index: check/bsd.check.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/check/bsd.check.mk,v
retrieving revision 1.10
diff -u -r1.10 bsd.check.mk
--- check/bsd.check.mk	3 Oct 2017 09:43:06 -0000	1.10
+++ check/bsd.check.mk	16 Jan 2022 10:14:32 -0000
@@ -29,6 +29,7 @@
 .include "check-headers.mk"
 .include "check-interpreter.mk"
 .include "check-perms.mk"
+.include "check-pie.mk"
 .include "check-portability.mk"
 .include "check-relro.mk"
 .include "check-shlibs.mk"
Index: check/check-pie-elf.awk
===================================================================
RCS file: check/check-pie-elf.awk
diff -N check/check-pie-elf.awk
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ check/check-pie-elf.awk	16 Jan 2022 10:14:32 -0000
@@ -0,0 +1,87 @@
+# $NetBSD$
+#
+# Read a list of potential ELF binaries from stdin. For each, extract the list
+# of headers. There are four possibilities:
+#
+# 1. Elf_Ehdr.e_type == ET_EXEC &&
+#    PT_INTERP does not exist
+#
+#	This is a statically-linked executable. Ignore these, as they cannot
+#	ever be a PIE.
+#
+# 2. Elf_Ehdr.e_type == ET_EXEC &&
+#    PT_INTERP exists in the program headers
+#
+#	This is a dynamically-linked non-PIE that this script complains about.
+#
+# 3. Elf_Ehdr.e_type == ET_DYN &&
+#    PT_INTERP exists in the program headers &&
+#    DT_FLAGS_1 exists in the dynamic section &&
+#    DT_FLAGS_1 contains DF_1_PIE
+#
+#	This is a PIE.
+#
+# 4. Elf_Ehdr.e_type == ET_DYN &&
+#    PT_INTERP does not exist in the program headers
+#
+#	This is a shared object.
+#
+# See also https://stackoverflow.com/a/55704865
+#
+
+function shquote(IN, out) {
+	out = IN;
+	gsub("\\\\", "\\\\", out);
+	gsub("\\\n", "\\n", out);
+	gsub("\\\t", "\\t", out);
+	gsub(" ", "\\ ", out);
+	gsub("'", "\\'", out);
+	gsub("`", "\\`", out);
+	gsub("\"", "\\\"", out);
+	gsub(";", "\\;", out);
+	gsub("&", "\\&", out);
+	gsub("<", "\\<", out);
+	gsub(">", "\\>", out);
+	gsub("\\(", "\\(", out);
+	gsub("\\)", "\\)", out);
+	gsub("\\|", "\\|", out);
+	gsub("\\*", "\\*", out);
+	gsub("\\?", "\\?", out);
+	gsub("\\{", "\\{", out);
+	gsub("\\}", "\\}", out);
+	gsub("\\[", "\\[", out);
+	gsub("\\]", "\\]", out);
+	gsub("\\$", "\\$", out);
+	gsub("!", "\\!", out);
+	gsub("#", "\\#", out);
+	gsub("\\^", "\\^", out);
+	gsub("~", "\\~", out);
+	return out;
+}
+
+function check_pie(ELF, is_non_pie, is_dyn_exec) {
+	is_non_pie = 0;
+	is_dyn_exec = 0;
+	cmd = readelf " -Whl " shquote(ELF) " 2>/dev/null";
+	while ((cmd | getline) > 0) {
+		if ($1 == "Type:" && $2 == "EXEC") {
+			is_non_pie = 1;
+		}
+		else if ($1 == "INTERP") {
+			is_dyn_exec = 1;
+		}
+	}
+	close(cmd);
+	if (is_non_pie == 1 && is_dyn_exec == 1) {
+		print ELF ": not a PIE";
+	}
+}
+
+BEGIN {
+	readelf = ENVIRON["READELF"];
+	if (readelf == "") {
+		readelf = "readelf";
+	}
+}
+
+{ check_pie($0); }
Index: check/check-pie.mk
===================================================================
RCS file: check/check-pie.mk
diff -N check/check-pie.mk
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ check/check-pie.mk	16 Jan 2022 10:14:32 -0000
@@ -0,0 +1,75 @@
+# $NetBSD
+#
+# This file verifies that MKPIE (position-independent executables) was applied
+# accordingly at build-time.
+#
+# User-settable variables:
+#
+# CHECK_PIE
+#	Whether the check should be enabled or not.
+#
+#	Default value: "yes" for PKG_DEVELOPERs, "no" otherwise.
+#
+# Package-settable variables:
+#
+# CHECK_PIE_SKIP
+#	A list of shell patterns (like man/*) that should be excluded
+#	from the check. Note that a * in a pattern also matches a slash
+#	in a pathname.
+#
+#	Default value: empty.
+#
+# CHECK_PIE_SUPPORTED
+#	Whether the check should be enabled for this package or not.
+#
+#	Default value: yes
+#
+
+_VARGROUPS+=		check-pie
+_USER_VARS.check-pie=	CHECK_PIE
+_PKG_VARS.check-pie=	CHECK_PIE_SUPPORTED
+
+.if ${_PKGSRC_MKPIE:Uno} != "no" && ${PKG_DEVELOPER:Uno:tl} != "no"
+CHECK_PIE?=		yes
+.else
+CHECK_PIE?=		no
+.endif
+CHECK_PIE_SUPPORTED?=	yes
+CHECK_PIE_SKIP?=	# none
+
+# All binaries but not libraries
+_CHECK_PIE_ERE=		(bin/|sbin/|libexec/)
+
+_CHECK_PIE_FILELIST_CMD?=					\
+	${SED} -e '/^@/d' ${PLIST} |				\
+	while read file; do					\
+		${TEST} -h "$$file" || ${ECHO} "$$file";	\
+	done
+
+_CHECK_PIE_CMD=		# empty
+.if ${OBJECT_FMT} == "ELF"
+USE_TOOLS+=		awk readelf
+_CHECK_PIE_CMD=		${AWK} -f ${PKGSRCDIR}/mk/check/check-pie-elf.awk
+_CHECK_PIE_ENV+=	READELF=${TOOLS_PATH.readelf:Q}
+.endif
+
+.if	${CHECK_PIE:tl} == "yes" && \
+	${CHECK_PIE_SUPPORTED:tl} == "yes" && \
+	!empty(_CHECK_PIE_CMD)
+privileged-install-hook: _check-pie
+_check-pie: error-check .PHONY
+	@${STEP_MSG} "Checking for PIE in ${PKGNAME}"
+	${RUN} rm -f ${ERROR_DIR}/${.TARGET}
+	${RUN}								\
+		cd ${DESTDIR:Q}${PREFIX:Q};				\
+		${_CHECK_PIE_FILELIST_CMD} |				\
+		${EGREP} -h ${_CHECK_PIE_ERE:Q} |			\
+		while read file; do					\
+			case "$$file" in				\
+				${CHECK_PIE_SKIP:@p@${p}) continue;;@}	\
+				*) ${ECHO} "$$file";			\
+			esac;						\
+		done |							\
+		${PKGSRC_SETENV} ${_CHECK_PIE_ENV} ${_CHECK_PIE_CMD}	\
+			> ${ERROR_DIR}/${.TARGET}
+.endif
Index: compiler/gcc.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/compiler/gcc.mk,v
retrieving revision 1.234
diff -u -r1.234 gcc.mk
--- compiler/gcc.mk	22 Dec 2021 10:14:27 -0000	1.234
+++ compiler/gcc.mk	16 Jan 2022 10:14:32 -0000
@@ -352,6 +352,7 @@
 # XXX for libraries a sink wrapper around gcc is required and used instead
 _MKPIE_LDFLAGS.gcc=	-pie
 
+.  if ${AUTOMATIC_MKPIE:tl} == "yes"
 _GCC_CFLAGS+=		${_MKPIE_CFLAGS.gcc}
 _GCC_FCFLAGS+=		${_MKPIE_FCFLAGS.gcc}
 #_GCC_LDFLAGS+=		${_MKPIE_LDFLAGS.gcc}
@@ -360,6 +361,7 @@
 CWRAPPERS_APPEND.f77+=	${_MKPIE_FCFLAGS.gcc}
 # this differs for libraries and executables (handled in mk/cwrappers.mk)
 # CWRAPPERS_APPEND.ld+=	${_MKPIE_LDFLAGS.gcc}
+.  endif
 .endif
 
 .if ${_PKGSRC_MKREPRO} == "yes"
Index: wrapper/bsd.wrapper.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/wrapper/bsd.wrapper.mk,v
retrieving revision 1.102
diff -u -r1.102 bsd.wrapper.mk
--- wrapper/bsd.wrapper.mk	27 Apr 2020 05:23:11 -0000	1.102
+++ wrapper/bsd.wrapper.mk	16 Jan 2022 10:14:32 -0000
@@ -304,7 +304,7 @@
 .if !empty(PKGSRC_COMPILER:Mgcc)
 _WRAP_TRANSFORM.CC=	${WRAPPER_TMPDIR}/transform-gcc
 _WRAP_TRANSFORM.CXX=	${_WRAP_TRANSFORM.CC}
-. if ${_PKGSRC_MKPIE} != "no"
+. if ${_PKGSRC_MKPIE} != "no" && ${AUTOMATIC_MKPIE:tl} != "no"
 _WRAP_CMD_SINK.CC=	${WRAPPER_TMPDIR}/cmd-sink-mkpie-gcc
 _WRAP_CMD_SINK.CXX=	${_WRAP_CMD_SINK.CC}
 _WRAP_CMD_SINK.LD=	${WRAPPER_TMPDIR}/cmd-sink-mkpie-ld



Home | Main Index | Thread Index | Old Index