Skip to content

ROX-34920: track symlink events - #1440

Open
Molter73 wants to merge 13 commits into
mainfrom
mauro/ROX-34920/track-symlinks
Open

ROX-34920: track symlink events#1440
Molter73 wants to merge 13 commits into
mainfrom
mauro/ROX-34920/track-symlinks

Conversation

@Molter73

@Molter73 Molter73 commented Aug 11, 2026

Copy link
Copy Markdown
Member

Description

Add a LSM hook for path_symlink, allowing us to handle events that create and modify symlinks in monitored directories. We also change the host scanner methods to use symlink_metadata in order to properly capture these types of files. This hook does not have the required inode for the symlink, so generating the event is delegated to d_instantiate instead.

While working on delegating to d_instatiate some refactoring was done:

  • Add path_read helpers allowing for reading bound_path_t into arbitrary buffers.
  • Rewrote most path_read methods to use these new helpers.
  • Add helpers for getting and deleting entries from the d_instantiate_ctx map.
  • Cleanup the d_instantiateandpath_mkdir` hooks with the new helper functions.
  • Remove should_track_mkdir and replaced it with a call to is_monitored passing NULL as the first argument.

Checklist

  • Patch has a change log entry OR does not need one.
  • Investigated and inspected CI test results
  • Updated documentation accordingly

Automated testing

  • Added unit tests
  • Added integration tests
  • Added regression tests

If any of these don't apply, please comment below.

Testing Performed

Added integration tests for multiple cases.

Summary by CodeRabbit

  • New Features

    • Added end-to-end monitoring for symbolic link creation and updates.
    • Symlink events include link targets, source paths, inode details, and monitoring status.
    • Host scanning follows symlinks and refreshes file mappings when links change.
    • Added symlink activity metrics and telemetry support.
  • Bug Fixes

    • Improved handling of relative, absolute, Unicode, ignored, mounted, and container-based symlink paths.
    • Scanning now logs and skips individual metadata or pattern errors instead of stopping entirely.
  • Tests

    • Added comprehensive coverage for symlink creation, overwriting, target resolution, and path edge cases.

@Molter73
Molter73 requested a review from a team as a code owner August 11, 2026 15:04
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds end-to-end symlink event support. eBPF hooks capture symlink creation and targets. Rust event types and serialization expose symlink data. Host scanning resolves targets and handles symlink events. Integration tests cover creation, overwrites, ignored paths, and filesystem variants.

Changes

Symlink event support

Layer / File(s) Summary
Kernel symlink capture
fact-ebpf/src/bpf/*, fact-ebpf/src/lib.rs
The eBPF layer stores dentry-operation context, captures symlink paths and targets, submits symlink events, and records path_symlink metrics.
Symlink event representation
fact/src/event/mod.rs, tests/event.py, tests/server.py, fact/src/metrics/kernel_metrics.rs
Event models represent symlinks, sanitize targets, expose symlink properties, and serialize them through existing open-file output.
Symlink host scanning
fact/src/host_scanner.rs, fact/src/metrics/host_scanner.rs
Host scanning uses symlink metadata, resolves targets, performs full scans for symlink events, and filters ignored paths.
Symlink integration validation
tests/test_path_symlink.py, CHANGELOG.md
Tests cover creation, target types, overwrites, ignored paths, varied filenames, containers, and mounted directories. The changelog records symlink tracking.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Filesystem
  participant path_symlink
  participant trace_d_instantiate
  participant EventModel
  participant HostScanner
  Filesystem->>path_symlink: create symlink
  path_symlink->>trace_d_instantiate: capture path and target context
  trace_d_instantiate->>EventModel: submit symlink event
  EventModel->>HostScanner: deliver parsed symlink event
  HostScanner->>Filesystem: scan symlink target
Loading

Possibly related PRs

Suggested reviewers: stringy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the main change: tracking symlink events.
Description check ✅ Passed The description explains the implementation, lists relevant checklist items, documents testing, and matches the pull request objectives.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mauro/ROX-34920/track-symlinks

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 86 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.18%. Comparing base (ede21cc) to head (40459ba).

Files with missing lines Patch % Lines
fact/src/host_scanner.rs 0.00% 71 Missing ⚠️
fact/src/event/mod.rs 0.00% 15 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1440      +/-   ##
==========================================
- Coverage   34.78%   34.18%   -0.61%     
==========================================
  Files          22       22              
  Lines        3300     3358      +58     
  Branches     3300     3358      +58     
==========================================
  Hits         1148     1148              
- Misses       2147     2205      +58     
  Partials        5        5              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (5)
tests/test_path_symlink.py (2)

475-479: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

ignored_dir is unused in test_mounted_dir.

The test requests the ignored_dir fixture but never references it. If the fixture is required to set up the mounted directory, add a comment. Otherwise remove the parameter.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_path_symlink.py` around lines 475 - 479, Update test_mounted_dir
to remove the unused ignored_dir fixture parameter unless it is required for
setup; if it must remain, add a comment explaining how the fixture establishes
the mounted directory.

