Skip to content
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

fix(drivers): add always a null terminator after args and envs #1800

Merged
merged 5 commits into from
Apr 23, 2024

Conversation

Andreagit97
Copy link
Member

What type of PR is this?

/kind bug

Any specific area of the project related to this PR?

/area driver-kmod

/area driver-modern-bpf

Does this PR require a change in the driver versions?

No

What this PR does / why we need it:

This PR contains multiple fixes:

  • modern_bpf: related to this [BUG] empty strings ("") are not collected in the same way between our drivers #866. Since bpf_probe_read_user_str returns 0 in case of an empty string we now add manually a null terminator directly in push__charbuf, in this case we can handle empty strings in just one place. Partially reverting this fix(driver/bpf): not sending all arguments on execve fail #1760.
  • modern_bpf: handle correctly args and envs greater than the limit (4096). Before this fix, in the case of args>= 4096, they were truncated without adding a \0 at the end. This causes issues when handling these params in userspace. Now even in case of partial reads we always terminate the args with a final \0.
  • kmod: solve the same issue of the modern with the null terminator
  • kmod: fix an issue with accumulate_argv_or_env. Before this fix, in case of page faults or other errors, the entire event was dropped, now in case of errors we send partial args null-terminated.

Which issue(s) this PR fixes:

Special notes for your reviewer:

Does this PR introduce a user-facing change?:

fix(drivers): add always a null terminator after `args` and `envs`

Instruct `push__charbuf` to send a single `\0` when the unsafe pointer
points to an empty string `""`. Doing it inside `push__charbuf` avoid
extra ad-hoc code in various places

Signed-off-by: Andrea Terzolo <[email protected]>
This commit handles `args` >= 4096. We always add a `\0` at the end of
the args even in case of partial reads. Moreover this changes uniform
the modern probe behavior with other drivers, before this commit we have
a max len of 4096 for `exe` and other `4096` for args. After this change
`exe + args` should be <= 4096

Signed-off-by: Andrea Terzolo <[email protected]>
Copy link

Please double check driver/SCHEMA_VERSION file. See versioning.

/hold

return 0;
}

/* Since `bpf_probe_read_user_str` return `0` in case of empty string we push a `\0` and we return 1. */
if(written_bytes==0)
Copy link
Member Author

Choose a reason for hiding this comment

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

in case of an empty string, we send to userspace "" instead of an empty param with len 0

