Loading...
tests/fortify_source/fortify_source.py /dev/null Libc-1725.40.4
--- /dev/null
+++ Libc/Libc-1725.40.4/tests/fortify_source/fortify_source.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python3
+"""
+test_fortify_source.py
+
+This verifies that when _FORTIFY_SOURCE is enabled, the appropriate functions
+get defined to a _chk or _ptrchk variant (and if necessary, that the size is
+queried with the right kind of __builtin_object_size invocation).
+
+IF YOU ARE ADDING A CHECKED VARIANT FOR A FUNCTION:
+After adding your variant, run this script with -g to generate the new expected
+normal. Replace expected values in the PARAMETERS arrays with what you get.
+
+IF YOU ARE ADDING A NEW CONDITIONAL MODE:
+If you conditionally add a checked variant based on a C version or POSIX
+standard version, make sure that there is a TestParameters entry that covers the
+case where you expect the builtin to be used and at least one case where it is
+not. Add entries as needed.
+
+Run the script at desk with:
+
+% python3 fortify_source.py --verbose --check-syntax --include-dir $(DSTROOT)/usr/include fortify_source.c
+
+Don't forget to test with each SDK variant (ie, Darwin userspace, DriverKit, etc).
+"""
+from argparse import ArgumentParser
+from collections import namedtuple
+import os
+import re
+import subprocess
+import sys
+
+BADFUNC = "BADFUNC"
+UNDEFINED = "UNDEFINED"
+BUILTIN_OBSZ = "BUILTIN_OBSZ"
+BUILTIN_OBSZ0 = "BUILTIN_OBSZ0"
+PTRCHECK_OBSZ = "PTRCHECK_OBSZ"
+PTRCHECK_OBSZ0 = "PTRCHECK_OBSZ0"
+
+TestParameters = namedtuple(typename="TestParameters", field_names=["posix_c_source", "darwin_c_level", "fbounds_safety", "expected_driverkit", "expected_userspace"])
+
+PARAMETERS = [
+	TestParameters(posix_c_source=None, darwin_c_level=0, fbounds_safety=None,
+		expected_driverkit={"bcopy": UNDEFINED, "bzero": UNDEFINED, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": UNDEFINED, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": UNDEFINED, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": UNDEFINED, "strlcpy": UNDEFINED, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": UNDEFINED, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": UNDEFINED, "bzero": UNDEFINED, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": UNDEFINED, "sprintf": BUILTIN_OBSZ0, "stpcpy": UNDEFINED, "stpncpy": UNDEFINED, "strcat": BUILTIN_OBSZ0, "strcpy": BUILTIN_OBSZ0, "strlcat": UNDEFINED, "strlcpy": UNDEFINED, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": UNDEFINED, "vsprintf": UNDEFINED, },),
+
+	TestParameters(posix_c_source=None, darwin_c_level="200112", fbounds_safety=None,
+		expected_driverkit={"bcopy": UNDEFINED, "bzero": UNDEFINED, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": UNDEFINED, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": UNDEFINED, "strlcpy": UNDEFINED, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": UNDEFINED, "bzero": UNDEFINED, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": BUILTIN_OBSZ0, "stpcpy": UNDEFINED, "stpncpy": UNDEFINED, "strcat": BUILTIN_OBSZ0, "strcpy": BUILTIN_OBSZ0, "strlcat": UNDEFINED, "strlcpy": UNDEFINED, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": BUILTIN_OBSZ0, },),
+
+	TestParameters(posix_c_source=None, darwin_c_level="200809", fbounds_safety=None,
+		expected_driverkit={"bcopy": UNDEFINED, "bzero": UNDEFINED, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": BUILTIN_OBSZ0, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": UNDEFINED, "strlcpy": UNDEFINED, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": UNDEFINED, "bzero": UNDEFINED, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": BUILTIN_OBSZ0, "stpcpy": BUILTIN_OBSZ0, "stpncpy": BUILTIN_OBSZ0, "strcat": BUILTIN_OBSZ0, "strcpy": BUILTIN_OBSZ0, "strlcat": UNDEFINED, "strlcpy": UNDEFINED, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": BUILTIN_OBSZ0, },),
+
+	TestParameters(posix_c_source=None, darwin_c_level="__DARWIN_C_FULL", fbounds_safety=None,
+		expected_driverkit={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": BUILTIN_OBSZ0, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": BUILTIN_OBSZ0, "stpcpy": BUILTIN_OBSZ0, "stpncpy": BUILTIN_OBSZ0, "strcat": BUILTIN_OBSZ0, "strcpy": BUILTIN_OBSZ0, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": BUILTIN_OBSZ0, },),
+
+	TestParameters(posix_c_source="0", darwin_c_level="__DARWIN_C_FULL", fbounds_safety=None,
+		expected_driverkit={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": BUILTIN_OBSZ0, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": BUILTIN_OBSZ0, "stpcpy": BUILTIN_OBSZ0, "stpncpy": BUILTIN_OBSZ0, "strcat": BUILTIN_OBSZ0, "strcpy": BUILTIN_OBSZ0, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": BUILTIN_OBSZ0, },),
+
+	TestParameters(posix_c_source=None, darwin_c_level="__DARWIN_C_FULL", fbounds_safety=None,
+		expected_driverkit={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": BUILTIN_OBSZ0, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": BUILTIN_OBSZ0, "stpcpy": BUILTIN_OBSZ0, "stpncpy": BUILTIN_OBSZ0, "strcat": BUILTIN_OBSZ0, "strcpy": BUILTIN_OBSZ0, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": BUILTIN_OBSZ0, },),
+
+	TestParameters(posix_c_source=None, darwin_c_level="__DARWIN_C_FULL", fbounds_safety="on",
+		expected_driverkit={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": BUILTIN_OBSZ0, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": BUILTIN_OBSZ, "bzero": BUILTIN_OBSZ, "memccpy": BUILTIN_OBSZ, "memmove": BUILTIN_OBSZ, "memcpy": BUILTIN_OBSZ, "memset": BUILTIN_OBSZ, "snprintf": BUILTIN_OBSZ0, "sprintf": BUILTIN_OBSZ0, "stpcpy": BUILTIN_OBSZ0, "stpncpy": BUILTIN_OBSZ0, "strcat": BUILTIN_OBSZ0, "strcpy": BUILTIN_OBSZ0, "strlcat": BUILTIN_OBSZ0, "strlcpy": BUILTIN_OBSZ0, "strncat": BUILTIN_OBSZ0, "strncpy": BUILTIN_OBSZ0, "vsnprintf": BUILTIN_OBSZ0, "vsprintf": BUILTIN_OBSZ0, },),
+
+	TestParameters(posix_c_source=None, darwin_c_level="__DARWIN_C_FULL", fbounds_safety="on_attributes",
+		expected_driverkit={"bcopy": PTRCHECK_OBSZ, "bzero": PTRCHECK_OBSZ, "memccpy": PTRCHECK_OBSZ, "memmove": PTRCHECK_OBSZ, "memcpy": PTRCHECK_OBSZ, "memset": PTRCHECK_OBSZ, "snprintf": PTRCHECK_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": UNDEFINED, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": PTRCHECK_OBSZ, "strlcpy": PTRCHECK_OBSZ, "strncat": PTRCHECK_OBSZ, "strncpy": UNDEFINED, "vsnprintf": PTRCHECK_OBSZ0, "vsprintf": UNDEFINED, },
+		expected_userspace={"bcopy": PTRCHECK_OBSZ, "bzero": PTRCHECK_OBSZ, "memccpy": PTRCHECK_OBSZ, "memmove": PTRCHECK_OBSZ, "memcpy": PTRCHECK_OBSZ, "memset": PTRCHECK_OBSZ, "snprintf": PTRCHECK_OBSZ0, "sprintf": UNDEFINED, "stpcpy": UNDEFINED, "stpncpy": UNDEFINED, "strcat": UNDEFINED, "strcpy": UNDEFINED, "strlcat": PTRCHECK_OBSZ, "strlcpy": PTRCHECK_OBSZ, "strncat": UNDEFINED, "strncpy": UNDEFINED, "vsnprintf": PTRCHECK_OBSZ0, "vsprintf": UNDEFINED, },),
+]
+
+
+def parse_substitution_dictionary(stream):
+	test_line_re = re.compile(rb"^test_(\w+):\s*([^(;\s]*).*$")
+	result = {}
+	for line in stream:
+		m = test_line_re.match(line)
+		if not m:
+			continue
+		key = m.group(1).decode("ASCII")
+		if m.group(2) == b"undefined":
+			result[key] = UNDEFINED
+			continue
+
+		# for all substitutions except bcopy/bzero (which boil down to memcpy/memset),
+		# we check that the result line contains the right function name
+		colon = line.index(b":") + 1
+		if line.find(b"_" + m.group(1) + b"_", colon) == -1:
+			if key not in ["bcopy", "bzero"]:
+				result[key] = BADFUNC
+				continue
+
+		if m.group(2) == "":
+			if "__libc_ptrchk_strbuf_chk" in line:
+				result[key] = PTRCHECK_OBSZ
+		else:
+			builtin = b"__builtin" in m.group(2)
+			obsz = False
+			idx = line.find(b"__builtin_object_size")
+			if idx != -1:
+				open = line.index(b",", idx) + 1
+				close = line.find(b")", open)
+				obsz = line[open:close].strip() != b"0"
+
+			if (builtin, obsz) == (True, True):
+				result[key] = BUILTIN_OBSZ0
+			elif (builtin, obsz) == (True, False):
+				result[key] = BUILTIN_OBSZ
+			elif (builtin, obsz) == (False, True):
+				result[key] = PTRCHECK_OBSZ0
+			elif (builtin, obsz) == (False, False):
+				result[key] = PTRCHECK_OBSZ
+	return result
+
+
+def print_substitution_dictionary(d):
+	print("{", end="")
+	for k in sorted(d.keys()):
+		print('"%s": %s, ' % (k, d[k]), end="")
+	print("},")
+
+
+def main():
+	parser = ArgumentParser(description="test that bounds-sensitive functions have _chk substitutions")
+	parser.add_argument("test_file", default="./fortify_source.c", help="file to preprocess")
+	parser.add_argument("--sdk", default=os.getenv("SDKROOT"), help="SDK to test for (defaults to $SDKROOT)")
+	parser.add_argument("-g", "--generate", action="store_true", help="print observed output instead of comparing it with desired output")
+	parser.add_argument("-I", "--include-dir", action="append", default=[], help="Additional include paths (you can point this to the usr/include of a libc root)")
+	parser.add_argument("-v", "--verbose", action="store_true", help="Print clang commands that are executed")
+	parser.add_argument("-s", "--check-syntax", action="store_true", help="Also run clang with -fsyntax-only to check that this actually builds")
+	arguments = parser.parse_args()
+	if arguments.sdk is None:
+		sys.stderr.write("*** $SDKROOT is not set\n")
+		sys.exit(1)
+
+	is_driverkit = "driverkit" in arguments.sdk.lower()
+
+	def verbose_print(value):
+		if arguments.verbose:
+			print(" ".join(value))
+		return value
+
+	failed = False
+	for (posix_c_source, darwin_c_level, fbounds_safety, expected_dk, expected_us) in PARAMETERS:
+		clang_args = [
+			"xcrun", "--sdk", arguments.sdk, "clang", "-Werror", "-D_FORTIFY_SOURCE=2", arguments.test_file]
+		for inc in arguments.include_dir:
+			clang_args.append("-isystem")
+			clang_args.append(inc)
+		if posix_c_source is not None:
+			clang_args.append("-D_TEST_POSIX_C_SOURCE=%s" % posix_c_source)
+		if darwin_c_level is not None:
+			clang_args.append("-D_TEST_DARWIN_C_LEVEL=%s" % darwin_c_level)
+		if fbounds_safety == "on":
+			clang_args.append("-fbounds-safety")
+			clang_args.append("-DUNDEF_BOUNDS_SAFETY_ATTRIBUTES")
+		elif fbounds_safety == "on_attributes":
+			clang_args.append("-fbounds-safety")
+			clang_args.append("-D__LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES")
+
+		if arguments.check_syntax:
+			syntax_check = list(clang_args)
+			syntax_check.extend(("-fsyntax-only", "-Wno-nullability-completeness"))
+			subprocess.check_call(verbose_print(syntax_check))
+
+		clang_args.extend(("-E", "-o", "-"))
+		p = subprocess.Popen(verbose_print(clang_args), stdin=subprocess.DEVNULL, stdout=subprocess.PIPE)
+
+		d = parse_substitution_dictionary(p.stdout)
+		p.wait()
+		if arguments.generate:
+			print_substitution_dictionary(d)
+			continue
+
+		expected = expected_dk if is_driverkit else expected_us
+		if d == expected:
+			continue
+
+		failed = True
+		params = []
+		params.append("_POSIX_C_SOURCE=%r" % posix_c_source)
+		params.append("__DARWIN_C_LEVEL=%r" % darwin_c_level)
+		params.append("-f%sbounds-safety" % ("" if fbounds_safety else "no"))
+		if fbounds_safety:
+			params.append("-%s__LIBC_STAGED_BOUNDS_SAFETY_ATTRIBUTES" % ("D" if fbounds_safety == "on_attributes" else "U"))
+		print("*** FAILED: " + ", ".join(params))
+		for k in sorted(d.keys()):
+			if k not in expected:
+				print("***  %s is %s (missing in expected dictionary)" % (k, d[k]))
+			elif d[k] != expected[k]:
+				print("***  %s is %s (expected: %s)" % (k, d[k], expected[k]))
+		for k in sorted(expected.keys()):
+			if k not in d:
+				print("***  %s is missing (expected: %s)" % (k, expected[k]))
+
+	sys.exit(1 if failed else 0)
+
+
+if __name__ == "__main__":
+	main()