485-486: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the exit codes of the container commands.

test_ovfs checks res.exit_code == 0 for both commands. test_mounted_dir ignores the results. If touch or ln fails inside the container, the test fails later with a timeout in wait_events instead of a clear error.

💚 Proposed fix
-    test_container.exec_run(f'touch {file}')
-    test_container.exec_run(f'ln -s {file} {link}')
+    res = test_container.exec_run(f'touch {file}')
+    assert res.exit_code == 0
+    res = test_container.exec_run(f'ln -s {file} {link}')
+    assert res.exit_code == 0
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_path_symlink.py` around lines 485 - 486, Update test_mounted_dir’s
container setup around the touch and ln -s exec_run calls to capture each
command result and assert exit_code == 0, matching test_ovfs. Keep the existing
command order and make failures surface immediately before wait_events runs.
fact-ebpf/src/bpf/bound_path.h (1)

72-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the duplicated failure log.

path_read_into_append_d_entry logs "Failed to read path". The callers in fact-ebpf/src/bpf/main.c at lines 309-311 and 623-625 log the same message for the same failure. Each failure then produces two identical trace lines. Keep the log in one place.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact-ebpf/src/bpf/bound_path.h` around lines 72 - 75, Remove the bpf_printk
failure log from the path_read_into call in path_read_into_append_d_entry, while
retaining its NULL return behavior; keep the existing caller-side logs in main.c
as the single failure log location.
fact-ebpf/src/bpf/maps.h (1)

112-134: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

get_or_insert_d_instantiate_ctx reuses stale field values.

If an entry already exists, the function resets only event_type. The fields path, parent_inode, monitored, and symlink_target keep the values of the previous operation for the same thread. trace_path_symlink never assigns monitored, so the entry carries a stale value into trace_d_instantiate. The symlink branch recomputes monitored with is_monitored, so the current code is correct. A future hook that reads ctx->monitored before assignment would read stale data.

Set the remaining scalar fields to defaults on reuse.

