Skip to content

Commit

Permalink
bpf: fix bugs in the prepend_name function
Browse files Browse the repository at this point in the history
Unit tests were added in the previous commit for this function,
highlighting some of the issues it presented. This commit introduce a
few changes trying to fix the detected bugs.

Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
  • Loading branch information
mtardy committed Jan 3, 2024
1 parent 0e242c4 commit c8bcf64
Showing 1 changed file with 38 additions and 25 deletions.
63 changes: 38 additions & 25 deletions bpf/process/bpf_process_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@

#define ENAMETOOLONG 36 /* File name too long */

#define MAX_BUF_LEN 256

struct buffer_heap_map_value {
unsigned char buf[PATH_MAP_SIZE];
// Buffer is twice the needed size because of the verifier. In prepend_name
// unit tests, the verifier figures out that 255 is enough and that the
// buffer_offset will not overflow, but in the real use-case it looks like
// it's forgetting about that.
unsigned char buf[MAX_BUF_LEN * 2];
};

struct {
Expand Down Expand Up @@ -108,42 +114,49 @@ d_unlinked(struct dentry *dentry)
}

static inline __attribute__((always_inline)) int
prepend_name(char *bf, char **buffer, int *buflen, const char *name, u32 dlen)
prepend_name(char *buf, char **bufptr, int *buflen, const char *name, u32 namelen)
{
char slash = '/';
u64 buffer_offset = (u64)(*buffer) - (u64)bf;

// Change dlen (the dentry name length) to fit in the buffer.
// We prefer to store the part of it that fits rather that discard it.
if (dlen + 1 /* for the slash */ >= *buflen)
dlen = *buflen - 1 /* for the slash */ -
1 /* in order to avoid the case to do *buflen == 0 */;

*buflen -= (dlen + 1);
// This will not happen as in the previous if-clause ensures that *buflen will be > 0
// Needed to make the verifier happy in older kernels.
if (*buflen <= 0)
// contains 1 if the buffer is large enough to contain the whole name and a slash prefix
bool write_slash = 1;

u64 buffer_offset = (u64)(*bufptr) - (u64)buf;

// Change name and namelen to fit in the buffer.
// We prefer to store the part of it that fits rather than discard it.
if (namelen >= *buflen) {
name += namelen - *buflen;
namelen = *buflen;
write_slash = 0;
}

*buflen -= (namelen + write_slash);

// This will not happen as buffer_offset cannot be above 256 and namelen is
// bound to 255. Needed to make the verifier happy in older kernels.
if (namelen + write_slash > buffer_offset)
return -ENAMETOOLONG;

buffer_offset -= (dlen + 1);
buffer_offset -= (namelen + write_slash);

// This will never happen. buffer_offset is the diff of the initial buffer pointer
// with the current buffer pointer. This will be at max 256 bytes (similar to the initial
// size).
// Needed to bound that for probe_read call.
if (buffer_offset > PATH_MAP_SIZE - 256)
if (buffer_offset >= MAX_BUF_LEN)
return -ENAMETOOLONG;

probe_read(bf + buffer_offset, sizeof(char), &slash);
// This ensures that dlen is < 256, which is aligned with kernel's max dentry name length
if (write_slash)
buf[buffer_offset] = '/';

// This ensures that namelen is < 256, which is aligned with kernel's max dentry name length
// that is 255 (https://elixir.bootlin.com/linux/v5.10/source/include/uapi/linux/limits.h#L12).
// Needed to bound that for probe_read call.
asm volatile("%[dlen] &= 0xff;\n" ::[dlen] "+r"(dlen)
asm volatile("%[namelen] &= 0xff;\n" ::[namelen] "+r"(namelen)
:);
probe_read(bf + buffer_offset + 1, dlen * sizeof(char), name);
probe_read(buf + buffer_offset + write_slash, namelen * sizeof(char), name);

*buffer = bf + buffer_offset;
return 0;
*bufptr = buf + buffer_offset;
return write_slash ? 0 : -ENAMETOOLONG;
}

/*
Expand Down Expand Up @@ -357,10 +370,10 @@ d_path_local(const struct path *path, int *buflen, int *error)
if (!buffer)
return 0;

*buflen = 256;
*buflen = MAX_BUF_LEN;
buffer = __d_path_local(path, buffer, buflen, error);
if (*buflen > 0)
*buflen = 256 - *buflen;
*buflen = MAX_BUF_LEN - *buflen;

return buffer;
}
Expand Down

0 comments on commit c8bcf64

Please sign in to comment.