Skip to content

linux: treats empty path to safe_openat as root #1753

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 27, 2025

Conversation

giuseppe
Copy link
Member

@giuseppe giuseppe commented May 20, 2025

When an empty string is provided as the path argument to safe_openat, it is now explicitly treated as "/".

Closes: #1752

Summary by Sourcery

Treat empty paths as root in safe_openat and streamline rootfsfd handling across mounting and device creation code, ensure proper cleanup of the rootfs file descriptor, add helper for validating fd targets, and expand tests for root-level bind and tmpfs mounts and rdt value length checks.

Bug Fixes:

  • Treat empty path passed to safe_openat as referring to the root directory of the container.
  • Close and reset the stored rootfs file descriptor properly after mounting operations.

Enhancements:

  • Consolidate use of rootfsfd by fetching it directly from private_data in various mount and device-creation routines.
  • Remove redundant local rootfsfd parameters and simplify do_mounts and libcrun_set_mounts signatures.

Tests:

  • Add tests for bind and tmpfs mounts targeting root (‘/’).
  • Add a check in Intel RDT tests to verify that get_rdt_value’s return length matches the output string.

Chores:

  • Introduce check_fd_is_path helper to verify that an open file descriptor points to a given directory.

Copy link

sourcery-ai bot commented May 20, 2025

Reviewer's Guide

This PR adds explicit handling for empty path arguments in safe_openat (treating them as root), refactors the create-and-open fallback logic for consistency, streamlines rootfsfd management across mounting routines, and includes new tests for mounting to root (bind and tmpfs) and for RDT value length validation.

Sequence Diagram for Root Filesystem Descriptor Update on Root Mount

sequenceDiagram
    participant DM as do_mount
    participant PD as libcrun_private_data
    participant OS as OperatingSystem

    Note over DM: Called within do_mounts when processing a mount entry
    DM->>DM: Mount target is empty string (e.g., destination: "/")
    DM->>OS: dup(fd_of_new_root_mount_source)
    OS-->>DM: tmp_fd (new rootfs fd)
    DM->>OS: close(PD.rootfsfd) (old rootfs fd)
    DM->>PD: Update rootfsfd = tmp_fd
Loading

File-Level Changes

Change Details Files
Handle empty path as root in safe_openat and crun_safe_ensure_at
  • Added early branch in safe_openat to open rootfs when path is empty
  • Introduced check_fd_is_path to verify the opened fd points to root
  • Extended crun_safe_ensure_at to return an O_PATH fd for empty paths
src/libcrun/utils.c
Refactor create-and-open fallback to use a single return variable
  • Replaced separate fd variable with a single ret variable in crun_safe_create_and_open_ref_at
  • Unified the initial safe_openat call and fallback path for file/dir creation
src/libcrun/utils.c
Streamline rootfsfd usage and lifecycle in linux.c
  • Removed local rootfsfd variables and inlined get_private_data(container)->rootfsfd everywhere
  • Updated do_mounts signature to drop rootfsfd parameter
  • Added closing/reset of rootfsfd in cleanup and after mounts are configured
src/libcrun/linux.c
Add tests for mounting to rootfs and RDT value length check
  • Introduced test_mount_bind_to_rootfs and test_mount_tmpfs_to_rootfs in test_mounts.py
  • Added strlen check for RDT get_rdt_value in tests_libcrun_intelrdt.c
tests/test_mounts.py
tests/tests_libcrun_intelrdt.c

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @giuseppe - I've reviewed your changes - here's some feedback:

  • In test_mounts.py, test_mount_to_root defines mount_opt but never applies it to the config or asserts any outcome—please add the mount to conf['mounts'] (or equivalent) and verify the expected behavior.
  • In crun_safe_create_and_open_ref_at, the final safe_openat call’s return value and error context aren’t checked before returning—capture and propagate its error result so failures aren’t swallowed.
Here's what I looked at during the review
  • 🟢 General issues: all looks good
  • 🟢 Security: all looks good
  • 🟡 Testing: 1 issue found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

conf = base_config()
conf['process']['args'] = ['/init', 'true']
add_all_namespaces(conf)
mount_opt = {"destination": "/", "type": "tmpfs", "source": "tmpfs", "options": ["tmpcopyup"]}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (testing): The mount_opt variable is defined but not added to the configuration passed to run_and_get_output.

Add conf['mounts'] = [mount_opt] before calling run_and_get_output so the test actually mounts to /.

@giuseppe giuseppe marked this pull request as draft May 20, 2025 14:17
Copy link

TMT tests failed. @containers/packit-build please check.

giuseppe added 3 commits May 20, 2025 16:54
Signed-off-by: Giuseppe Scrivano <[email protected]>
Signed-off-by: Giuseppe Scrivano <[email protected]>
@giuseppe giuseppe force-pushed the fix-opening-root branch 2 times, most recently from b04d295 to 6a4ee79 Compare May 20, 2025 18:53
giuseppe added 5 commits May 20, 2025 21:42
if `do_open` is used with an empty path, the reopen the `dirpath`.

Signed-off-by: Giuseppe Scrivano <[email protected]>
This change ensures that the file descriptor for the
rootfs is always sourced directly from the container's
private data.  This avoids potential stale file descriptor
issues that could happen if a local variable were used and
the descriptor in the private data was updated elsewhere.

Should not introduce any behavior change.

Signed-off-by: Giuseppe Scrivano <[email protected]>
If an empty path is used, reopens directly the rootfs so that it can
grab a reference to the topmost mount, not the previously open file
descriptor.

Signed-off-by: Giuseppe Scrivano <[email protected]>
When a mount operation replaces the container's root filesystem ("/"),
the existing `rootfsfd` becomes stale.  This file descriptor would
still point to the old, now overmounted root, potentially causing
subsequent filesystem operations within the container setup to fail
or target the incorrect filesystem.

Closes: containers#1752

Signed-off-by: Giuseppe Scrivano <[email protected]>
@giuseppe giuseppe force-pushed the fix-opening-root branch from 6a4ee79 to 64a2e0e Compare May 20, 2025 19:44
@giuseppe giuseppe marked this pull request as ready for review May 21, 2025 14:56
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @giuseppe - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟡 Testing: 2 issues found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

return crun_make_error (err, errno, "open `%s`", rootfs);

get_private_data (container)->rootfsfd = ret;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Potential file descriptor leak on early return

Open the fd into a temporary variable (e.g. via cleanup_close) and assign it to private_data only after all operations succeed, or close/reset it on every error path.

out, _ = run_and_get_output(conf, hide_stderr=True, callback_prepare_rootfs=prepare_rootfs)
if "712" in out:
return 0
return -1

def test_mount_bind_to_rootfs():
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (testing): Add assertions to verify the bind mount to root.

Create a unique file in tmpdir before invoking the container, then inside the container (e.g. via /init or its args) assert that this file appears at / to confirm the bind mount.

_, _ = run_and_get_output(conf, hide_stderr=True)
return 0

def test_mount_tmpfs_to_rootfs():
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (testing): Add assertions to verify the tmpfs mount to root and tmpcopyup.

Assert that /init is present and executable inside the container to confirm tmpcopyup worked after mounting root as tmpfs.

@giuseppe
Copy link
Member Author

@kolyshkin PTAL

@giuseppe
Copy link
Member Author

@flouthoc PTAL

Copy link
Collaborator

@flouthoc flouthoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@flouthoc flouthoc merged commit b1a71e7 into containers:main May 27, 2025
60 of 61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

allow bind mount on root directory
2 participants