🛡️ Proposed defensive reset
   struct d_instantiate_ctx_t* ctx = bpf_map_lookup_elem(&d_instantiate_ctx, &pid);
   if (ctx != NULL) {
     // Clear the event type so `d_instantiate` doesn't trigger by accident
     ctx->event_type = FILE_ACTIVITY_INIT;
+    ctx->monitored = NOT_MONITORED;
+    ctx->path.len = 0;
+    ctx->symlink_target[0] = '\0';
     return ctx;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact-ebpf/src/bpf/maps.h` around lines 112 - 134, Update
get_or_insert_d_instantiate_ctx so an existing context is fully reset before
reuse, not just event_type. Restore path, parent_inode, monitored, and
symlink_target to their default values while preserving the existing return
behavior and initialization defaults.
fact-ebpf/src/bpf/main.c (1)

391-392: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use delete_d_instantiate_ctx() for the cleanup.

Line 392 calls bpf_map_delete_elem directly with the locally captured pid_tgid. The new helper delete_d_instantiate_ctx() performs the same operation. Use the helper so all access to d_instantiate_ctx goes through one place. The local pid_tgid at line 343 then becomes unnecessary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact-ebpf/src/bpf/main.c` around lines 391 - 392, Update the cleanup path to
call delete_d_instantiate_ctx() instead of directly invoking bpf_map_delete_elem
on d_instantiate_ctx. Remove the now-unused local pid_tgid declaration and
retain the existing cleanup behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fact-ebpf/src/bpf/bound_path.h`:
- Around line 57-63: Update the d_path handling in the bound path construction
flow to capture its signed result in a signed local variable before assigning
bound_path->len. Check that local result for len <= 0, return NULL on failure,
then assign the validated value to bound_path->len before applying
PATH_LEN_CLAMP.

In `@fact-ebpf/src/bpf/events.h`:
- Around line 249-258: Update submit_symlink_event to check the return value of
bpf_probe_read_str; on failure, either initialize args->event->from.filename[0]
to '\0' before __submit_event or release/discard the reserved event instead of
submitting uninitialized symlink-target data.

In `@fact-ebpf/src/bpf/main.c`:
- Line 628: Update the parent inode assignment in the relevant hook to obtain
the inode through a CO-RE read of the dentry chain, matching the existing
trace_path_mkdir pattern with BPF_CORE_READ(dir, dentry, d_inode), then pass
that result to inode_to_key.

In `@fact-ebpf/src/bpf/maps.h`:
- Around line 86-100: Reduce the `d_instantiate_ctx` map’s `max_entries` from
16384 to a value bounded by the maximum number of concurrent pid_tgid contexts,
or redesign `d_instantiate_ctx_t` storage to avoid embedding both path buffers
per entry. Preserve context correlation while ensuring the preallocated LRU hash
value memory fits the intended resource budget.

In `@tests/test_path_symlink.py`:
- Line 89: Update the pytest.param case with id='Invalid' in the symlink path
tests so the symlink argument is a bytes literal containing the undecodable
filename bytes, matching the b'test\xff\xfe.txt' input and exercising the
intended invalid filename behavior.

---

Nitpick comments:
In `@fact-ebpf/src/bpf/bound_path.h`:
- Around line 72-75: Remove the bpf_printk failure log from the path_read_into
call in path_read_into_append_d_entry, while retaining its NULL return behavior;
keep the existing caller-side logs in main.c as the single failure log location.

In `@fact-ebpf/src/bpf/main.c`:
- Around line 391-392: Update the cleanup path to call
delete_d_instantiate_ctx() instead of directly invoking bpf_map_delete_elem on
d_instantiate_ctx. Remove the now-unused local pid_tgid declaration and retain
the existing cleanup behavior.

In `@fact-ebpf/src/bpf/maps.h`:
- Around line 112-134: Update get_or_insert_d_instantiate_ctx so an existing
context is fully reset before reuse, not just event_type. Restore path,
parent_inode, monitored, and symlink_target to their default values while
preserving the existing return behavior and initialization defaults.

In `@tests/test_path_symlink.py`:
- Around line 475-479: Update test_mounted_dir to remove the unused ignored_dir
fixture parameter unless it is required for setup; if it must remain, add a
comment explaining how the fixture establishes the mounted directory.
- Around line 485-486: Update test_mounted_dir’s container setup around the
touch and ln -s exec_run calls to capture each command result and assert
exit_code == 0, matching test_ovfs. Keep the existing command order and make
failures surface immediately before wait_events runs.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 19c859c2-bd26-4af1-a990-a77bfc286ed2

📥 Commits

Reviewing files that changed from the base of the PR and between 57f7a6b and b88f069.

📒 Files selected for processing (14)
  • fact-ebpf/src/bpf/bound_path.h
  • fact-ebpf/src/bpf/events.h
  • fact-ebpf/src/bpf/file.h
  • fact-ebpf/src/bpf/main.c
  • fact-ebpf/src/bpf/maps.h
  • fact-ebpf/src/bpf/types.h
  • fact-ebpf/src/lib.rs
  • fact/src/event/mod.rs
  • fact/src/host_scanner.rs
  • fact/src/metrics/host_scanner.rs
  • fact/src/metrics/kernel_metrics.rs
  • tests/event.py
  • tests/server.py
  • tests/test_path_symlink.py
💤 Files with no reviewable changes (1)
  • fact-ebpf/src/bpf/file.h

Comment thread fact-ebpf/src/bpf/bound_path.h Outdated
Comment thread fact-ebpf/src/bpf/events.h
Comment thread fact-ebpf/src/bpf/main.c
Comment thread fact-ebpf/src/bpf/maps.h
Comment on lines +86 to +100
// Context for correlating operations in d_instantiate
struct d_instantiate_ctx_t {
struct bound_path_t path;
inode_key_t parent_inode;
monitored_t monitored;
file_activity_type_t event_type;
char symlink_target[PATH_MAX];
};

struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__type(key, __u64);
__type(value, struct mkdir_context_t);
__type(value, struct d_instantiate_ctx_t);
__uint(max_entries, 16384);
} mkdir_context SEC(".maps");
} d_instantiate_ctx SEC(".maps");

@coderabbitai coderabbitai Bot Aug 11, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Compare entry counts and value sizes of the BPF maps in this repo.
fd -e h -e c . fact-ebpf/src/bpf --exec rg -n -C6 'BPF_MAP_TYPE_[A-Z_]+|max_entries' {}

Repository: stackrox/fact

Length of output: 10918


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- maps.h ---'
sed -n '1,155p' fact-ebpf/src/bpf/maps.h
printf '%s\n' '--- relevant type definitions ---'
rg -n -C4 'typedef .*inode_key_t|typedef .*monitored_t|file_activity_type_t|struct bound_path_t|struct d_instantiate_ctx_t' fact-ebpf/src/bpf
printf '%s\n' '--- d_instantiate_ctx accesses ---'
rg -n -C8 'd_instantiate_ctx|get_d_instantiate_ctx|symlink_target' fact-ebpf/src/bpf

Repository: stackrox/fact

Length of output: 39288


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import ctypes

PATH_MAX = 4096

class BoundPath(ctypes.Structure):
    _fields_ = [('len', ctypes.c_uint), ('path', ctypes.c_char * (PATH_MAX * 2))]

# These scalar fields are resolved from the source output in the first probe.
print('BoundPath size:', ctypes.sizeof(BoundPath))
PY

Repository: stackrox/fact

Length of output: 172


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- prior context-map references ---'
rg -n -C5 'mkdir_context|d_instantiate_ctx' .
printf '%s\n' '--- map declarations in project BPF sources ---'
rg -n -C3 '__uint\(type, BPF_MAP_TYPE_(LRU_HASH|HASH)\)|__uint\(max_entries' fact-ebpf/src/bpf -g '*.{h,c}'
printf '%s\n' '--- target and compiler metadata ---'
rg -n -C2 'TARGET_ARCH|__TARGET_ARCH|aarch64|x86_64|clang|bpf' fact-ebpf/Makefile fact-ebpf -g 'Makefile' -g '*.mk' -g '*.sh' -g '*.yml' -g '*.yaml' 2>/dev/null || true

Repository: stackrox/fact

Length of output: 10785


🌐 Web query:

Linux kernel BPF_MAP_TYPE_LRU_HASH preallocation BPF_F_NO_PREALLOC support value_size max_entries memory allocation

💡 Result:

The Linux kernel does not support the BPF_F_NO_PREALLOC flag for BPF_MAP_TYPE_LRU_HASH and its variants [1]. While standard hash maps (BPF_MAP_TYPE_HASH) support BPF_F_NO_PREALLOC to disable pre-allocation [2][3][4], LRU hash maps require pre-allocation of nodes [5][6][1]. Key technical details regarding memory allocation for BPF_MAP_TYPE_LRU_HASH include: Pre-allocation Requirement: All nodes for an LRU hash map are pre-allocated at map creation time and placed into a "free" list [5][6][1]. Because this mechanism is fundamental to the LRU eviction logic, opting out of pre-allocation is not supported [1]. Memory Limits: The size of keys and values is restricted such that their combined size, along with implementation overhead, must not exceed KMALLOC_MAX_SIZE [5][6]. If this limit is exceeded during map creation, the syscall will return an -E2BIG error [5][6]. Allocation Behavior: The map maintains internal lists (typically active, inactive, and free lists) to manage elements [5][6]. In the default configuration, these lists are global, but the BPF_F_NO_COMMON_LRU flag can be used to request per-CPU LRU lists for potentially improved performance in specific multi-CPU scenarios [2][5][1]. The maximum number of entries (max_entries) specified at creation determines the size of the pre-allocated pool [2][5]. In summary, users cannot use BPF_F_NO_PREALLOC with LRU hash maps, and the memory footprint is determined at creation based on max_entries, key_size, and value_size [5][6][1].

Citations:


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import ctypes
from pathlib import Path

PATH_MAX = 4096
max_entries = 16384

class BoundPath(ctypes.Structure):
    _fields_ = [
        ("len", ctypes.c_uint),
        ("path", ctypes.c_char * (PATH_MAX * 2)),
    ]

class InodeKey(ctypes.Structure):
    _fields_ = [
        ("inode", ctypes.c_ulong),
        ("dev", ctypes.c_ulong),
    ]

class Context(ctypes.Structure):
    _fields_ = [
        ("path", BoundPath),
        ("parent_inode", InodeKey),
        ("monitored", ctypes.c_int),
        ("event_type", ctypes.c_int),
        ("symlink_target", ctypes.c_char * PATH_MAX),
    ]

maps = Path("fact-ebpf/src/bpf/maps.h").read_text()
assert "__uint(type, BPF_MAP_TYPE_LRU_HASH)" in maps
assert "__uint(max_entries, 16384)" in maps

print("BoundPath:", ctypes.sizeof(BoundPath))
print("InodeKey:", ctypes.sizeof(InodeKey))
print("Context:", ctypes.sizeof(Context))
print("Context alignment:", ctypes.alignment(Context))
print("Key:", ctypes.sizeof(ctypes.c_uint64))
print("Value payload:", ctypes.sizeof(Context) * max_entries)
print("Value payload MiB:", ctypes.sizeof(Context) * max_entries / (1024 * 1024))
print("Value payload at 1024 entries MiB:", ctypes.sizeof(Context) * 1024 / (1024 * 1024))
for field, _ in Context._fields_:
    print(f"offset {field}: {getattr(Context, field).offset}")
PY

Repository: stackrox/fact

Length of output: 433


Reduce d_instantiate_ctx’s preallocated capacity

struct d_instantiate_ctx_t is 12,320 bytes on the 64-bit BPF layout. BPF_MAP_TYPE_LRU_HASH preallocates all entries, so max_entries = 16384 requires at least 192.5 MiB for values, before key, bucket, and LRU-node overhead. Set max_entries from the maximum number of concurrent pid_tgid contexts, or redesign the storage to avoid embedding both path buffers in every entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fact-ebpf/src/bpf/maps.h` around lines 86 - 100, Reduce the
`d_instantiate_ctx` map’s `max_entries` from 16384 to a value bounded by the
maximum number of concurrent pid_tgid contexts, or redesign
`d_instantiate_ctx_t` storage to avoid embedding both path buffers per entry.
Preserve context correlation while ensuring the preallocated LRU hash value
memory fits the intended resource budget.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I can't think of a way we could redesign this buffer ATM, I'll look into reducing the number of entries by default and make it configurable in case we need to tweak it in production, but will do so in a follow up PR.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Molter73, understood. The current finding remains open because d_instantiate_ctx still preallocates the large value capacity.

A follow-up that reduces the default capacity and adds production configuration is a reasonable approach. Would you like me to create a GitHub issue for that follow-up?

You are interacting with an AI system.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@CodeRabbit follow up PR is already up: #1453

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="stackrox/fact"

echo "--- pull request metadata ---"
gh pr view 1453 --repo "$repo" --json number,title,state,url,body,headRefName,baseRefName

echo "--- changed files and patch for maps.h ---"
gh pr diff 1453 --repo "$repo" --name-only
gh pr diff 1453 --repo "$repo" -- fact-ebpf/src/bpf/maps.h 2>/dev/null || true

Length of output: 1829


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="stackrox/fact"
patch="$(mktemp)"
trap 'rm -f "$patch"' EXIT

gh pr diff 1453 --repo "$repo" > "$patch"

echo "--- fact-ebpf/src/bpf/maps.h patch ---"
sed -n '/^diff --git a\/fact-ebpf\/src\/bpf\/maps\.h /,/^diff --git /p' "$patch"

echo "--- runtime configuration patches ---"
sed -n '/^diff --git a\/fact\/src\/bpf\/mod\.rs /,/^diff --git /p' "$patch"
sed -n '/^diff --git a\/fact\/src\/config\/mod\.rs /,/^diff --git /p' "$patch"
sed -n '/^diff --git a\/fact\/src\/config\/tests\.rs /,/^diff --git /p' "$patch"

Length of output: 11785


@Molter73, verified. #1453 reduces the default d_instantiate_ctx capacity from 16,384 to 512 entries. It also adds runtime configuration through YAML, FACT_D_INSTANTIATE_CTX_SIZE, and --d-inst-size.

#1453 directly addresses this finding. The finding remains open on #1440 because the change is in the follow-up pull request.

You are interacting with an AI system.

Comment thread tests/test_path_symlink.py Outdated
Molter73 added a commit that referenced this pull request Aug 12, 2026
With the changes introduced by #1440, the d_instantiate_ctx map has
grown considerably in size. This patch reduces the default size and
exposes a method for setting the size at runtime for fine-tweaking.
@Molter73
Molter73 marked this pull request as draft August 12, 2026 09:26
@Molter73

Copy link
Copy Markdown
Member Author

Looks like the symlink tests are very flaky, will set the PR back as open once I've managed to figure out why and make them more reliable.

Add a LSM hook for path_symlink, allowing us to handle events that
create and modify symlinks in monitored directories. We also change the
host scanner methods to use `symlink_metadata` in order to properly
capture these types of files.
Since path_symlink does not have an inode available for the symlink
being created yet, we delegate obtaining that information to the
d_instantiate hook as we do with path_mkdir. This means we will reliably
get an inode in kernel space and we won't have to depend on userspace
being fast enough to retrieve this information when the symlink is short
lived.

While working on this I also refactored some of the code in path_mkdir
and d_instantiate to make them a bit simpler.
When reaching a symlink, whatever it points to will be added to the list
of inodes to be tracked.
* Properly count the number of events for path_symlink as soon as
  possible.
* Remove unused method `set_inode
* Keep scanning when failing to retrieve metadata for a path.
* Properly check length returned by d_path.
* Check return value of bpf_probe_read_str call.
* Change string to bytes blob in test_path_symlink.py.
@Molter73
Molter73 force-pushed the mauro/ROX-34920/track-symlinks branch from 3a0a7ff to bb01edc Compare August 12, 2026 09:54
Molter73 added a commit that referenced this pull request Aug 12, 2026
With the changes introduced by #1440, the d_instantiate_ctx map has
grown considerably in size. This patch reduces the default size and
exposes a method for setting the size at runtime for fine-tweaking.
@Molter73
Molter73 force-pushed the mauro/ROX-34920/track-symlinks branch 2 times, most recently from d8e986c to bcd36b2 Compare August 12, 2026 13:20
@Molter73
Molter73 force-pushed the mauro/ROX-34920/track-symlinks branch from bcd36b2 to 40459ba Compare August 12, 2026 15:47
Molter73 added a commit that referenced this pull request Aug 12, 2026
With the changes introduced by #1440, the d_instantiate_ctx map has
grown considerably in size. This patch reduces the default size and
exposes a method for setting the size at runtime for fine-tweaking.
@Molter73
Molter73 marked this pull request as ready for review August 12, 2026 16:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_path_symlink.py`:
- Around line 204-207: Fix the path translation used by
test_follow_symlink_to_dir so absolute symlink targets resolve correctly when
running inside a container with host-mounted paths, then remove its
pytest.mark.skip decorator so CI executes the test.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: 3fb41b5d-5c60-4843-aaa0-1e04ce03344b

📥 Commits

Reviewing files that changed from the base of the PR and between 3a0a7ff and 40459ba.

📒 Files selected for processing (4)
  • fact-ebpf/src/bpf/main.c
  • fact-ebpf/src/lib.rs
  • fact/src/host_scanner.rs
  • tests/test_path_symlink.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • fact-ebpf/src/lib.rs
  • fact/src/host_scanner.rs
  • fact-ebpf/src/bpf/main.c

Comment thread tests/test_path_symlink.py
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.

2 participants