Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
libpkg: add SHLIB_PROVIDE_PATHS_* options
Isaac Freund committed 1 year ago
commit a35c0a302ad81813b121d94924d6ad6b6d691d21
parent dfee49c
10 files changed +241 -23
modified libpkg/pkg_abi.c
@@ -22,6 +22,7 @@
#include "private/binfmt.h"
#include "private/event.h"
#include "private/pkg.h"
+
#include "private/utils.h"
#include "xmalloc.h"

#define _PATH_UNAME "/usr/bin/uname"
@@ -448,7 +449,8 @@ pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
	bool failures = false;

	int (*pkg_analyse_init)(const char *stage);
-
	int (*pkg_analyse)(const bool developer_mode, struct pkg *pkg, const char *fpath);
+
	int (*pkg_analyse)(const bool developer_mode, struct pkg *pkg,
+
	    const char *fpath, char **provided, enum pkg_shlib_flags *flags);
	int (*pkg_analyse_close)();

	if (0 == strncmp(pkg->abi, "Darwin", 6)) {
@@ -479,6 +481,11 @@ pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
		pkg->flags &= ~(PKG_CONTAINS_ELF_OBJECTS |
		    PKG_CONTAINS_STATIC_LIBS | PKG_CONTAINS_LA);

+
	/* shlibs that are provided by files in the package but not matched by
+
	   SHLIB_PROVIDE_PATHS_* are still used to filter the shlibs
+
	   required by the package */
+
	stringlist_t internal_provided = tll_init();
+

	while (pkg_files(pkg, &file) == EPKG_OK) {
		if (stage != NULL)
			snprintf(fpath, sizeof(fpath), "%s/%s", stage,
@@ -486,10 +493,43 @@ pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
		else
			strlcpy(fpath, file->path, sizeof(fpath));

-
		ret = pkg_analyse(ctx.developer_mode, pkg, fpath);
+
		char *provided = NULL;
+
		enum pkg_shlib_flags provided_flags = PKG_SHLIB_FLAGS_NONE;
+
		ret = pkg_analyse(ctx.developer_mode, pkg, fpath, &provided, &provided_flags);
		if (EPKG_WARN == ret) {
			failures = true;
		}
+

+
		if (provided != NULL) {
+
			const ucl_object_t *paths = NULL;
+

+
			switch (provided_flags) {
+
			case PKG_SHLIB_FLAGS_NONE:
+
				paths = pkg_config_get("SHLIB_PROVIDE_PATHS_NATIVE");
+
				break;
+
			case PKG_SHLIB_FLAGS_COMPAT_32:
+
				paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_32");
+
				break;
+
			case PKG_SHLIB_FLAGS_COMPAT_LINUX:
+
				paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_LINUX");
+
				break;
+
			case (PKG_SHLIB_FLAGS_COMPAT_32 | PKG_SHLIB_FLAGS_COMPAT_LINUX):
+
				paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_LINUX_32");
+
				break;
+
			default:
+
				assert(0);
+
			}
+
			assert(paths != NULL);
+

+
			/* If the corresponding PATHS option isn't set (i.e. an empty ucl array)
+
			   don't do any filtering for backwards compatibility. */
+
			if (ucl_array_size(paths) == 0 || pkg_match_paths_list(paths, file->path)) {
+
				pkg_addshlib_provided(pkg, provided, provided_flags);
+
				free(provided);
+
			} else {
+
				tll_push_back(internal_provided, provided);
+
			}
+
		}
	}

	/*
@@ -497,7 +537,8 @@ pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
	 */
	tll_foreach(pkg->shlibs_required, s)
	{
-
		if (stringlist_contains(&pkg->shlibs_provided, s->item)) {
+
		if (stringlist_contains(&pkg->shlibs_provided, s->item) ||
+
		    stringlist_contains(&internal_provided, s->item)) {
			pkg_debug(2,
			    "remove %s from required shlibs as the "
			    "package %s provides this library itself",
@@ -521,6 +562,8 @@ pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage)
		}
	}

+
	tll_free_and_free(internal_provided, free);
+

	/*
	 * if the package is not supposed to provide share libraries then
	 * drop the provided one
modified libpkg/pkg_abi_macho.c
@@ -301,8 +301,12 @@ cleanup:
}

static int
-
analyse_macho(int fd, struct pkg *pkg)
+
analyse_macho(int fd, struct pkg *pkg, char **provided,
+
    enum pkg_shlib_flags *provided_flags)
{
+
	assert(*provided == NULL);
+
	assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
+

	ssize_t x;
	pkg_error_t ret = EPKG_END;

@@ -381,7 +385,11 @@ analyse_macho(int fd, struct pkg *pkg)
				xasprintf(&lib_with_version, "%s-%"PRIuFAST16".%"PRIuFAST16, basename, dylib->current_version.major, dylib->current_version.minor);
			}
			if (LC_ID_DYLIB == loadcmd) {
-
				pkg_addshlib_provided(pkg, lib_with_version, PKG_SHLIB_FLAGS_NONE);
+
				if (*provided != NULL) {
+
					pkg_emit_error("malformed Macho-O file has multiple LC_ID_DYLIB entries");
+
					goto cleanup;
+
				}
+
				*provided = xstrdup(lib_with_version);
			} else {
				pkg_addshlib_required(pkg, lib_with_version, PKG_SHLIB_FLAGS_NONE);
			}
@@ -416,8 +424,12 @@ pkg_analyse_init_macho(__unused const char *stage)
}

int
-
pkg_analyse_macho(const bool developer_mode, struct pkg *pkg, const char *fpath)
+
pkg_analyse_macho(const bool developer_mode, struct pkg *pkg,
+
    const char *fpath, char **provided, enum pkg_shlib_flags *provided_flags)
{
+
	assert(*provided == NULL);
+
	assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
+

	int ret = EPKG_OK;
	pkg_debug(1, "Analysing Mach-O %s", fpath);

@@ -428,7 +440,7 @@ pkg_analyse_macho(const bool developer_mode, struct pkg *pkg, const char *fpath)
		// Be consistent with analyse_elf and return no error if fpath cannot be opened
		return ret;
	} else {
-
		ret = analyse_macho(fd, pkg);
+
		ret = analyse_macho(fd, pkg, provided, provided_flags);
		if (-1 == close(fd)) {
			pkg_emit_errno("close_pkg_analyse_macho", fpath);
			ret = EPKG_FATAL;
modified libpkg/pkg_config.c
@@ -430,6 +430,26 @@ static struct config_entry c[] = {
	},
	{
		PKG_ARRAY,
+
		"SHLIB_PROVIDE_PATHS_NATIVE",
+
		NULL,
+
	},
+
	{
+
		PKG_ARRAY,
+
		"SHLIB_PROVIDE_PATHS_COMPAT_32",
+
		NULL,
+
	},
+
	{
+
		PKG_ARRAY,
+
		"SHLIB_PROVIDE_PATHS_COMPAT_LINUX",
+
		NULL,
+
	},
+
	{
+
		PKG_ARRAY,
+
		"SHLIB_PROVIDE_PATHS_COMPAT_LINUX_32",
+
		NULL,
+
	},
+
	{
+
		PKG_ARRAY,
		"SHLIB_REQUIRE_IGNORE_GLOB",
		NULL,
	},
modified libpkg/pkg_elf.c
@@ -65,8 +65,12 @@ typedef Elf32_Nhdr Elf_Note;
#endif

static int
-
analyse_elf(struct pkg *pkg, const char *fpath)
+
analyse_elf(struct pkg *pkg, const char *fpath, char **provided,
+
    enum pkg_shlib_flags *provided_flags)
{
+
	assert(*provided == NULL);
+
	assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
+

	int ret = EPKG_OK;

	pkg_debug(1, "analysing elf %s", fpath);
@@ -232,7 +236,13 @@ analyse_elf(struct pkg *pkg, const char *fpath)
		}

		if (dyn->d_tag == DT_SONAME) {
-
			pkg_addshlib_provided(pkg, shlib, flags);
+
			if (*provided != NULL) {
+
				pkg_emit_error("malformed ELF file %s has "
+
				    "multiple DT_SONAME entries", fpath);
+
				goto cleanup;
+
			}
+
			*provided = xstrdup(shlib);
+
			*provided_flags = flags;
		} else if (dyn->d_tag == DT_NEEDED) {
			/*
			 * some packages record fullpath to a lib
@@ -659,9 +669,13 @@ int pkg_analyse_init_elf(__unused const char* stage) {
	return (EPKG_OK);
}

-
int pkg_analyse_elf(const bool developer_mode, struct pkg *pkg, const char *fpath)
+
int pkg_analyse_elf(const bool developer_mode, struct pkg *pkg,
+
    const char *fpath, char **provided, enum pkg_shlib_flags *provided_flags)
{
-
	int ret = analyse_elf(pkg, fpath);
+
	assert(*provided == NULL);
+
	assert(*provided_flags == PKG_SHLIB_FLAGS_NONE);
+

+
	int ret = analyse_elf(pkg, fpath, provided, provided_flags);
	if (developer_mode) {
		if (ret != EPKG_OK && ret != EPKG_END) {
			return EPKG_WARN;
modified libpkg/private/binfmt.h
@@ -17,10 +17,12 @@ enum pkg_provide_flags {

int pkg_elf_abi_from_fd(int fd, struct pkg_abi *abi);
int pkg_analyse_init_elf(const char* stage);
-
int pkg_analyse_elf(const bool developer_mode, struct pkg *pkg, const char *fpath);
+
int pkg_analyse_elf(const bool developer_mode, struct pkg *pkg,
+
    const char *fpath, char **provided, enum pkg_shlib_flags *provided_flags);
int pkg_analyse_close_elf();

int pkg_macho_abi_from_fd(int fd, struct pkg_abi *abi, enum pkg_arch arch_hint);
int pkg_analyse_init_macho(const char* stage);
-
int pkg_analyse_macho(const bool developer_mode, struct pkg *pkg, const char *fpath);
+
int pkg_analyse_macho(const bool developer_mode, struct pkg *pkg,
+
    const char *fpath, char **provided, enum pkg_shlib_flags *provided_flags);
int pkg_analyse_close_macho();
modified libpkg/private/utils.h
@@ -111,6 +111,7 @@ bool mkdirat_p(int fd, const char *path);
int get_socketpair(int *);
int checkflags(const char *mode, int *optr);
bool match_ucl_lists(const char *buffer, const ucl_object_t *globs, const ucl_object_t *regexes);
+
bool pkg_match_paths_list(const ucl_object_t *paths, const char *file);
char *get_dirname(char *dir);
char *rtrimspace(char *buf);
void hidden_tempfile(char *buf, int buflen, const char *path);
modified libpkg/utils.c
@@ -97,6 +97,75 @@ match_ucl_lists(const char *buf, const ucl_object_t *globs, const ucl_object_t *
	return (false);
}

+
/* Check if two absolute directory paths are equal, collapsing consecutive
+
 * path separators and ignoring trailing path separators. */
+
static bool
+
dir_paths_equal(const char *a, const char *b)
+
{
+
	assert(a != NULL);
+
	assert(b != NULL);
+
	assert(*a == '/');
+
	assert(*b == '/');
+

+
	while (*a == *b) {
+
		if (*a == '\0') {
+
			return (true);
+
		}
+

+
		/* Skip over consecutive path separators */
+
		if (*a == '/') {
+
			while (*a == '/') a++;
+
			while (*b == '/') b++;
+
		} else {
+
			a++;
+
			b++;
+
		}
+
	}
+

+
	/* There may be trailing path separators on one path but not the other */
+
	while (*a == '/') a++;
+
	while (*b == '/') b++;
+

+
	return (*a == *b);
+
}
+

+
/*
+
 * Given a ucl list of directory paths, check if the file is in one of the
+
 * directories in the list (subdirectories not included).
+
 *
+
 * Asserts that file is an absolute path that does not end in /. */
+
bool
+
pkg_match_paths_list(const ucl_object_t *paths, const char *file)
+
{
+
	assert(file != NULL);
+
	assert(file[0] == '/');
+

+
	char *copy = xstrdup(file);
+
	char *final_slash = strrchr(copy, '/');
+
	assert(final_slash != NULL);
+
	assert(*(final_slash + 1) != '\0');
+
	if (final_slash == copy) {
+
		*(final_slash + 1) = '\0';
+
	} else {
+
		*final_slash = '\0';
+
	}
+
	const char *dirname = copy;
+

+
	bool found = false;
+
	const ucl_object_t *cur;
+
	ucl_object_iter_t it = NULL;
+
	while ((cur = ucl_object_iterate(paths, &it, true))) {
+
		if (dir_paths_equal(dirname, ucl_object_tostring(cur))) {
+
			found = true;
+
			break;
+
		}
+
	}
+

+
	free(copy);
+

+
	return (found);
+
}
+

int
pkg_mkdirs(const char *_path)
{
modified tests/frontend/create-parsebin.sh
@@ -13,6 +13,8 @@ genmanifest() {
    local PKG_SHA256=""
    local NL="
"
+
    local hide_provided="$1"
+
    shift

    bin_meta "$1"
    while [ -n "$1" ]; do
@@ -29,6 +31,10 @@ genmanifest() {
        shift
    done

+
    if [ -n "${hide_provided}" ]; then
+
        Xshlibs_provided=""
+
    fi
+

	cat << EOF > ${PKG_NAME}.manifest
name: ${PKG_NAME}
origin: ${PKG_NAME}
@@ -94,15 +100,17 @@ EOF
do_check() {
    local PKG_NAME=$1
    local file1=$(atf_get_srcdir)/$2
+
    local hide_provided=$3

-
    genmanifest ${PKG_NAME} ${file1}
+
    genmanifest ${PKG_NAME} "${hide_provided}" ${file1}

    # cat ${PKG_NAME}.manifest
    atf_check \
        -o empty \
        -e empty \
        -s exit:0 \
-
        pkg -o IGNORE_OSMAJOR=1 -o ABI_FILE=${file1} create -M ./${PKG_NAME}.manifest -r ${TMPDIR}
+
        pkg -o IGNORE_OSMAJOR=1 -o ABI_FILE=${file1} $hide_provided \
+
        create -M ./${PKG_NAME}.manifest -r ${TMPDIR}

    # cat ${PKG_NAME}.expected
    atf_check \
@@ -122,5 +130,6 @@ create_from_bin_body() {
        macosfatlib.bin "macosfatlib.bin#x86_64" "macosfatlib.bin#aarch64"
    do
        do_check testbin $bin
+
        do_check testbin $bin "-o SHLIB_PROVIDE_PATHS_NATIVE=/does/not/exist"
    done
}
modified tests/lib/pkg_elf.c
@@ -48,6 +48,8 @@ ATF_TC_BODY(analyse_elf, tc)
{
	struct pkg *p = NULL;
	char *binpath = NULL;
+
	char *provided = NULL;
+
	enum pkg_shlib_flags provided_flags = PKG_SHLIB_FLAGS_NONE;

	ctx.abi.os = PKG_OS_FREEBSD;
	ctx.abi.arch = PKG_ARCH_AMD64;
@@ -58,27 +60,37 @@ ATF_TC_BODY(analyse_elf, tc)
	ATF_REQUIRE(p != NULL);

	ATF_REQUIRE_EQ(tll_length(p->shlibs_required), 0);
+
	ATF_REQUIRE_EQ(pkg_analyse_elf(false, p, binpath, &provided, &provided_flags), EPKG_OK);
	ATF_REQUIRE_EQ(tll_length(p->shlibs_provided), 0);
-
	ATF_REQUIRE_EQ(pkg_analyse_elf(false, p, binpath), EPKG_OK);
-
	ATF_REQUIRE_EQ(tll_length(p->shlibs_provided), 1);
-
	ATF_REQUIRE_STREQ(tll_front(p->shlibs_provided), "libtestfbsd.so.1");
+
	ATF_REQUIRE_STREQ(provided, "libtestfbsd.so.1");
+
	ATF_REQUIRE_EQ(provided_flags, PKG_SHLIB_FLAGS_NONE);
	ATF_REQUIRE_EQ(tll_length(p->shlibs_required), 1);
	ATF_REQUIRE_STREQ(tll_front(p->shlibs_required), "libc.so.7");
+
	free(provided);
	free(binpath);

+
	provided = NULL;
+
	provided_flags = PKG_SHLIB_FLAGS_NONE;
	xasprintf(&binpath, "%s/Makefile", atf_tc_get_config_var(tc, "srcdir"));
-
	ATF_REQUIRE_EQ(pkg_analyse_elf(false, p, binpath), EPKG_END);
-
	ATF_REQUIRE_EQ(tll_length(p->shlibs_provided), 1);
+
	ATF_REQUIRE_EQ(pkg_analyse_elf(false, p, binpath, &provided, &provided_flags), EPKG_END);
+
	ATF_REQUIRE_EQ(tll_length(p->shlibs_provided), 0);
+
	ATF_REQUIRE_EQ(provided, NULL);
+
	ATF_REQUIRE_EQ(provided_flags, PKG_SHLIB_FLAGS_NONE);
	ATF_REQUIRE_EQ(tll_length(p->shlibs_required), 1);
+
	free(provided);
	free(binpath);

+
	provided = NULL;
+
	provided_flags = PKG_SHLIB_FLAGS_NONE;
	xasprintf(&binpath, "%s/frontend/libtest2fbsd.so.1", atf_tc_get_config_var(tc, "srcdir"));
-
	ATF_REQUIRE_EQ(pkg_analyse_elf(false, p, binpath), EPKG_OK);
-
	ATF_REQUIRE_EQ(tll_length(p->shlibs_provided), 2);
-
	ATF_REQUIRE_STREQ(tll_back(p->shlibs_provided), "libtest2fbsd.so.1");
+
	ATF_REQUIRE_EQ(pkg_analyse_elf(false, p, binpath, &provided, &provided_flags), EPKG_OK);
+
	ATF_REQUIRE_EQ(tll_length(p->shlibs_provided), 0);
+
	ATF_REQUIRE_STREQ(provided, "libtest2fbsd.so.1");
+
	ATF_REQUIRE_EQ(provided_flags, PKG_SHLIB_FLAGS_NONE);
	ATF_REQUIRE_EQ(tll_length(p->shlibs_required), 2);
	ATF_REQUIRE_STREQ(tll_front(p->shlibs_required), "libc.so.7");
	ATF_REQUIRE_STREQ(tll_back(p->shlibs_required), "libfoo.so.1");
+
	free(provided);
	free(binpath);

	pkg_free(p);
modified tests/lib/utils.c
@@ -36,6 +36,7 @@ ATF_TC_WITHOUT_HEAD(json_escape);
ATF_TC_WITHOUT_HEAD(open_tempdir);
ATF_TC_WITHOUT_HEAD(get_http_auth);
ATF_TC_WITHOUT_HEAD(str_ends_with);
+
ATF_TC_WITHOUT_HEAD(match_paths);

ATF_TC_BODY(hidden_tempfile, tc) {
	const char *filename = "plop";
@@ -135,6 +136,40 @@ ATF_TC_BODY(str_ends_with, tc) {
	ATF_REQUIRE(str_ends_with("backend", "end"));
}

+
ATF_TC_BODY(match_paths, tc) {
+
	const char *paths[] = {
+
		"////",
+
		"/foo1",
+
		"/foo2/",
+
		"////foo3/bar",
+
		"/foo4//bar",
+
		"/foo5//////bar",
+
		"//foo6//bar/",
+
		"/foo7//////bar/",
+
		"////foo8//bar///",
+
		NULL,
+
	};
+

+
	ucl_object_t *list = ucl_object_typed_new(UCL_ARRAY);
+
	for (size_t i = 0; paths[i] != NULL; i++) {
+
		ucl_array_append(list, ucl_object_fromstring_common(paths[i], 0, 0));
+
	}
+

+
	ATF_REQUIRE(pkg_match_paths_list(list, "/target.so"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "/foo1/whatever"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "/foo2/thing.txt"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "/foo3/bar/baz.so.1.1.1"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "////foo4//bar/thingy"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "/foo5//////bar/whatisit"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "/foo6//bar/afile"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "/foo7//////bar/foooo"));
+
	ATF_REQUIRE(pkg_match_paths_list(list, "/foo8//bar///other"));
+

+
	ATF_REQUIRE(!pkg_match_paths_list(list, "/notinpath/target.so"));
+
	ATF_REQUIRE(!pkg_match_paths_list(list, "//////notinpath////other.so.1"));
+
	ATF_REQUIRE(!pkg_match_paths_list(list, "/a/b/c/d/e/f/g"));
+
}
+

ATF_TP_ADD_TCS(tp)
{
	ATF_TP_ADD_TC(tp, hidden_tempfile);
@@ -143,6 +178,7 @@ ATF_TP_ADD_TCS(tp)
	ATF_TP_ADD_TC(tp, open_tempdir);
	ATF_TP_ADD_TC(tp, get_http_auth);
	ATF_TP_ADD_TC(tp, str_ends_with);
+
	ATF_TP_ADD_TC(tp, match_paths);

	return (atf_no_error());
}