@@ -214,7 +214,8 @@ static __always_inline void push__previous_character(uint8_t *data, uint64_t *pa
* @param charbuf_pointer pointer to the charbuf.
* @param limit maximum number of bytes that we read in case we don't find a `\0`
* @param mem tell where it must read: user-space or kernel-space.
* @return (uint16_t) the number of bytes written in the buffer. Could be '0' if the passed pointer is not valid.
* @return (uint16_t) the number of bytes written in the buffer. Returns '0' if the passed pointer is not valid.
* Returns `1` if the provided pointer points to an empty string "".
Copy link
Member Author

Choose a reason for hiding this comment

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

before this patch we returned 0 and so we sent a param with len 0 to userspace

*/
if(auxmap__store_bytebuf_param(auxmap, start_pointer, len_to_read, USER) > 0)
{
// maybe we read only part of the last argument so we need to put a `\0` at the end.
Copy link
Member Author

Choose a reason for hiding this comment

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

if auxmap__store_bytebuf_param returned 0 we don't need to push a null terminator

* @param auxmap pointer to the auxmap in which we are storing the param.
* @param charbuf pointer array, obtained directly from the syscall (`argv`).
*/
static __always_inline void auxmap__store_exe_args_failure(struct auxiliary_map *auxmap, char **array)
Copy link
Member Author

Choose a reason for hiding this comment

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

this is a pretty ugly helper that pushes together 2 params, the exe and the args. This is done to be compliant with other drivers (see tests). all the other drivers share a unique limit of 4096 bytes for exe and args, while in the modern, before this patch we could have an exe of 4096 + an args of 4096


args_len += push__charbuf(auxmap->data, &auxmap->payload_pos, charbuf_pointer, MAX_PROC_ARG_ENV, USER);

/* the sum of `exe` + `args` should be `<= MAX_PROC_ARG_ENV` */
Copy link
Member Author

Choose a reason for hiding this comment

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

like in the other drivers

* @param auxmap pointer to the auxmap in which we are storing the param.
* @param charbuf pointer array, obtained directly from the syscall (`argv`).
*/
static __always_inline void auxmap__store_exe_args_failure(struct auxiliary_map *auxmap, char **array)
Copy link
Member Author

Choose a reason for hiding this comment

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

this is called failure becuase it is used to collect exe and args only in case of failures

if(args_len > 0)
{
auxmap->payload_pos = initial_payload_pos + args_len;
push__previous_character(auxmap->data, &auxmap->payload_pos, '\0');
Copy link
Member Author

Choose a reason for hiding this comment

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

always null terminated

@@ -396,25 +465,18 @@ static __always_inline void auxmap__store_execve_args(struct auxiliary_map *auxm
}

arg_len = push__charbuf(auxmap->data, &auxmap->payload_pos, charbuf_pointer, MAX_PROC_ARG_ENV, USER);
Copy link
Member Author

Choose a reason for hiding this comment

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

now that push__charbuf handles empty strings we can simplify the flow


if (unlikely(ppm_get_user(p, argv)))
return PPM_FAILURE_INVALID_USER_MEMORY;
Copy link
Member Author

Choose a reason for hiding this comment

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

this caused an event drop in case of failure

@@ -991,8 +987,8 @@ int f_proc_startupdate(struct event_filler_arguments *args)
args_len = mm->arg_end - mm->arg_start;

if (args_len) {
if (args_len > PAGE_SIZE)
args_len = PAGE_SIZE;
if (args_len > STR_STORAGE_SIZE)
Copy link
Member Author

Choose a reason for hiding this comment

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

STR_STORAGE_SIZE is PAGE_SIZE but if one day they should diverge is better to use STR_STORAGE_SIZE everywhere

@Andreagit97 Andreagit97 modified the milestones: 0.16.0, TBD, next-driver Apr 17, 2024
args_len = accumulate_argv_or_env((const char __user * __user *)val,
args->str_storage, available);

if (unlikely(args_len < 0))
Copy link
Member Author

Choose a reason for hiding this comment

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

this is no more possible, we cannot return an args_len < 0

@Andreagit97 Andreagit97 changed the title [WIP] fix(drivers): add always a null terminator after args and envs fix(drivers): add always a null terminator after args and envs Apr 17, 2024
@Andreagit97
Copy link
Member Author

there is still an issue with the legacy ebpf probe, I'm trying to understand what is going on...
/hold

@Andreagit97 Andreagit97 force-pushed the fix_modern_bpf_probe branch from 04590f1 to bfca262 Compare April 18, 2024 08:46
if(argv == NULL)
{
// we need to put a `\0` otherwise we could read junk data
data->buf[off & SCRATCH_SIZE_HALF] = '\0';
Copy link
Member Author

Choose a reason for hiding this comment

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

this is why the CI was failing, if we called execve with argv or 'env NULLwe ended up reading random memory indata->buf` written by the previous process on this CPU.

@Andreagit97
Copy link
Member Author

Andreagit97 commented Apr 18, 2024

It seems there are no regressions in the kernel testing:

x86

KERNEL CMAKE-CONFIGURE KMOD BUILD KMOD SCAP-OPEN BPF-PROBE BUILD BPF-PROBE SCAP-OPEN MODERN-BPF SCAP-OPEN
amazonlinux2-4.19 🟢 🟢 🟢 🟢 🟡
amazonlinux2-5.10 🟢 🟢 🟢 🟢 🟢 🟢
amazonlinux2-5.15 🟢 🟢 🟢 🟢 🟢 🟢
amazonlinux2-5.4 🟢 🟢 🟢 🟢 🟢 🟡
amazonlinux2022-5.15 🟢 🟢 🟢 🟢 🟢 🟢
amazonlinux2023-6.1 🟢 🟢 🟢 🟢 🟢 🟢
archlinux-6.0 🟢 🟢 🟢 🟢 🟢 🟢
archlinux-6.7 🟢 🟢 🟢 🟢 🟢 🟢
centos-3.10 🟢 🟢 🟢 🟡 🟡 🟡
centos-4.18 🟢 🟢 🟢 🟢 🟢 🟢
centos-5.14 🟢 🟢 🟢 🟢 🟢 🟢
fedora-5.17 🟢 🟢 🟢 🟢 🟢 🟢
fedora-5.8 🟢 🟢 🟢 🟢 🟢 🟢
fedora-6.2 🟢 🟢 🟢 🟢 🟢 🟢
oraclelinux-3.10 🟢 🟢 🟢 🟡 🟡 🟡
oraclelinux-4.14 🟢 🟢 🟢 🟢 🟢 🟡
oraclelinux-5.15 🟢 🟢 🟢 🟢 🟢 🟢
oraclelinux-5.4 🟢 🟢 🟢 🟢 🟢 🟡
ubuntu-4.15 🟢 🟢 🟢 🟢 🟢 🟡
ubuntu-5.8 🟢 🟢 🟢 🟢 🟢 🟡
ubuntu-6.5 🟢 🟢 🟢 🟢 🟢 🟢

arm64

KERNEL CMAKE-CONFIGURE KMOD BUILD KMOD SCAP-OPEN BPF-PROBE BUILD BPF-PROBE SCAP-OPEN MODERN-BPF SCAP-OPEN
amazonlinux2-5.4 🟢 🟢 🟢 🟢 🟢 🟡
amazonlinux2022-5.15 🟢 🟢 🟢 🟢 🟢 🟢
fedora-6.2 🟢 🟢 🟢 🟢 🟢 🟢
oraclelinux-4.14 🟢 🟢 🟢 🟡 🟡 🟡
oraclelinux-5.15 🟢 🟢 🟢 🟢 🟢 🟢
ubuntu-6.5 🟢 🟢 🟢 🟢 🟢 🟢

Copy link
Contributor

@FedeDP FedeDP left a comment

Choose a reason for hiding this comment

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

/approve

Great job as always!

@poiana
Copy link
Contributor

poiana commented Apr 19, 2024

LGTM label has been added.

Git tree hash: 2c78ee4f425665ed0aa2c7a546859523b7851f2c

Copy link
Contributor

@LucaGuerra LucaGuerra left a comment

Choose a reason for hiding this comment

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

Thanks! As a precaution I would add userspace checks on what the kernel provides in these cases to make it easier to catch bugs before we release this. I meant to do it for a while, I'll help you with that.

@poiana
Copy link
Contributor

poiana commented Apr 23, 2024

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Andreagit97, FedeDP, LucaGuerra

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [Andreagit97,FedeDP,LucaGuerra]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@FedeDP
Copy link
Contributor

FedeDP commented Apr 23, 2024

/unhold

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants