ROX-33217: Instrument inode tracking on directory being created path mkdir#465
Conversation
| struct inode* parent_inode_ptr = BPF_CORE_READ(parent_dentry, d_inode); | ||
| inode_key_t parent_inode = inode_to_key(parent_inode_ptr); | ||
|
|
||
| if (should_track_mkdir(parent_inode, path) != PARENT_MONITORED) { |
There was a problem hiding this comment.
I originally had
if (should_track_mkdir(parent_inode, path) == NOT_MONITORED) {
However, test_nonrecursive_wildcard::test_wildcard.py failed with that change. The reason was that was the inodes of all directories created in that test were tracked and subsequently all files in those directories were tracked regardless if they matched the wildcard pattern or not.
| #define FMODE_PWRITE ((fmode_t)(1 << 4)) | ||
| #define FMODE_CREATED ((fmode_t)(1 << 20)) | ||
|
|
||
| // File type constants from linux/stat.h |
There was a problem hiding this comment.
Could we add a permalink to the definition?
| umode_t mode = BPF_CORE_READ(inode, i_mode); | ||
| if (!S_ISDIR(mode)) { | ||
| bpf_map_delete_elem(&mkdir_context, &pid_tgid); | ||
| m->d_instantiate.ignored++; | ||
| return 0; | ||
| } |
There was a problem hiding this comment.
We should check if m->d_instantiate.ignored actually increases, I have a feeling this condition should never be met.
There was a problem hiding this comment.
m->d_instantiate.ignored increases, but I don't know if it is due to this or other locations with m->d_instantiate.ignored++;.
fact-ebpf/src/bpf/main.c
Outdated
|
|
||
| m->d_instantiate.total++; | ||
|
|
||
| if (inode == NULL) { |
There was a problem hiding this comment.
We should check if inode can actually be null, if so, we probably still want to try and cleanup the mkdir_context map entry.
There was a problem hiding this comment.
There is now cleanup even when the inode is null.
fact-ebpf/src/bpf/events.h
Outdated
| } | ||
|
|
||
| // d_instantiate doesn't support bpf_d_path, so we use false and rely on the stashed path from path_mkdir | ||
| __submit_event(event, m, FILE_ACTIVITY_CREATION, filename, inode, parent_inode, false); |
There was a problem hiding this comment.
Calling __submit_event will cause a full event to be filled in, including process information. However, mkdir is mostly needed for inode tracking and probably not interesting enough to send an event over gRPC, so we might want to look into having an abbreviated version that will just submit a FILE_ACTIVITY_MKDIR event that will be processed by HostScanner and dropped after adding to the userspace map.
fact-ebpf/src/bpf/maps.h
Outdated
| struct { | ||
| __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); | ||
| __type(key, __u32); | ||
| __type(value, struct mkdir_context_t); | ||
| __uint(max_entries, 1); | ||
| } mkdir_context_heap SEC(".maps"); | ||
|
|
||
| __always_inline static struct mkdir_context_t* get_mkdir_context() { | ||
| unsigned int zero = 0; | ||
| return bpf_map_lookup_elem(&mkdir_context_heap, &zero); | ||
| } |
There was a problem hiding this comment.
This is used. get_mkdir_context is used at https://github.com/stackrox/fact/pull/465/changes#diff-ffd13aea0af666b10cf909b17d6e19eb4c0855151d6cf2cbf6567a941c21b068R264
There was a problem hiding this comment.
You are right, but now I'm wondering why we are not just getting an entry from mkdir_context directly and filling that in, instead of getting an entry from mkdir_context_heap and then copying it over.
There's a few things you could try for when the entry doesn't actually exist.
- If you drop
BPF_F_NO_PREALLOCfrommkdir_context, I'm fairly certain you should always get a value back. - You can try using
bpf_map_lookup_elemand if it fails, create a new entry passingvalueasNULL. - If that doesn't work, you can just pass a pointer to a default value from a const global, which will end up in
.rodatasection.
There was a problem hiding this comment.
I think you also need to add your metrics here:
Lines 123 to 127 in eb0b3a9
I will look into improving how we handle these, it is getting very repetitive and having it scattered among multiple files is very error prone.
tests/test_path_mkdir.py
Outdated
| os.mkdir(level1) | ||
| os.mkdir(level2) | ||
| os.mkdir(level3) |
There was a problem hiding this comment.
[nit] We can probably just use os.makedirs on level3.
tests/test_path_mkdir.py
Outdated
| from event import Event, EventType, Process | ||
|
|
||
|
|
||
| def test_mkdir_nested(monitored_dir, server): |
There was a problem hiding this comment.
Maybe we can parametrize the final directory with UTF-8 characters?
fact-ebpf/src/bpf/maps.h
Outdated
| struct { | ||
| __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); | ||
| __type(key, __u32); | ||
| __type(value, struct mkdir_context_t); | ||
| __uint(max_entries, 1); | ||
| } mkdir_context_heap SEC(".maps"); | ||
|
|
||
| __always_inline static struct mkdir_context_t* get_mkdir_context() { | ||
| unsigned int zero = 0; | ||
| return bpf_map_lookup_elem(&mkdir_context_heap, &zero); | ||
| } |
There was a problem hiding this comment.
You are right, but now I'm wondering why we are not just getting an entry from mkdir_context directly and filling that in, instead of getting an entry from mkdir_context_heap and then copying it over.
There's a few things you could try for when the entry doesn't actually exist.
- If you drop
BPF_F_NO_PREALLOCfrommkdir_context, I'm fairly certain you should always get a value back. - You can try using
bpf_map_lookup_elemand if it fails, create a new entry passingvalueasNULL. - If that doesn't work, you can just pass a pointer to a default value from a const global, which will end up in
.rodatasection.
4a6a257 to
b0ef4fe
Compare
Co-authored-by: Mauro Ezequiel Moltrasio <mmoltras@redhat.com>
Description
Directory creation events need to be handled correctly. When a directory is created in a tracked directory its inode should be added to a hash set in kernel space. In user space an entry needs to be added into a map with the inode as the key and file path as the value.
An alternative approach can be found at #449
Checklist
Automated testing
If any of these don't apply, please comment below.
Testing Performed
Checked metrics