This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git

commit 0b3d36a1a689886af7c6ad9e71058275060ddebb
Author: Abhishek Mishra <[email protected]>
AuthorDate: Wed Aug 19 08:07:17 2026 +0000

    testing: add ostest coverage for chroot
    
    Check jail visibility, host-path isolation, child inheritance, and that
    a pre-opened host fd remains usable after chroot().
    
    Signed-off-by: Abhishek Mishra <[email protected]>
---
 testing/ostest/CMakeLists.txt |   8 ++
 testing/ostest/Makefile       |   8 ++
 testing/ostest/chroot.c       | 304 ++++++++++++++++++++++++++++++++++++++++++
 testing/ostest/ostest.h       |   7 +
 testing/ostest/ostest_main.c  |  14 ++
 5 files changed, 341 insertions(+)

diff --git a/testing/ostest/CMakeLists.txt b/testing/ostest/CMakeLists.txt
index 8eb651679..7b8a034c3 100644
--- a/testing/ostest/CMakeLists.txt
+++ b/testing/ostest/CMakeLists.txt
@@ -68,6 +68,14 @@ if(CONFIG_TESTING_OSTEST)
     list(APPEND SRCS waitpid.c)
   endif()
 
+  if(CONFIG_FS_CHROOT)
+    if(CONFIG_SCHED_WAITPID)
+      if(NOT CONFIG_BUILD_KERNEL)
+        list(APPEND SRCS chroot.c)
+      endif()
+    endif()
+  endif()
+
   if(NOT CONFIG_DISABLE_PTHREAD)
     list(
       APPEND
diff --git a/testing/ostest/Makefile b/testing/ostest/Makefile
index c89b2466f..9ddd8fffb 100644
--- a/testing/ostest/Makefile
+++ b/testing/ostest/Makefile
@@ -83,6 +83,14 @@ ifeq ($(CONFIG_SCHED_WAITPID),y)
 CSRCS += waitpid.c
 endif
 
+ifeq ($(CONFIG_FS_CHROOT),y)
+ifeq ($(CONFIG_SCHED_WAITPID),y)
+ifneq ($(CONFIG_BUILD_KERNEL),y)
+CSRCS += chroot.c
+endif
+endif
+endif
+
 ifneq ($(CONFIG_DISABLE_PTHREAD),y)
 CSRCS += cancel.c cond.c mutex.c timedmutex.c sem.c semtimed.c barrier.c
 CSRCS += timedwait.c pthread_rwlock.c pthread_rwlock_cancel.c schedlock.c
diff --git a/testing/ostest/chroot.c b/testing/ostest/chroot.c
new file mode 100644
index 000000000..b5fc44465
--- /dev/null
+++ b/testing/ostest/chroot.c
@@ -0,0 +1,304 @@
+/****************************************************************************
+ * apps/testing/ostest/chroot.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ostest.h"
+
+#if defined(CONFIG_FS_CHROOT) && defined(CONFIG_SCHED_WAITPID) && \
+    !defined(CONFIG_BUILD_KERNEL)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifdef CONFIG_LIBC_TMPDIR
+#  define CHROOT_TMPDIR CONFIG_LIBC_TMPDIR
+#else
+#  define CHROOT_TMPDIR "/tmp"
+#endif
+
+#define CHROOT_JAIL    CHROOT_TMPDIR "/ostest_jail"
+#define CHROOT_MARK    CHROOT_JAIL "/marker"
+#define CHROOT_SECRET  CHROOT_TMPDIR "/ostest_secret"
+#define CHROOT_PRIO    100
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int chroot_fail(FAR const char *msg)
+{
+  printf("chroot_test: ERROR %s errno=%d\n", msg, errno);
+  ASSERT(false);
+  return EXIT_FAILURE;
+}
+
+static int chroot_grandchild(int argc, FAR char *argv[])
+{
+  struct stat st;
+
+  UNUSED(argc);
+  UNUSED(argv);
+
+  if (stat("/marker", &st) < 0)
+    {
+      return chroot_fail("grandchild stat(/marker) failed");
+    }
+
+  printf("chroot_test: grandchild still sees the jail\n");
+  return EXIT_SUCCESS;
+}
+
+static int chroot_child(int argc, FAR char *argv[])
+{
+  struct stat st;
+  pid_t pid;
+  int status;
+  int fd;
+  int hostfd;
+
+  UNUSED(argc);
+  UNUSED(argv);
+
+  hostfd = open(CHROOT_SECRET, O_RDONLY);
+  if (hostfd < 0)
+    {
+      return chroot_fail("open(host secret) failed");
+    }
+
+  if (chdir(CHROOT_JAIL) < 0)
+    {
+      return chroot_fail("chdir(jail) failed");
+    }
+
+  if (chroot(".") < 0)
+    {
+      return chroot_fail("chroot(.) failed");
+    }
+
+  if (chdir("/") < 0)
+    {
+      return chroot_fail("chdir(/) failed");
+    }
+
+  if (stat("/marker", &st) < 0)
+    {
+      return chroot_fail("stat(/marker) failed");
+    }
+
+  printf("chroot_test: /marker is visible inside the jail\n");
+
+  if (stat("/dev", &st) == 0)
+    {
+      return chroot_fail("stat(/dev) succeeded inside jail");
+    }
+
+  if (errno != ENOENT)
+    {
+      return chroot_fail("stat(/dev) expected ENOENT");
+    }
+
+  fd = open("/../dev", O_RDONLY);
+  if (fd >= 0)
+    {
+      close(fd);
+      errno = 0;
+      return chroot_fail("open(/../dev) escaped the jail");
+    }
+
+#ifdef CONFIG_DEV_NULL
+  fd = open("/../dev/null", O_RDWR);
+  if (fd >= 0)
+    {
+      close(fd);
+      errno = 0;
+      return chroot_fail("open(/../dev/null) escaped the jail");
+    }
+#endif
+
+  printf("chroot_test: host paths are not visible inside the jail\n");
+
+  /* chroot() does not close pre-opened host fds (POSIX). */
+
+  if (fcntl(hostfd, F_GETFD) < 0)
+    {
+      return chroot_fail("host fd closed by chroot()");
+    }
+
+  printf("chroot_test: pre-opened host fd still usable after chroot\n");
+  close(hostfd);
+
+  pid = task_create("chroot_gc", CHROOT_PRIO, STACKSIZE,
+                    chroot_grandchild, NULL);
+  if (pid < 0)
+    {
+      return chroot_fail("task_create(grandchild) failed");
+    }
+
+  if (waitpid(pid, &status, 0) != pid)
+    {
+      return chroot_fail("waitpid(grandchild) failed");
+    }
+
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
+    {
+      printf("chroot_test: ERROR grandchild status=%d\n", status);
+      ASSERT(false);
+      return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}
+
+static int chroot_prepare(void)
+{
+  int fd;
+  ssize_t nwritten;
+  static const char marker[] = "ostest-chroot\n";
+
+  mkdir(CHROOT_TMPDIR, 0777);
+  if (mkdir(CHROOT_JAIL, 0777) < 0 && errno != EEXIST)
+    {
+      printf("chroot_test: ERROR mkdir(%s) failed errno=%d\n",
+             CHROOT_JAIL, errno);
+      ASSERT(false);
+      return ERROR;
+    }
+
+  fd = open(CHROOT_MARK, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+  if (fd < 0)
+    {
+      printf("chroot_test: ERROR open(%s) failed errno=%d\n",
+             CHROOT_MARK, errno);
+      ASSERT(false);
+      return ERROR;
+    }
+
+  nwritten = write(fd, marker, sizeof(marker) - 1);
+  close(fd);
+  if (nwritten != (ssize_t)(sizeof(marker) - 1))
+    {
+      printf("chroot_test: ERROR write(marker) failed errno=%d\n", errno);
+      ASSERT(false);
+      return ERROR;
+    }
+
+  fd = open(CHROOT_SECRET, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+  if (fd < 0)
+    {
+      printf("chroot_test: ERROR open(%s) failed errno=%d\n",
+             CHROOT_SECRET, errno);
+      ASSERT(false);
+      return ERROR;
+    }
+
+  close(fd);
+  return OK;
+}
+
+static void chroot_cleanup(void)
+{
+  unlink(CHROOT_MARK);
+  unlink(CHROOT_SECRET);
+  rmdir(CHROOT_JAIL);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int chroot_test(void)
+{
+  pid_t pid;
+  int status;
+#ifdef CONFIG_DEV_NULL
+  int fd;
+#endif
+
+  printf("chroot_test: Starting test\n");
+
+  if (chroot_prepare() < 0)
+    {
+      return ERROR;
+    }
+
+  pid = task_create("chroot_child", CHROOT_PRIO, STACKSIZE,
+                    chroot_child, NULL);
+  if (pid < 0)
+    {
+      printf("chroot_test: ERROR task_create failed errno=%d\n", errno);
+      ASSERT(false);
+      chroot_cleanup();
+      return ERROR;
+    }
+
+  if (waitpid(pid, &status, 0) != pid)
+    {
+      printf("chroot_test: ERROR waitpid failed errno=%d\n", errno);
+      ASSERT(false);
+      chroot_cleanup();
+      return ERROR;
+    }
+
+  chroot_cleanup();
+
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
+    {
+      printf("chroot_test: ERROR child status=%d\n", status);
+      ASSERT(false);
+      return ERROR;
+    }
+
+#ifdef CONFIG_DEV_NULL
+  fd = open("/dev/null", O_RDWR);
+  if (fd < 0)
+    {
+      printf("chroot_test: ERROR parent lost /dev/null errno=%d\n",
+             errno);
+      ASSERT(false);
+      return ERROR;
+    }
+
+  close(fd);
+#endif
+
+  printf("chroot_test: PASSED\n");
+  return OK;
+}
+
+#endif /* CONFIG_FS_CHROOT && CONFIG_SCHED_WAITPID && !CONFIG_BUILD_KERNEL */
diff --git a/testing/ostest/ostest.h b/testing/ostest/ostest.h
index 8fa6d64d0..e9b509811 100644
--- a/testing/ostest/ostest.h
+++ b/testing/ostest/ostest.h
@@ -137,6 +137,13 @@ void restart_test(void);
 int waitpid_test(void);
 #endif
 
+/* chroot.c *****************************************************************/
+
+#if defined(CONFIG_FS_CHROOT) && defined(CONFIG_SCHED_WAITPID) && \
+    !defined(CONFIG_BUILD_KERNEL)
+int chroot_test(void);
+#endif
+
 /* wqueue.c *****************************************************************/
 
 #if defined(CONFIG_SCHED_LPWORK) || defined(CONFIG_SCHED_HPWORK)
diff --git a/testing/ostest/ostest_main.c b/testing/ostest/ostest_main.c
index 14ed98de3..523b6aa4a 100644
--- a/testing/ostest/ostest_main.c
+++ b/testing/ostest/ostest_main.c
@@ -403,6 +403,20 @@ static int user_main(int argc, char *argv[])
       check_test_memory_usage();
 #endif
 
+#if defined(CONFIG_FS_CHROOT) && defined(CONFIG_SCHED_WAITPID) && \
+    !defined(CONFIG_BUILD_KERNEL)
+      /* Check chroot() filesystem jail */
+
+      printf("\nuser_main: chroot test\n");
+      if (chroot_test() != 0)
+        {
+          printf("user_main: ERROR chroot test failed\n");
+          ASSERT(false);
+        }
+
+      check_test_memory_usage();
+#endif
+
 #if defined(CONFIG_TESTING_OSTEST_MULTIUSER) && 
defined(CONFIG_SCHED_USER_IDENTITY)
       /* Multi-user identity and file permission regression tests */
 

Reply via email to