From patchwork Mon Apr 27 17:56:37 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Hatle X-Patchwork-Id: 87008 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3EA44FF8860 for ; Mon, 27 Apr 2026 17:56:58 +0000 (UTC) Received: from gate.crashing.org (gate.crashing.org [63.228.1.57]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.2122.1777312615422348099 for ; Mon, 27 Apr 2026 10:56:55 -0700 Authentication-Results: mx.groups.io; dkim=none (message not signed); spf=pass (domain: kernel.crashing.org, ip: 63.228.1.57, mailfrom: mark.hatle@kernel.crashing.org) Received: from kernel.crashing.org.net (70-99-78-136.nuveramail.net [70.99.78.136] (may be forged)) by gate.crashing.org (8.18.1/8.18.1/Debian-2) with ESMTP id 63RHugAk876837; Mon, 27 Apr 2026 12:56:45 -0500 From: Mark Hatle To: yocto-patches@lists.yoctoproject.org, richard.purdie@linuxfoundation.org Cc: dburgener@linux.microsoft.com, peter.kjellerstedt@axis.com Subject: [pseudo][PATCH 07/11] test: Add test cases for canonicalize functions Date: Mon, 27 Apr 2026 12:56:37 -0500 Message-Id: <1777312601-1393-8-git-send-email-mark.hatle@kernel.crashing.org> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1777312601-1393-1-git-send-email-mark.hatle@kernel.crashing.org> References: <1777312601-1393-1-git-send-email-mark.hatle@kernel.crashing.org> List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Mon, 27 Apr 2026 17:56:58 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/yocto-patches/message/3859 From: Mark Hatle Verify canonicalize, which calls realpath, both in a normal and emulated chroot environment. AI-Generated: test cases generated by github copilot (claude opus 6.4) Signed-off-by: Mark Hatle Signed-off-by: Mark Hatle --- test/test-canonicalize.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++ test/test-canonicalize.sh | 11 +++ 2 files changed, 182 insertions(+) create mode 100644 test/test-canonicalize.c create mode 100755 test/test-canonicalize.sh diff --git a/test/test-canonicalize.c b/test/test-canonicalize.c new file mode 100644 index 0000000..db20f25 --- /dev/null +++ b/test/test-canonicalize.c @@ -0,0 +1,171 @@ +/* + * Test canonicalize_file_name + * SPDX-License-Identifier: LGPL-2.1-only + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +static int failures = 0; + +static void check(const char *desc, int condition) { + if (!condition) { + fprintf(stderr, "FAIL: %s\n", desc); + failures++; + } +} + +static int test_basic(void) { + char *result; + char cwd[PATH_MAX]; + int fd; + + if (!getcwd(cwd, sizeof(cwd))) { + perror("getcwd"); + return 1; + } + + /* Create a test file and symlink */ + fd = open("test_canon_file", O_CREAT | O_WRONLY, 0644); + close(fd); + if (symlink("test_canon_file", "test_canon_link") != 0) { + perror("symlink"); + return 1; + } + + /* canonicalize_file_name on regular file */ + result = canonicalize_file_name("test_canon_file"); + check("canon file", result != NULL); + if (result) { + check("canon file starts with /", result[0] == '/'); + check("canon file contains name", strstr(result, "test_canon_file") != NULL); + free(result); + } + + /* canonicalize_file_name on symlink (should resolve) */ + result = canonicalize_file_name("test_canon_link"); + check("canon link", result != NULL); + if (result) { + check("canon link resolves", strstr(result, "test_canon_file") != NULL); + /* Should NOT contain the link name */ + check("canon link not link", strstr(result, "test_canon_link") == NULL); + free(result); + } + + /* canonicalize_file_name on . */ + result = canonicalize_file_name("."); + check("canon dot", result != NULL); + if (result) { + check("canon dot matches cwd", strcmp(result, cwd) == 0); + free(result); + } + + /* canonicalize_file_name on nonexistent should return NULL */ + result = canonicalize_file_name("test_canon_noexist"); + check("canon noexist NULL", result == NULL); + + unlink("test_canon_link"); + unlink("test_canon_file"); + + return 0; +} + +static int test_chroot(const char *chroot_dir) { + char *result; + char path[PATH_MAX]; + int fd; + + /* Create test file and symlink inside the chroot directory */ + snprintf(path, sizeof(path), "%s/cr_canon_file", chroot_dir); + fd = open(path, O_CREAT | O_WRONLY, 0644); + if (fd < 0) { perror("open chroot file"); return 1; } + close(fd); + + snprintf(path, sizeof(path), "%s/cr_canon_link", chroot_dir); + if (symlink("cr_canon_file", path) != 0) { + perror("symlink in chroot dir"); + return 1; + } + + /* Also create an absolute-target symlink */ + snprintf(path, sizeof(path), "%s/cr_abs_link", chroot_dir); + if (symlink("/cr_canon_file", path) != 0) { + perror("abs symlink in chroot dir"); + return 1; + } + + if (chroot(chroot_dir) != 0) { + perror("chroot"); + return 1; + } + if (chdir("/") != 0) { + perror("chdir /"); + return 1; + } + + /* canonicalize on a file: result must be /cr_canon_file, not + * /real/path/to/chroot/cr_canon_file */ + result = canonicalize_file_name("/cr_canon_file"); + check("chroot: canon abs file", result != NULL); + if (result) { + check("chroot: canon abs file path", strcmp(result, "/cr_canon_file") == 0); + free(result); + } + + /* canonicalize on a relative symlink */ + result = canonicalize_file_name("/cr_canon_link"); + check("chroot: canon rel symlink", result != NULL); + if (result) { + check("chroot: canon rel symlink resolves", strcmp(result, "/cr_canon_file") == 0); + free(result); + } + + /* canonicalize on an absolute-target symlink */ + result = canonicalize_file_name("/cr_abs_link"); + check("chroot: canon abs symlink", result != NULL); + if (result) { + check("chroot: canon abs symlink resolves", strcmp(result, "/cr_canon_file") == 0); + free(result); + } + + /* canonicalize on . should return / */ + result = canonicalize_file_name("."); + check("chroot: canon dot", result != NULL); + if (result) { + check("chroot: canon dot is /", strcmp(result, "/") == 0); + free(result); + } + + /* path traversal: /../cr_canon_file must stay confined */ + result = canonicalize_file_name("/../cr_canon_file"); + check("chroot: canon traverse", result != NULL); + if (result) { + check("chroot: canon traverse resolves", strcmp(result, "/cr_canon_file") == 0); + free(result); + } + + return 0; +} + +int main(int argc, char *argv[]) { + int rc; + + rc = test_basic(); + if (rc) + return rc; + + if (argc > 1) { + rc = test_chroot(argv[1]); + if (rc) + return rc; + } + + return failures; +} diff --git a/test/test-canonicalize.sh b/test/test-canonicalize.sh new file mode 100755 index 0000000..4a1c321 --- /dev/null +++ b/test/test-canonicalize.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# SPDX-License-Identifier: LGPL-2.1-only +# +# Test canonicalize_file_name (basic + chroot) + +CHROOT_DIR=$(mktemp -d "${PWD}/chroot_canon_XXXXXX") +trap "rm -rf '$CHROOT_DIR'" 0 + +rm -f test_canon_file test_canon_link +./test/test-canonicalize "$CHROOT_DIR"