Bug 217679 - bpf: bpf_probe_read_user_str() returns 0 for empty strings
Summary: bpf: bpf_probe_read_user_str() returns 0 for empty strings
Status: NEW
Alias: None
Product: Linux
Classification: Unclassified
Component: Kernel (show other bugs)
Hardware: All Linux
: P3 normal
Assignee: Virtual assignee for kernel bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-07-18 11:29 UTC by Max Froehling
Modified: 2023-07-18 11:29 UTC (History)
0 users

See Also:
Kernel Version:
Subsystem:
Regression: No
Bisected commit-id:


Attachments

Description Max Froehling 2023-07-18 11:29:02 UTC
Overview:

From within eBPF, calling the helper function bpf_probe_read_user_str(void *dst, __u32 size, const void *unsafe_ptr returns 0 when the source string (void *unsafe_ptr) consists of a string containing only a single null-byte.

This violates various functions documentations (the helper and various internal kernel functions), which all state:

> On success, the strictly positive length of the output string,
> including the trailing NUL character. On error, a negative value.

To me, this states that the function should return 1 for char myString[] = ""; However, this is not the case. The function returns 0 instead.

For non-empty strings, it works as expected. For example, char myString[] = "abc"; returns 4.

Steps to Reproduce:
* Write an eBPF program that calls bpf_probe_read_user_str(), using a userspace pointer pointing to an empty string.
* Store the result value of that function
* Do the same thing, but try out bpf_probe_read_kernel_str(), like this:
char empty[] = "";
char copy[5];
long ret = bpf_probe_read_kernel_str(copy, 5, empty);
* Compare the return value of bpf_probe_read_user_str() and bpf_probe_read_kernel_str()

Expected Result:

Both functions return 1 (because of the single NULL byte).

Actual Result:

bpf_probe_read_user_str() returns 0, while bpf_probe_read_kernel_str() returns 1.

Additional Information:

I believe I can see the bug on the current Linux kernel master branch.

In the file/function mm/maccess.c::strncpy_from_user_nofault() the helper implementation calls strncpy_from_user(), which returns the length without trailing 0. Hence this function returns 0 for an empty string.

However, in line 192 (as of commit fdf0eaf11452d72945af31804e2a1048ee1b574c) there is a check that only increments ret, if it is > 0. This appears to be the logic that adds the trailing null byte. Since the check only does this for a ret > 0, a ret of 0 remains at 0.

This is a possible off-by-one error that might cause the behavior.

Note You need to log in before you can comment on or make changes to this bug.