Skip to content

ROX-30299: Track io uring operations - #1331

Draft
JoukoVirtanen wants to merge 2 commits into
mainfrom
jv-ROX-30299-track-io_uring-operations
Draft

ROX-30299: Track io uring operations#1331
JoukoVirtanen wants to merge 2 commits into
mainfrom
jv-ROX-30299-track-io_uring-operations

Conversation

@JoukoVirtanen

@JoukoVirtanen JoukoVirtanen commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a test for modifying a file with io_uring. I initially thought that it was possible to modify a file using io_uring without triggering an open event in Fact. This test seems to show that is not the case and that it is indeed not possible to use io_uring to modify a file in a way that does not trigger a Fact event. Two binary files are added in this PR, io_uring_write.c and io_uring_write_raw.c. Only io_uring_write_raw.c is currently used. If this were to be merged, this would be cleaned up.

Even though tracking io_uring operation may not be needed, having this test might still be beneficial.

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

The following command was run

strace ./io_uring_write_raw test.txt hi

It had the following output

execve("./io_uring_write_raw", ["./io_uring_write_raw", "test.txt", "hi"], 0x7ffec06703b0 /* 101 vars */) = 0
brk(NULL)                               = 0x2d7a3000
brk(0x2d7a3d80)                         = 0x2d7a3d80
arch_prctl(ARCH_SET_FS, 0x2d7a3400)     = 0
set_tid_address(0x2d7a3a28)             = 693011
set_robust_list(0x2d7a36e0, 24)         = 0
rseq({cpu_id_start=0, cpu_id=RSEQ_CPU_ID_UNINITIALIZED, rseq_cs=NULL, flags=0, node_id=0, mm_cid=0, slice_ctrl={request=0, granted=0, __reserved=0}, __reserved=0}, 33, 0, 0x53053053) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
getrandom("\x82\xfe\xa6\x56\xfe\x0e\xba\xfb", 8, GRND_NONBLOCK) = 8
readlinkat(AT_FDCWD, "/proc/self/exe", "/home/jvirtane/projects/fact/tes"..., 4096) = 53
brk(NULL)                               = 0x2d7a3d80
brk(0x2d7c4d80)                         = 0x2d7c4d80
brk(0x2d7c5000)                         = 0x2d7c5000
mprotect(0x4a4000, 16384, PROT_READ)    = 0
io_uring_setup(4, {flags=0, sq_thread_cpu=0, sq_thread_idle=0, sq_entries=4, cq_entries=8, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS|IORING_FEAT_CQE_SKIP|IORING_FEAT_LINKED_FILE|IORING_FEAT_REG_REG_RING|IORING_FEAT_RECVSEND_BUNDLE|IORING_FEAT_MIN_TIMEOUT|IORING_FEAT_RW_ATTR|IORING_FEAT_NO_IOWAIT, sq_off={head=0, tail=4, ring_mask=16, ring_entries=24, flags=36, dropped=32, array=192, user_addr=0}, cq_off={head=8, tail=12, ring_mask=20, ring_entries=28, overflow=44, cqes=64, flags=40, user_addr=0}}) = 3
mmap(NULL, 208, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, 3, 0) = 0x7f8d24205000
mmap(NULL, 192, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, 3, 0x8000000) = 0x7f8d24204000
mmap(NULL, 256, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, 3, 0x10000000) = 0x7f8d24203000
io_uring_enter(3, 1, 1, IORING_ENTER_GETEVENTS, NULL, 0) = 1
io_uring_enter(3, 1, 1, IORING_ENTER_GETEVENTS, NULL, 0) = 1
io_uring_enter(3, 1, 1, IORING_ENTER_GETEVENTS, NULL, 0) = 1
close(3)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Neither the open or openat syscalls are used, but Fact did report the file being open for writing.

Here is the test failure when running locally.

>               raise ValueError(json.dumps(diff, indent=4, default=str))
E               ValueError: {
E                   "process": {
E                       "pid": {
E                           "expected": 849391,
E                           "actual": 849481
E                       },
E                       "exe_path": {
E                           "expected": "/home/jvirtane/.pyenv/versions/3.11.4/bin/python3.11",
E                           "actual": "/home/jvirtane/projects/fact/tests/io_uring_write_raw"
E                       },
E                       "args": {
E                           "expected": "/home/jvirtane/projects/fact/.venv/bin/python3 -m pytest tests/test_io_uring.py -v '--image=quay.io/stackrox-io/fact:0.3.x-244-ga28ab9a263'",
E                           "actual": "/home/jvirtane/projects/fact/tests/io_uring_write_raw /home/jvirtane/projects/fact/fact-test-krffiu67/io_uring_test.txt bye"
E                       },
E                       "name": {
E                           "expected": "python3",
E                           "actual": "iou-wrk-849481"
E                       }
E                   },
E                   "event_type": {
E                       "expected": "EventType.CREATION",
E                       "actual": "EventType.OPEN"
E                   }
E               }

Assisted-by: Claude Code (claude-opus-4-6)
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Enterprise

Run ID: b6aa67ca-c399-4bcd-9ace-cd37861b7489

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 35.04%. Comparing base (18b4dc3) to head (a28ab9a).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1331      +/-   ##
==========================================
- Coverage   35.42%   35.04%   -0.38%     
==========================================
  Files          22       22              
  Lines        3241     3276      +35     
  Branches     3241     3276      +35     
==========================================
  Hits         1148     1148              
- Misses       2088     2123      +35     
  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.

@Molter73 Molter73 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would suggest we put the new executables in a Containerfile and run them from there, this should make it easier to port the test to other systems in the future. There is a tests/containers directory already for storing container related files for testing.

Comment thread tests/io_uring_write.c
io_uring_cqe_seen(&ring, cqe);

/* Write content via io_uring */
sqe = io_uring_get_sqe(&ring);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need to get the submission queue on every step? I would expect we could do that just at the start.

Comment thread tests/io_uring_write.c
Comment on lines +40 to +50
io_uring_submit(&ring);
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
fprintf(stderr, "wait openat: %s\n", strerror(-ret));
goto err;
}
if (cqe->res < 0) {
fprintf(stderr, "openat: %s\n", strerror(-cqe->res));
io_uring_cqe_seen(&ring, cqe);
goto err;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

After the prep step, all sections in the program seem to follow this patter of submit -> wait cqe -> check ret -> check res -> mark cqe as seen, could we somehow wrap this in a function to simplify it?

Comment thread tests/io_uring_write.c
Comment on lines +81 to +86
io_uring_queue_exit(&ring);
return 0;

err:
io_uring_queue_exit(&ring);
return 3;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Define int result = 3 at the top of this function and this can be reduced to:

Suggested change
io_uring_queue_exit(&ring);
return 0;
err:
io_uring_queue_exit(&ring);
return 3;
result = 0;
err:
io_uring_queue_exit(&ring);
return result;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this needed because some systems might not have liburing + devel installed? If so, as my original comment stated, we probably want to containerize the binary and just run that.

Comment thread tests/test_io_uring.py

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This test seems to only pass if the write done via io_uring is not detected by fact, CI is failing due to cc not being found AFAICT. Is this test failing on your system because fact does see the event?

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.

3 participants